- m-1: the no-change Divi result carries preview_url/edit_url too - m-2: overview names replace-text's per-page tree_rev - m-3: divi-shortform documents validation_failed vs not_found for unknown IDs - m-4: DiviWriter/DiviPageLoader final (no mocks left), neutral NewDraft log detail, snapshots are Etappe 4 - m-5: Tested up to 7.1, changelog completed - m-6: content-create warns when the extras changed the stored layout - m-7: deleting a whole section outlines nothing instead of the next one - m-8: uninstall removes the user meta lit_mcp_log_per_page; readme lists it Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01At3V6SjwbMe6eMzVGW3Hs7
106 lines
4.0 KiB
PHP
106 lines
4.0 KiB
PHP
<?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 LIT\MCP\Log;
|
||
use WP_Error;
|
||
|
||
defined('ABSPATH') || exit;
|
||
|
||
/**
|
||
* 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.
|
||
*/
|
||
final 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) {
|
||
Log::failure(sprintf('divi parse of post %d', $post->ID), $e->getMessage());
|
||
|
||
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));
|
||
}
|
||
|
||
/** The entry loaded for writing and checked by writable() – the refusal of either step. */
|
||
public function loadWritable(mixed $postId): DiviPage|WP_Error
|
||
{
|
||
$page = $this->load($postId, true);
|
||
if ($page instanceof WP_Error) {
|
||
return $page;
|
||
}
|
||
|
||
return $this->writable($page) ?? $page;
|
||
}
|
||
|
||
/**
|
||
* 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);
|
||
}
|
||
}
|