feat: global instance in the builder's format, deglobalize, library content rules
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,107 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace LIT\MCP\Integrations\Divi\BlockTree;
|
||||
|
||||
defined('ABSPATH') || exit;
|
||||
|
||||
/**
|
||||
* post_content of Divi Library entries (F4, fixtures stage3): the nodes sit in a divi/placeholder
|
||||
* frame; a section/row/module entry has exactly one root, a layout entry sections. Pure PHP.
|
||||
*/
|
||||
final class LibraryContent
|
||||
{
|
||||
public const WRAPPER = 'divi/placeholder';
|
||||
|
||||
/**
|
||||
* Parses library content for copying and unifies its line breaks (Container::unifyLineBreaks()).
|
||||
* Never used for the content a tool edits in place – that keeps its bytes.
|
||||
*
|
||||
* @throws MalformedMarkup
|
||||
*/
|
||||
public static function load(Parser $parser, string $content): Document
|
||||
{
|
||||
$document = $parser->parse($content);
|
||||
$document->unifyLineBreaks();
|
||||
|
||||
return $document;
|
||||
}
|
||||
|
||||
/** post_content of a library entry holding $node, framed like the builder's "add to library". */
|
||||
public static function wrap(Block $node): string
|
||||
{
|
||||
return '<!-- wp:' . self::WRAPPER . ' -->' . Serializer::block($node) . '<!-- /wp:' . self::WRAPPER . ' -->';
|
||||
}
|
||||
|
||||
/** @return list<Block> Top-level nodes: the children of the first divi/placeholder, else of the document. */
|
||||
public static function roots(Document $document): array
|
||||
{
|
||||
foreach ($document->children() as $block) {
|
||||
if ($block->isPlaceholder()) {
|
||||
return $block->children();
|
||||
}
|
||||
}
|
||||
|
||||
return $document->children();
|
||||
}
|
||||
|
||||
/** Whether $block is or contains a global module. */
|
||||
public static function containsGlobal(Block $block): bool
|
||||
{
|
||||
if ($block->isGlobal()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
foreach ($block->children() as $child) {
|
||||
if (self::containsGlobal($child)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Why the roots $after may not replace $before, or null. Instances on pages expect their
|
||||
* entry's root type (blockName), so a section/row/module entry keeps exactly one root of the
|
||||
* same type; a layout entry (or one with several roots) holds sections only. A global entry
|
||||
* contains no global modules (F10: Divi forbids nesting).
|
||||
*
|
||||
* @param list<Block> $before
|
||||
* @param list<Block> $after
|
||||
*/
|
||||
public static function shapeViolation(array $before, array $after, ?string $layoutType, bool $global): ?string
|
||||
{
|
||||
if ($after === []) {
|
||||
return 'Der Bibliothekseintrag darf nicht leer werden.';
|
||||
}
|
||||
|
||||
if ($layoutType === 'layout' || count($before) !== 1) {
|
||||
foreach ($after as $root) {
|
||||
if (GlobalInstance::effectiveName($root) !== 'divi/section') {
|
||||
return 'Ein Layout-Eintrag der Bibliothek enthält nur Abschnitte.';
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$expected = GlobalInstance::effectiveName($before[0]);
|
||||
if (count($after) !== 1 || GlobalInstance::effectiveName($after[0]) !== $expected) {
|
||||
return sprintf(
|
||||
'Der Bibliothekseintrag muss genau ein %s bleiben (Instanzen auf den Seiten erwarten diesen Typ).',
|
||||
$expected
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if ($global) {
|
||||
foreach ($after as $root) {
|
||||
if (self::containsGlobal($root)) {
|
||||
return 'Ein globaler Bibliothekseintrag darf keine globalen Module enthalten (Divi erlaubt keine '
|
||||
. 'Verschachtelung).';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user