Files
WP-LIT-MCP/src/Integrations/Divi/DiviIntegration.php
T

255 lines
10 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?php
declare(strict_types=1);
namespace LIT\MCP\Integrations\Divi;
use Closure;
use LIT\MCP\ChangeLog\ChangeLog;
use LIT\MCP\Integrations\Divi\BlockTree\Parser;
use LIT\MCP\Integrations\Divi\Design\DesignSystemReader;
use LIT\MCP\Integrations\Divi\Design\DesignSystemWriter;
use LIT\MCP\Integrations\Divi\Design\DiviDesignSystemAbility;
use LIT\MCP\Integrations\Divi\Design\DesignChange;
use LIT\MCP\Integrations\Divi\Design\DiviPresetGetAbility;
use LIT\MCP\Integrations\Divi\Design\DiviPresetsSetAbility;
use LIT\MCP\Integrations\Divi\Design\PresetComposer;
use LIT\MCP\Integrations\Divi\Design\SnapshotStore;
use LIT\MCP\Integrations\Divi\Library\DiviLibraryStore;
use LIT\MCP\Integrations\Divi\Library\DiviRoles;
use LIT\MCP\Integrations\Divi\Library\GlobalEdits;
use LIT\MCP\Integrations\Divi\Library\GlobalUsages;
use LIT\MCP\Integrations\Divi\Library\LibraryAccess;
use LIT\MCP\Integrations\Divi\Library\LibraryIntegration;
use LIT\MCP\Integrations\Divi\Library\SimilarSections;
use LIT\MCP\Integrations\Divi\Pages\DiviComposition;
use LIT\MCP\Integrations\Divi\Pages\DiviContentComposer;
use LIT\MCP\Integrations\Divi\Pages\DiviDeleteAbility;
use LIT\MCP\Integrations\Divi\Pages\DiviDuplicateNodeAbility;
use LIT\MCP\Integrations\Divi\Pages\DiviDynamicSourcesAbility;
use LIT\MCP\Integrations\Divi\Pages\DynamicFields;
use LIT\MCP\Integrations\Divi\Pages\DiviInsertAbility;
use LIT\MCP\Integrations\Divi\Pages\DiviMoveAbility;
use LIT\MCP\Integrations\Divi\Pages\DiviNodeCopyAbility;
use LIT\MCP\Integrations\Divi\Pages\DiviNodeGetAbility;
use LIT\MCP\Integrations\Divi\Pages\DiviOutlineAbility;
use LIT\MCP\Integrations\Divi\Pages\DiviPageLoader;
use LIT\MCP\Integrations\Divi\Pages\DiviReplaceTextAbility;
use LIT\MCP\Integrations\Divi\Pages\DiviSaver;
use LIT\MCP\Integrations\Divi\Pages\DiviUpdateAbility;
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\Divi\ThemeBuilder\ThemeBuilderIntegration;
use LIT\MCP\Integrations\Integration;
use LIT\MCP\Integrations\WordPress\Content\ContentComposer;
use LIT\MCP\Integrations\WordPress\Schema\ContentSchema;
defined('ABSPATH') || exit;
/**
* Divi 5 pages (spec §5, Etappe 2). Only available when the Divi 5 builder is loaded
* (spec §2 principle 4); Divi 4 is never edited (spec §3.4). The write abilities share one
* DiviWriter; each binds the composition to its entry via DiviWriter::composition().
* contentComposer() gives lit/content-create the Divi short form (spec §6.2).
*/
final class DiviIntegration implements Integration
{
private DiviModules $modules;
private DesignSystemReader $design;
private LibraryAccess $access;
private ThemeBuilderAccess $themeBuilderAccess;
private DesignSystemWriter $designWriter;
/**
* @param (Closure(): bool)|null $libraryWritable Tools-tab gate for writes to library entries
* (group "Divi-Bibliothek" writable); null = always.
* @param (Closure(): bool)|null $libraryVisible Tools-tab gate for library entries at all (group
* "Divi-Bibliothek" not disabled, ruling P9); null = always.
* @param (Closure(): bool)|null $diviWritable Whether lit/divi-insert is enabled – lit/divi-node-copy
* 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,
private ChangeLog $log,
?Closure $libraryWritable = null,
?Closure $libraryVisible = null,
private ?Closure $diviWritable = null,
private ?Closure $libraryInsertable = null,
?Closure $themeBuilderWritable = null,
?Closure $themeBuilderVisible = null
) {
$this->modules = new DiviModules();
$this->design = new DesignSystemReader();
$this->designWriter = new DesignSystemWriter();
$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
{
return 'divi';
}
/** Ruling P13: whether this connection may see library IDs (library group visible, library right). */
public function libraryIdsVisible(): bool
{
return $this->access->revealsIds();
}
public function label(): string
{
return __('Divi', 'wp-lit-mcp');
}
public function isAvailable(): bool
{
return DiviModules::isAvailable();
}
/**
* Divi short form for lit/content-create; available() reports whether Divi 5 is loaded,
* writable() asks $writable (the Tools-tab gate, security M2).
*
* @param (Closure(): bool)|null $writable
*/
public function contentComposer(?Closure $writable = null): ContentComposer
{
return new DiviContentComposer(
$this->schema,
new DiviComposition($this->modules, $this->design),
new DiviSaver(),
$this->design,
$writable
);
}
public function abilities(): array
{
$loader = $this->loader();
$writer = $this->writer($loader);
return [
new DiviOutlineAbility($loader, $this->design),
new DiviNodeGetAbility($loader, $this->design),
new DiviCatalogAbility($this->modules),
new DiviDesignSystemAbility($this->design),
new DiviInsertAbility($writer),
new DiviUpdateAbility($writer),
new DiviMoveAbility($writer),
new DiviDeleteAbility($writer),
new DiviDuplicateNodeAbility($writer),
new DiviReplaceTextAbility($this->schema, $loader, $writer),
new DiviNodeCopyAbility($writer, $loader, $this->diviWritable, $this->libraryInsertable),
new DiviDynamicSourcesAbility($this->schema, new DynamicContentMap(), $this->access->roles()),
new DiviPresetGetAbility($this->designWriter, $this->globalUsages()),
new DiviPresetsSetAbility(
$this->designWriter,
$this->designChange(),
$this->globalUsages(),
$loader,
$this->presetComposer(...)
),
];
}
/**
* The group "Divi-Bibliothek" (lit/library-*), sharing this integration's library access and its
* $diviWritable gate (lit-library-insert follows lit/divi-insert, audit M2).
*/
public function library(): LibraryIntegration
{
$loader = $this->loader();
return new LibraryIntegration(
$this->access,
$loader,
$this->design,
$this->writer($loader),
new DiviSaver(),
$this->log,
$this->modules,
$this->diviWritable,
$this->schema,
$this->globalUsages()
);
}
/**
* The usages of library entries with this integration's Theme Builder access (ruling Q17 M1, review I-2):
* Theme Builder posts are named only while ThemeBuilderAccess::visible(), and a global edit that changes them
* needs the rights of every Theme Builder write (canUse(), in visible()) and a writable "Divi Theme Builder"
* group – as the Divi tools on those layouts.
*/
public function globalUsages(?\wpdb $db = null): GlobalUsages
{
return new GlobalUsages($db, $this->access->roles(), $this->themeBuilderAccess->usageState(...));
}
/** The group "Divi Theme Builder" (lit/tb-*), sharing this integration's Theme Builder access (Etappe 3b). */
public function themeBuilder(): ThemeBuilderIntegration
{
return new ThemeBuilderIntegration(
$this->themeBuilderAccess,
new DiviComposition($this->modules, $this->design),
new DiviSaver(),
$this->log,
new DynamicFields($this->schema)
);
}
private function writer(DiviPageLoader $loader): DiviWriter
{
return new DiviWriter(
$loader,
new DiviSaver(),
$this->log,
$this->design,
new DiviComposition($this->modules, $this->design),
new GlobalEdits($this->access, $this->globalUsages()),
new ThemeBuilderEdits($this->themeBuilderAccess, fields: new DynamicFields($this->schema)),
new SimilarSections($this->access)
);
}
private function designChange(): DesignChange
{
return new DesignChange(new SnapshotStore(), $this->log);
}
private function presetComposer(): PresetComposer
{
$composition = new DiviComposition($this->modules, $this->design);
return new PresetComposer(
$composition->composer(),
$composition->storedGuard(),
$this->designWriter->presetAttrs(...),
$this->designWriter->groupsOf(...),
$this->modules->builderVersion(),
static fn (): int => (int) floor(microtime(true) * 1000)
);
}
private function loader(): DiviPageLoader
{
return new DiviPageLoader($this->schema, new Parser(), $this->access, $this->themeBuilderAccess);
}
}