feat: lit-site-status, Admin-Tab und Admin-Hinweis zeigen Divi-Fähigkeiten mit Zustand, Quelle, Problemen und betroffenen Werkzeugen
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Sonnet 5.5
parent
b92545a8fe
commit
2aef6e1154
+142
-10
@@ -10,6 +10,8 @@ use LIT\MCP\Integrations\Divi\Release\CheckReport;
|
||||
use LIT\MCP\Integrations\Divi\Release\CheckReportStore;
|
||||
use LIT\MCP\Integrations\Divi\Release\DiviCheckRoute;
|
||||
use LIT\MCP\Integrations\Divi\Release\DiviEnvironment;
|
||||
use LIT\MCP\Integrations\Divi\Release\FeatureOverview;
|
||||
use LIT\MCP\Integrations\Divi\Release\FeatureStatus;
|
||||
use LIT\MCP\Integrations\Divi\Release\ReleaseGate;
|
||||
use LIT\MCP\Integrations\Divi\Release\ReleaseResolver;
|
||||
use LIT\MCP\Integrations\Divi\Release\ReleaseState;
|
||||
@@ -21,10 +23,12 @@ use LIT\MCP\Plugin;
|
||||
defined('ABSPATH') || exit;
|
||||
|
||||
/**
|
||||
* Tab "Divi" (spec §6): release status of the installed Divi, the latest check report, "Prüfung starten" (REST,
|
||||
* DiviCheckRoute), "Divi <Version> freigeben" (only after a fresh green report) and "Freigabe zurückziehen" (only
|
||||
* for a database release). Loading the tab removes check pages a dead check left behind – in the page's load hook
|
||||
* (removeOrphans()), not while rendering.
|
||||
* Tab "Divi" (spec §6): release status of the installed Divi, the table of its features (state, source, problems,
|
||||
* affected tools – E3), the latest check report, "Prüfung starten" (REST, DiviCheckRoute), "Divi <Version>
|
||||
* freigeben" (only after a fresh green report) and "Freigabe zurückziehen" (whenever a database release applies,
|
||||
* whatever the overall state). Loading the tab removes check pages a dead check left behind – in the page's load
|
||||
* hook (removeOrphans()), not while rendering. renderNotice() is the admin notice on every admin page while a
|
||||
* feature is unusable.
|
||||
*/
|
||||
final class DiviTab implements AdminTab
|
||||
{
|
||||
@@ -38,6 +42,7 @@ final class DiviTab implements AdminTab
|
||||
/**
|
||||
* @param (Closure(): string)|null $version Installed Divi version ('' = none); null = DiviEnvironment.
|
||||
* @param (Closure(string): string)|null $stateKey null = ReleaseResolver::stateKey().
|
||||
* @param FeatureOverview|null $overview null = a table without the affected tools.
|
||||
*/
|
||||
public function __construct(
|
||||
private ?CheckReportStore $reports = null,
|
||||
@@ -45,7 +50,8 @@ final class DiviTab implements AdminTab
|
||||
private ?ChangeLog $log = null,
|
||||
private ?AdminPostAction $action = null,
|
||||
private ?Closure $version = null,
|
||||
private ?Closure $stateKey = null
|
||||
private ?Closure $stateKey = null,
|
||||
private ?FeatureOverview $overview = null
|
||||
) {
|
||||
}
|
||||
|
||||
@@ -68,6 +74,55 @@ final class DiviTab implements AdminTab
|
||||
add_action('admin_post_' . self::RELEASE_ACTION, [$this, 'release']);
|
||||
add_action('admin_post_' . self::WITHDRAW_ACTION, [$this, 'withdraw']);
|
||||
add_action('load-' . SettingsPage::SCREEN_HOOK, [$this, 'removeOrphans']);
|
||||
$this->registerNotice();
|
||||
}
|
||||
|
||||
/** The admin notice for administrators while a Divi feature is unusable (registerActions() runs in admin only). */
|
||||
public function registerNotice(): void
|
||||
{
|
||||
add_action('admin_notices', [$this, 'renderNotice']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Only for administrators, only with Divi active, only while a feature is not usable – and not on the Divi tab,
|
||||
* which shows the same table. Reads the cached matrix (D7), so no check runs on an ordinary admin page.
|
||||
*/
|
||||
public function renderNotice(): void
|
||||
{
|
||||
if (! current_user_can('manage_options') || $this->version() === '' || $this->onDiviTab()) {
|
||||
return;
|
||||
}
|
||||
|
||||
$unusable = ReleaseGate::status()->matrix->unusable();
|
||||
if ($unusable === []) {
|
||||
return;
|
||||
}
|
||||
|
||||
printf(
|
||||
'<div class="notice notice-warning"><p>%s <a href="%s">%s</a></p></div>',
|
||||
esc_html(sprintf(
|
||||
/* translators: 1: Divi version, 2: comma separated feature names */
|
||||
__(
|
||||
// phpcs:ignore Generic.Files.LineLength.TooLong -- WordPress.WP.I18n forbids splitting the text argument.
|
||||
'LIT MCP: Mit Divi %1$s sind diese Fähigkeiten nicht nutzbar: %2$s. Die betroffenen Werkzeuge lehnen ab, alle anderen laufen weiter.',
|
||||
'wp-lit-mcp'
|
||||
),
|
||||
$this->version(),
|
||||
implode(', ', array_map(
|
||||
static fn (FeatureStatus $status): string => $status->feature->label(),
|
||||
$unusable
|
||||
))
|
||||
)),
|
||||
esc_url(SettingsPage::url(self::SLUG)),
|
||||
esc_html__('Details im Reiter „Divi“', 'wp-lit-mcp')
|
||||
);
|
||||
}
|
||||
|
||||
private function onDiviTab(): bool
|
||||
{
|
||||
$screen = function_exists('get_current_screen') ? get_current_screen() : null;
|
||||
|
||||
return is_object($screen) && $screen->id === SettingsPage::SCREEN_HOOK && $this->requested();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -153,7 +208,7 @@ final class DiviTab implements AdminTab
|
||||
echo '<h2>' . esc_html__('Divi-Freigabe', 'wp-lit-mcp') . '</h2>';
|
||||
echo '<p>' . esc_html__(
|
||||
// phpcs:ignore Generic.Files.LineLength.TooLong -- WordPress.WP.I18n forbids splitting the text argument.
|
||||
'LIT MCP schreibt nur mit Divi-Versionen, die geprüft sind. Nach einem Divi-Update hier die Prüfung starten und die Version freigeben; bis dahin lesen die Divi-Werkzeuge nur.',
|
||||
'LIT MCP prüft nach einem Divi-Update jede Fähigkeit einzeln (z. B. „Seiten schreiben“). Ist sie nicht nutzbar, lehnen nur die Werkzeuge ab, die sie brauchen; alle anderen laufen weiter.',
|
||||
'wp-lit-mcp'
|
||||
) . '</p>';
|
||||
if ($this->removed !== []) {
|
||||
@@ -168,6 +223,7 @@ final class DiviTab implements AdminTab
|
||||
}
|
||||
|
||||
$this->renderStatus($status);
|
||||
$this->renderFeatures($status);
|
||||
$this->renderForms($status, $report);
|
||||
if ($report !== null) {
|
||||
$this->renderReport($report);
|
||||
@@ -184,7 +240,7 @@ final class DiviTab implements AdminTab
|
||||
);
|
||||
printf(
|
||||
'<tr><th scope="row">%s</th><td><strong>%s</strong> – %s</td></tr>',
|
||||
esc_html__('Zustand', 'wp-lit-mcp'),
|
||||
esc_html__('Gesamtzustand', 'wp-lit-mcp'),
|
||||
esc_html($this->stateLabel($status->state)),
|
||||
esc_html($this->explanation($status))
|
||||
);
|
||||
@@ -198,6 +254,80 @@ final class DiviTab implements AdminTab
|
||||
echo '</tbody></table>';
|
||||
}
|
||||
|
||||
private function renderFeatures(ReleaseStatus $status): void
|
||||
{
|
||||
echo '<h3>' . esc_html__('Fähigkeiten', 'wp-lit-mcp') . '</h3>';
|
||||
echo '<table class="widefat striped lit-mcp-divi-features"><thead><tr>';
|
||||
$headings = [
|
||||
__('Fähigkeit', 'wp-lit-mcp'),
|
||||
__('Zustand', 'wp-lit-mcp'),
|
||||
__('Quelle', 'wp-lit-mcp'),
|
||||
__('Probleme', 'wp-lit-mcp'),
|
||||
__('Betroffene Werkzeuge', 'wp-lit-mcp'),
|
||||
];
|
||||
foreach ($headings as $heading) {
|
||||
echo '<th scope="col">' . esc_html($heading) . '</th>';
|
||||
}
|
||||
echo '</tr></thead><tbody>';
|
||||
foreach (($this->overview ?? FeatureOverview::withoutTools())->rows($status->matrix) as $row) {
|
||||
echo '<tr><td>' . esc_html($row['label']) . '</td>';
|
||||
echo '<td><strong>' . esc_html($this->stateLabel(ReleaseState::from($row['state']))) . '</strong></td>';
|
||||
echo '<td>' . esc_html($this->sourceLabel($row['source'])) . '</td>';
|
||||
echo '<td>';
|
||||
$this->renderList($row['problems'], false);
|
||||
echo '</td><td>';
|
||||
$this->renderList($row['tools'], true);
|
||||
echo '</td></tr>';
|
||||
}
|
||||
echo '</tbody></table>';
|
||||
$this->renderAutomationNote();
|
||||
}
|
||||
|
||||
/** @param list<string> $items */
|
||||
private function renderList(array $items, bool $code): void
|
||||
{
|
||||
if ($items === []) {
|
||||
echo '–';
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
echo '<ul>';
|
||||
foreach ($items as $item) {
|
||||
echo $code ? '<li><code>' . esc_html($item) . '</code></li>' : '<li>' . esc_html($item) . '</li>';
|
||||
}
|
||||
echo '</ul>';
|
||||
}
|
||||
|
||||
private function sourceLabel(?string $source): string
|
||||
{
|
||||
return match ($source) {
|
||||
ReleaseStatus::SOURCE_CODE => __('Plugin (code)', 'wp-lit-mcp'),
|
||||
ReleaseStatus::SOURCE_DATABASE => __('Freigabe auf dieser Website (database)', 'wp-lit-mcp'),
|
||||
ReleaseStatus::SOURCE_AUTO => __('Automatik (auto)', 'wp-lit-mcp'),
|
||||
default => '–',
|
||||
};
|
||||
}
|
||||
|
||||
/** E1: what the automatic release does and how to switch it off. */
|
||||
private function renderAutomationNote(): void
|
||||
{
|
||||
echo '<p class="description">' . esc_html__(
|
||||
// phpcs:ignore Generic.Files.LineLength.TooLong -- WordPress.WP.I18n forbids splitting the text argument.
|
||||
'Automatik: Ist die installierte Divi-Version im Plugin nicht als getestet hinterlegt, gibt LIT MCP jede Fähigkeit einzeln frei, wenn alle benutzten Divi-Funktionen vorhanden sind, ihre Aufrufe (Signaturen) und die Fingerabdrücke nachgebauter Divi-Abläufe unverändert sind und alle Konstanten definiert sind. Das prüft Aufrufformen, nicht das Verhalten von Divi. Fehlt für eine Stelle die Referenz, ist die Fähigkeit ungetestet; weicht etwas ab, ist sie inkompatibel.',
|
||||
'wp-lit-mcp'
|
||||
) . '</p>';
|
||||
echo '<p class="description">' . sprintf(
|
||||
/* translators: %s: filter name in a code element */
|
||||
esc_html__(
|
||||
// phpcs:ignore Generic.Files.LineLength.TooLong -- WordPress.WP.I18n forbids splitting the text argument.
|
||||
'Die Automatik abschalten: Filter %s auf false setzen (z. B. in einem mu-Plugin). Dann gelten nicht hinterlegte Divi-Versionen bis zur Freigabe unten als ungetestet.',
|
||||
'wp-lit-mcp'
|
||||
),
|
||||
'<code>lit_mcp_divi_auto_release</code>'
|
||||
) . '</p>';
|
||||
}
|
||||
|
||||
private function stateLabel(ReleaseState $state): string
|
||||
{
|
||||
return match ($state) {
|
||||
@@ -212,13 +342,14 @@ final class DiviTab implements AdminTab
|
||||
if ($status->state === ReleaseState::Incompatible) {
|
||||
return __(
|
||||
// phpcs:ignore Generic.Files.LineLength.TooLong -- WordPress.WP.I18n forbids splitting the text argument.
|
||||
'Divi hat Stellen geändert, die LIT MCP nutzt. Schreiben bleibt gesperrt, bis ein Plugin-Update von LIT MCP eingespielt ist.',
|
||||
'Divi hat Stellen geändert, die LIT MCP nutzt. Die betroffenen Fähigkeiten (Tabelle unten) bleiben gesperrt, bis ein Plugin-Update von LIT MCP eingespielt ist; alle anderen laufen weiter.',
|
||||
'wp-lit-mcp'
|
||||
);
|
||||
}
|
||||
if ($status->state === ReleaseState::Untested) {
|
||||
return __(
|
||||
'Diese Version ist nicht geprüft. Die Divi-Werkzeuge lesen nur, bis sie freigegeben ist.',
|
||||
// phpcs:ignore Generic.Files.LineLength.TooLong -- WordPress.WP.I18n forbids splitting the text argument.
|
||||
'Diese Version ist nicht geprüft. Die Werkzeuge der ungetesteten Fähigkeiten (Tabelle unten) lehnen ab, bis die Version freigegeben ist; alle anderen laufen weiter.',
|
||||
'wp-lit-mcp'
|
||||
);
|
||||
}
|
||||
@@ -283,7 +414,8 @@ final class DiviTab implements AdminTab
|
||||
}
|
||||
}
|
||||
|
||||
if ($status->state === ReleaseState::Released && $status->source === ReleaseStatus::SOURCE_DATABASE) {
|
||||
// A database release applies whatever the overall state: one incompatible feature must not hide the way back.
|
||||
if ($status->source === ReleaseStatus::SOURCE_DATABASE || $status->release !== null) {
|
||||
$this->actionForm(self::WITHDRAW_ACTION, __('Freigabe zurückziehen', 'wp-lit-mcp'), 'secondary');
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user