diff --git a/src/Integrations/Divi/DiviIntegration.php b/src/Integrations/Divi/DiviIntegration.php index 12ca023..dcc3f7e 100644 --- a/src/Integrations/Divi/DiviIntegration.php +++ b/src/Integrations/Divi/DiviIntegration.php @@ -30,12 +30,18 @@ use LIT\MCP\Integrations\Divi\Design\DiviPresetsAssignAbility; use LIT\MCP\Integrations\Divi\Design\DiviPresetsDeleteAbility; use LIT\MCP\Integrations\Divi\Design\DiviPresetsSetAbility; 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\FontInstaller; use LIT\MCP\Integrations\Divi\Design\PageRewriter; use LIT\MCP\Integrations\Divi\Design\PresetAssignment; use LIT\MCP\Integrations\Divi\Design\PresetComposer; use LIT\MCP\Integrations\Divi\Design\PresetRestorePlanner; 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\Library\DiviLibraryStore; 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\Integration; 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\Content\ContentComposer; use LIT\MCP\Integrations\WordPress\Schema\ContentSchema; @@ -98,6 +106,13 @@ final class DiviIntegration implements Integration, Bootable 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. */ private ?Closure $diviWritable; @@ -123,6 +138,9 @@ final class DiviIntegration implements Integration, Bootable $this->designWriter = new DesignSystemWriter(); $this->colorsAdapter = new GlobalColorsAdapter(); $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->libraryInsertable = $this->gate( static fn (ToolAvailability $t): bool => $t->allows('lit/library-insert') @@ -280,7 +298,8 @@ final class DiviIntegration implements Integration, Bootable new SnapshotStore(), new PresetRestorePlanner($this->designWriter, DesignRights::unfilteredHtml()), 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( $this->designWriter, @@ -306,7 +325,21 @@ final class DiviIntegration implements Integration, Bootable $this->variablesAdapter, $this->designChange(), $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() ), ]; } diff --git a/tests/Unit/Integrations/Divi/DiviIntegrationTest.php b/tests/Unit/Integrations/Divi/DiviIntegrationTest.php index 6a3f91d..5467647 100644 --- a/tests/Unit/Integrations/Divi/DiviIntegrationTest.php +++ b/tests/Unit/Integrations/Divi/DiviIntegrationTest.php @@ -59,6 +59,9 @@ final class DiviIntegrationTest extends TestCase 'lit/divi-design-detach', 'lit/divi-colors-set', 'lit/divi-variables-set', + 'lit/divi-theme-options-get', + 'lit/divi-theme-options-update', + 'lit/divi-fonts-upload', ]; public function testProvidesTheDiviAbilitiesOnlyWithDivi5(): void diff --git a/tests/Unit/Integrations/Divi/Release/DiviToolFeaturesTest.php b/tests/Unit/Integrations/Divi/Release/DiviToolFeaturesTest.php index e534bc1..55c525c 100644 --- a/tests/Unit/Integrations/Divi/Release/DiviToolFeaturesTest.php +++ b/tests/Unit/Integrations/Divi/Release/DiviToolFeaturesTest.php @@ -56,7 +56,9 @@ final class DiviToolFeaturesTest extends TestCase 'lit/divi-presets-set' => ['design-read', 'preset-write', 'catalog'], 'lit/divi-presets-delete' => ['design-read', 'preset-write'], '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' => [ 'page-read', 'page-write', 'catalog', 'design-read', 'preset-write', 'library-read', 'library-write', 'tb-write', 'roles', @@ -67,6 +69,9 @@ final class DiviToolFeaturesTest extends TestCase ], 'lit/divi-colors-set' => ['design-read', 'color-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) 'lit/library-list' => ['library-read', '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'], ['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 of one entry' => [ '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/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-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-presets-assign' => 'page-write', 'lit/tb-assign' => 'tb-write', 'lit/tb-activate' => 'tb-write', 'lit/tb-create-template' => 'tb-write',