feat: Divi tools edit Theme Builder layouts; visible layouts need confirm

TB layouts load through ThemeBuilderAccess (P12), live layouts (attached +
enabled, Q8) need a dry run and confirm (Q9) and get the shared restore point
of VisibleEdits, extracted from GlobalEdits (Q2: backup when revisions are
off, P9 refusals, P14 naming). divi/post-content only in body layouts (F22),
also for nested stored nodes. SaveScope::ThemeBuilderLayout sets Divi flags
and clears site CSS + TB cache.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-09-27 15:45:07 +02:00
co-authored by Claude Opus 5.5
parent 4ff1c1e2a5
commit b1dd35abd8
27 changed files with 1106 additions and 132 deletions
+22 -4
View File
@@ -33,6 +33,9 @@ use LIT\MCP\Integrations\Divi\Pages\DiviWriter;
use LIT\MCP\Integrations\Divi\Pages\DynamicContentMap;
use LIT\MCP\Integrations\Divi\Schema\DiviCatalogAbility;
use LIT\MCP\Integrations\Divi\Schema\DiviModules;
use LIT\MCP\Integrations\Divi\ThemeBuilder\DiviThemeBuilder;
use LIT\MCP\Integrations\Divi\ThemeBuilder\ThemeBuilderAccess;
use LIT\MCP\Integrations\Divi\ThemeBuilder\ThemeBuilderEdits;
use LIT\MCP\Integrations\Integration;
use LIT\MCP\Integrations\WordPress\Content\ContentComposer;
use LIT\MCP\Integrations\WordPress\Schema\ContentSchema;
@@ -50,6 +53,7 @@ final class DiviIntegration implements Integration
private DiviModules $modules;
private DesignSystemReader $design;
private LibraryAccess $access;
private ThemeBuilderAccess $themeBuilderAccess;
/**
* @param (Closure(): bool)|null $libraryWritable Tools-tab gate for writes to library entries
@@ -60,6 +64,10 @@ final class DiviIntegration implements Integration
* and lit-library-insert follow it (ruling P9, E9, audit M2); null = always.
* @param (Closure(): bool)|null $libraryInsertable Whether lit/library-insert is enabled –
* lit/divi-node-copy follows it for library entries as source (audit L5); null = always.
* @param (Closure(): bool)|null $themeBuilderWritable Tools-tab gate for writes to Theme Builder layouts in
* the Divi tools (group "Divi Theme Builder" writable, E9); null = always.
* @param (Closure(): bool)|null $themeBuilderVisible Tools-tab gate for Theme Builder layouts in the Divi
* tools at all (group "Divi Theme Builder" not disabled); null = always.
*/
public function __construct(
private ContentSchema $schema,
@@ -67,11 +75,20 @@ final class DiviIntegration implements Integration
?Closure $libraryWritable = null,
?Closure $libraryVisible = null,
private ?Closure $diviWritable = null,
private ?Closure $libraryInsertable = null
private ?Closure $libraryInsertable = null,
?Closure $themeBuilderWritable = null,
?Closure $themeBuilderVisible = null
) {
$this->modules = new DiviModules();
$this->design = new DesignSystemReader();
$this->access = new LibraryAccess(new DiviLibraryStore(), new DiviRoles(), $libraryWritable, $libraryVisible);
$roles = new DiviRoles();
$this->access = new LibraryAccess(new DiviLibraryStore(), $roles, $libraryWritable, $libraryVisible);
$this->themeBuilderAccess = new ThemeBuilderAccess(
new DiviThemeBuilder(),
$roles,
$themeBuilderWritable,
$themeBuilderVisible
);
}
public function id(): string
@@ -161,12 +178,13 @@ final class DiviIntegration implements Integration
$this->log,
$this->design,
new DiviComposition($this->modules, $this->design),
new GlobalEdits($this->access, new GlobalUsages(null, $this->access->roles()))
new GlobalEdits($this->access, new GlobalUsages(null, $this->access->roles())),
new ThemeBuilderEdits($this->themeBuilderAccess)
);
}
private function loader(): DiviPageLoader
{
return new DiviPageLoader($this->schema, new Parser(), $this->access);
return new DiviPageLoader($this->schema, new Parser(), $this->access, $this->themeBuilderAccess);
}
}