feat: Theme-Optionen und Schriften-Upload in der Divi-Integration; je ein geteilter Adapter; Restore von theme_options

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
Jan-Niclas Loosen
2026-10-19 18:08:39 +02:00
co-authored by Claude Sonnet 5.5
parent 35e92fcb6c
commit 7e7568ea62
3 changed files with 49 additions and 3 deletions
+35 -2
View File
@@ -30,12 +30,18 @@ use LIT\MCP\Integrations\Divi\Design\DiviPresetsAssignAbility;
use LIT\MCP\Integrations\Divi\Design\DiviPresetsDeleteAbility; use LIT\MCP\Integrations\Divi\Design\DiviPresetsDeleteAbility;
use LIT\MCP\Integrations\Divi\Design\DiviPresetsSetAbility; use LIT\MCP\Integrations\Divi\Design\DiviPresetsSetAbility;
use LIT\MCP\Integrations\Divi\Design\DiviVariablesSetAbility; use LIT\MCP\Integrations\Divi\Design\DiviVariablesSetAbility;
use LIT\MCP\Integrations\Divi\Design\DiviFontsUploadAbility;
use LIT\MCP\Integrations\Divi\Design\DiviThemeOptionsGetAbility;
use LIT\MCP\Integrations\Divi\Design\DiviThemeOptionsUpdateAbility;
use LIT\MCP\Integrations\Divi\Design\FontCatalog; use LIT\MCP\Integrations\Divi\Design\FontCatalog;
use LIT\MCP\Integrations\Divi\Design\FontInstaller;
use LIT\MCP\Integrations\Divi\Design\PageRewriter; use LIT\MCP\Integrations\Divi\Design\PageRewriter;
use LIT\MCP\Integrations\Divi\Design\PresetAssignment; use LIT\MCP\Integrations\Divi\Design\PresetAssignment;
use LIT\MCP\Integrations\Divi\Design\PresetComposer; use LIT\MCP\Integrations\Divi\Design\PresetComposer;
use LIT\MCP\Integrations\Divi\Design\PresetRestorePlanner; use LIT\MCP\Integrations\Divi\Design\PresetRestorePlanner;
use LIT\MCP\Integrations\Divi\Design\SnapshotStore; use LIT\MCP\Integrations\Divi\Design\SnapshotStore;
use LIT\MCP\Integrations\Divi\Design\ThemeOptionsAdapter;
use LIT\MCP\Integrations\Divi\Design\ThemeOptionsRestorePlanner;
use LIT\MCP\Integrations\Divi\Design\VariableRestorePlanner; use LIT\MCP\Integrations\Divi\Design\VariableRestorePlanner;
use LIT\MCP\Integrations\Divi\Library\DiviLibraryStore; use LIT\MCP\Integrations\Divi\Library\DiviLibraryStore;
use LIT\MCP\Integrations\Divi\Library\DiviRoles; use LIT\MCP\Integrations\Divi\Library\DiviRoles;
@@ -74,6 +80,8 @@ use LIT\MCP\Integrations\Divi\ThemeBuilder\ThemeBuilderIntegration;
use LIT\MCP\Integrations\Core\EnvironmentComponent; use LIT\MCP\Integrations\Core\EnvironmentComponent;
use LIT\MCP\Integrations\Integration; use LIT\MCP\Integrations\Integration;
use LIT\MCP\Integrations\Media\MediaUsageSource; use LIT\MCP\Integrations\Media\MediaUsageSource;
use LIT\MCP\Integrations\Media\RemoteFileFetcher;
use LIT\MCP\Limits;
use LIT\MCP\Integrations\WordPress\BuilderDetector; use LIT\MCP\Integrations\WordPress\BuilderDetector;
use LIT\MCP\Integrations\WordPress\Content\ContentComposer; use LIT\MCP\Integrations\WordPress\Content\ContentComposer;
use LIT\MCP\Integrations\WordPress\Schema\ContentSchema; use LIT\MCP\Integrations\WordPress\Schema\ContentSchema;
@@ -98,6 +106,13 @@ final class DiviIntegration implements Integration, Bootable
private GlobalVariablesAdapter $variablesAdapter; private GlobalVariablesAdapter $variablesAdapter;
private ThemeOptionsAdapter $themeOptionsAdapter;
/** One per request: the catalog holds Divi's big font list (ruling W5). */
private FontCatalog $fontCatalog;
private FontInstaller $fontInstaller;
/** @var (Closure(): bool)|null Whether lit/divi-insert is enabled (ruling P9, E9, audit M2); null = always. */ /** @var (Closure(): bool)|null Whether lit/divi-insert is enabled (ruling P9, E9, audit M2); null = always. */
private ?Closure $diviWritable; private ?Closure $diviWritable;
@@ -123,6 +138,9 @@ final class DiviIntegration implements Integration, Bootable
$this->designWriter = new DesignSystemWriter(); $this->designWriter = new DesignSystemWriter();
$this->colorsAdapter = new GlobalColorsAdapter(); $this->colorsAdapter = new GlobalColorsAdapter();
$this->variablesAdapter = new GlobalVariablesAdapter(); $this->variablesAdapter = new GlobalVariablesAdapter();
$this->themeOptionsAdapter = new ThemeOptionsAdapter();
$this->fontCatalog = new FontCatalog();
$this->fontInstaller = new FontInstaller();
$this->diviWritable = $this->gate(static fn (ToolAvailability $t): bool => $t->allows('lit/divi-insert')); $this->diviWritable = $this->gate(static fn (ToolAvailability $t): bool => $t->allows('lit/divi-insert'));
$this->libraryInsertable = $this->gate( $this->libraryInsertable = $this->gate(
static fn (ToolAvailability $t): bool => $t->allows('lit/library-insert') static fn (ToolAvailability $t): bool => $t->allows('lit/library-insert')
@@ -280,7 +298,8 @@ final class DiviIntegration implements Integration, Bootable
new SnapshotStore(), new SnapshotStore(),
new PresetRestorePlanner($this->designWriter, DesignRights::unfilteredHtml()), new PresetRestorePlanner($this->designWriter, DesignRights::unfilteredHtml()),
new ColorRestorePlanner($this->designWriter, $this->colorsAdapter), new ColorRestorePlanner($this->designWriter, $this->colorsAdapter),
new VariableRestorePlanner($this->designWriter, $this->variablesAdapter) new VariableRestorePlanner($this->designWriter, $this->variablesAdapter),
new ThemeOptionsRestorePlanner($this->designWriter, $this->themeOptionsAdapter)
), ),
new DiviPresetsAssignAbility( new DiviPresetsAssignAbility(
$this->designWriter, $this->designWriter,
@@ -306,7 +325,21 @@ final class DiviIntegration implements Integration, Bootable
$this->variablesAdapter, $this->variablesAdapter,
$this->designChange(), $this->designChange(),
$this->globalUsages(), $this->globalUsages(),
new FontCatalog() $this->fontCatalog
),
new DiviThemeOptionsGetAbility($this->themeOptionsAdapter),
new DiviThemeOptionsUpdateAbility(
$this->designWriter,
$this->themeOptionsAdapter,
$this->fontCatalog,
$this->designChange()
),
new DiviFontsUploadAbility(
$this->fontCatalog,
$this->fontInstaller,
new RemoteFileFetcher(),
$this->log,
new Limits()
), ),
]; ];
} }
@@ -59,6 +59,9 @@ final class DiviIntegrationTest extends TestCase
'lit/divi-design-detach', 'lit/divi-design-detach',
'lit/divi-colors-set', 'lit/divi-colors-set',
'lit/divi-variables-set', 'lit/divi-variables-set',
'lit/divi-theme-options-get',
'lit/divi-theme-options-update',
'lit/divi-fonts-upload',
]; ];
public function testProvidesTheDiviAbilitiesOnlyWithDivi5(): void public function testProvidesTheDiviAbilitiesOnlyWithDivi5(): void
@@ -56,7 +56,9 @@ final class DiviToolFeaturesTest extends TestCase
'lit/divi-presets-set' => ['design-read', 'preset-write', 'catalog'], 'lit/divi-presets-set' => ['design-read', 'preset-write', 'catalog'],
'lit/divi-presets-delete' => ['design-read', 'preset-write'], 'lit/divi-presets-delete' => ['design-read', 'preset-write'],
'lit/divi-design-snapshots' => [], 'lit/divi-design-snapshots' => [],
'lit/divi-design-restore' => ['design-read', 'preset-write', 'color-write', 'variable-write'], 'lit/divi-design-restore' => [
'design-read', 'preset-write', 'color-write', 'variable-write', 'theme-option-read', 'theme-option-write',
],
'lit/divi-presets-assign' => [ 'lit/divi-presets-assign' => [
'page-read', 'page-write', 'catalog', 'design-read', 'preset-write', 'library-read', 'library-write', 'page-read', 'page-write', 'catalog', 'design-read', 'preset-write', 'library-read', 'library-write',
'tb-write', 'roles', 'tb-write', 'roles',
@@ -67,6 +69,9 @@ final class DiviToolFeaturesTest extends TestCase
], ],
'lit/divi-colors-set' => ['design-read', 'color-write'], 'lit/divi-colors-set' => ['design-read', 'color-write'],
'lit/divi-variables-set' => ['design-read', 'variable-write'], 'lit/divi-variables-set' => ['design-read', 'variable-write'],
'lit/divi-theme-options-get' => ['theme-option-read'],
'lit/divi-theme-options-update' => ['theme-option-read', 'theme-option-write'],
'lit/divi-fonts-upload' => ['font-upload'],
// Library (divi-library) // Library (divi-library)
'lit/library-list' => ['library-read', 'roles'], 'lit/library-list' => ['library-read', 'roles'],
'lit/library-find-similar' => ['page-read', 'catalog', 'roles'], 'lit/library-find-similar' => ['page-read', 'catalog', 'roles'],
@@ -194,6 +199,10 @@ final class DiviToolFeaturesTest extends TestCase
'lit/divi-design-restore', ['snapshot_id' => 'variables-20261016T100000Z-aaaaaa'], 'lit/divi-design-restore', ['snapshot_id' => 'variables-20261016T100000Z-aaaaaa'],
['design-read', 'variable-write'], ['design-read', 'variable-write'],
], ],
'design-restore of theme_options' => [
'lit/divi-design-restore', ['snapshot_id' => 'theme_options-20261016T100000Z-aaaaaa'],
['theme-option-read', 'theme-option-write'],
],
'library-list' => ['lit/library-list', [], ['library-read', 'roles']], 'library-list' => ['lit/library-list', [], ['library-read', 'roles']],
'library-list of one entry' => [ 'library-list of one entry' => [
'lit/library-list', ['id' => $library], ['library-read', 'roles', 'page-read', 'design-read'], 'lit/library-list', ['id' => $library], ['library-read', 'roles', 'page-read', 'design-read'],
@@ -351,6 +360,7 @@ final class DiviToolFeaturesTest extends TestCase
'lit/library-make-global' => 'library-write', 'lit/library-save' => 'library-write', 'lit/library-make-global' => 'library-write', 'lit/library-save' => 'library-write',
'lit/divi-presets-set' => 'preset-write', 'lit/divi-presets-delete' => 'preset-write', 'lit/divi-presets-set' => 'preset-write', 'lit/divi-presets-delete' => 'preset-write',
'lit/divi-colors-set' => 'color-write', 'lit/divi-variables-set' => 'variable-write', 'lit/divi-colors-set' => 'color-write', 'lit/divi-variables-set' => 'variable-write',
'lit/divi-theme-options-update' => 'theme-option-write', 'lit/divi-fonts-upload' => 'font-upload',
'lit/divi-design-restore' => 'preset-write', 'lit/divi-design-detach' => 'preset-write', 'lit/divi-design-restore' => 'preset-write', 'lit/divi-design-detach' => 'preset-write',
'lit/divi-presets-assign' => 'page-write', 'lit/tb-assign' => 'tb-write', 'lit/tb-activate' => 'tb-write', 'lit/divi-presets-assign' => 'page-write', 'lit/tb-assign' => 'tb-write', 'lit/tb-activate' => 'tb-write',
'lit/tb-create-template' => 'tb-write', 'lit/tb-create-template' => 'tb-write',