feat: lit/divi-outline and lit/divi-node-get with tree_rev
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01At3V6SjwbMe6eMzVGW3Hs7
This commit is contained in:
@@ -0,0 +1,102 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace LIT\MCP\Integrations\Divi\Pages;
|
||||
|
||||
use LIT\MCP\Abilities\Errors;
|
||||
use LIT\MCP\Integrations\Divi\BlockTree\MalformedMarkup;
|
||||
use LIT\MCP\Integrations\Divi\BlockTree\NodeIndex;
|
||||
use LIT\MCP\Integrations\Divi\BlockTree\Parser;
|
||||
use LIT\MCP\Integrations\WordPress\PostGuard;
|
||||
use LIT\MCP\Integrations\WordPress\Schema\ContentSchema;
|
||||
use WP_Error;
|
||||
use WP_Post;
|
||||
|
||||
/**
|
||||
* Loads an entry for the Divi tools: rights and status via PostGuard (spec §4.5), then the
|
||||
* block tree. Divi 4 shortcode content and unbalanced markup are refused with a hint –
|
||||
* spec §3.4: Divi 4 content is kept, never edited. Not final so abilities can be tested
|
||||
* with a mock.
|
||||
*/
|
||||
class DiviPageLoader
|
||||
{
|
||||
public const OUTLINE_HINT = 'Aktuellen Stand mit lit/divi-outline (direkt: lit-divi-outline) neu lesen und die '
|
||||
. 'Änderung mit der neuen tree_rev wiederholen.';
|
||||
|
||||
public function __construct(private ContentSchema $schema, private Parser $parser = new Parser())
|
||||
{
|
||||
}
|
||||
|
||||
/** Readers pass $forWrite = false (trashed entries may be read, like lit-content-get). */
|
||||
public function load(mixed $postId, bool $forWrite): DiviPage|WP_Error
|
||||
{
|
||||
$post = PostGuard::editable($postId, $this->schema, 'post_id', ! $forWrite);
|
||||
if ($post instanceof WP_Error) {
|
||||
return $post;
|
||||
}
|
||||
|
||||
$content = (string) $post->post_content;
|
||||
if (! str_contains($content, '<!-- wp:divi/') && str_contains($content, '[et_pb_')) {
|
||||
return Errors::unsupported(
|
||||
sprintf(
|
||||
'Eintrag %d enthält Divi-4-Inhalt (Shortcodes); die Divi-Werkzeuge bearbeiten nur Divi 5.',
|
||||
$post->ID
|
||||
),
|
||||
'Die Seite einmal im Divi-Builder öffnen und speichern – Divi 5 wandelt sie dabei um. Bis dahin '
|
||||
. 'bleibt der Inhalt unverändert.'
|
||||
);
|
||||
}
|
||||
|
||||
try {
|
||||
$document = $this->parser->parse($content);
|
||||
} catch (MalformedMarkup $e) {
|
||||
$message = sprintf('[wp-lit-mcp] divi parse of post %d failed: %s', $post->ID, $e->getMessage());
|
||||
error_log($message); // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log
|
||||
|
||||
return Errors::unsupported(
|
||||
sprintf(
|
||||
'Der Inhalt von Eintrag %d ist kein gültiges Block-Markup; nichts wurde gelesen oder geändert.',
|
||||
$post->ID
|
||||
),
|
||||
'Die Seite im Divi-Builder öffnen und speichern (Divi repariert dabei die Struktur) oder den Nutzer '
|
||||
. 'fragen, ob eine frühere Revision (lit/revisions-list) wiederhergestellt werden soll.'
|
||||
);
|
||||
}
|
||||
|
||||
return new DiviPage($post, $document, NodeIndex::build($document));
|
||||
}
|
||||
|
||||
/**
|
||||
* Additional checks for writing: the type must use the Divi builder, the content must be a
|
||||
* Divi 5 layout (or empty) and saving must not publish a missed schedule (spec §2).
|
||||
*/
|
||||
public function writable(DiviPage $page): ?WP_Error
|
||||
{
|
||||
$post = $page->post;
|
||||
if (! $this->schema->diviEnabled((string) $post->post_type)) {
|
||||
return Errors::unsupported(
|
||||
sprintf('Für den Inhaltstyp "%s" ist der Divi-Builder nicht aktiviert.', $post->post_type),
|
||||
'In Divi → Theme-Optionen → Builder den Builder für diesen Inhaltstyp aktivieren (Administrator).'
|
||||
);
|
||||
}
|
||||
|
||||
if (! $page->isDiviLayout()) {
|
||||
return Errors::unsupported(
|
||||
sprintf('Eintrag %d ist kein Divi-Layout (Block-Editor- oder klassischer Inhalt).', $post->ID),
|
||||
'Den Inhalt mit lit/content-update (direkt: lit-content-update) bearbeiten, oder den Nutzer fragen, ob '
|
||||
. 'die Seite im Divi-Builder neu aufgebaut werden soll.'
|
||||
);
|
||||
}
|
||||
|
||||
return PostGuard::statusStable($post);
|
||||
}
|
||||
|
||||
public static function unknownNode(string $id, WP_Post $post): WP_Error
|
||||
{
|
||||
return Errors::notFound(
|
||||
sprintf('Knoten "%s" gibt es in Eintrag %d nicht.', $id, $post->ID),
|
||||
self::OUTLINE_HINT
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user