From 1a215fd7bf001d9be0d46a74ff1cfa330fc850b0 Mon Sep 17 00:00:00 2001 From: Jan-Niclas Loosen Date: Wed, 30 Sep 2026 01:31:23 +0200 Subject: [PATCH] refactor: Divi design adapter, logo lookup moves out of media Co-Authored-By: Claude Opus 5.5 (1M context) --- .../Divi/Design/DesignSystemReader.php | 40 ++++++++++++++-- .../Divi/Release/DiviAdapters.php | 5 +- src/Integrations/Media/MediaUsageFinder.php | 11 +++-- .../Divi/Design/DesignSystemReaderTest.php | 46 +++++++++++++++++++ .../Divi/Release/DiviBoundaryTest.php | 7 ++- 5 files changed, 97 insertions(+), 12 deletions(-) create mode 100644 tests/Unit/Integrations/Divi/Design/DesignSystemReaderTest.php diff --git a/src/Integrations/Divi/Design/DesignSystemReader.php b/src/Integrations/Divi/Design/DesignSystemReader.php index 8465643..87a77d9 100644 --- a/src/Integrations/Divi/Design/DesignSystemReader.php +++ b/src/Integrations/Divi/Design/DesignSystemReader.php @@ -6,24 +6,45 @@ namespace LIT\MCP\Integrations\Divi\Design; use ET\Builder\Packages\GlobalData\GlobalData; use ET\Builder\Packages\GlobalData\GlobalPreset; +use LIT\MCP\Integrations\Divi\Release\Call; +use LIT\MCP\Integrations\Divi\Release\DiviAdapter; +use LIT\MCP\Integrations\Divi\Release\Manifest; defined('ABSPATH') || exit; /** - * Reads the design system from Divi's own APIs once per request (spec §2 principle 2 – no - * copy is ever stored). Not final so abilities can be unit-tested with a mock. + * Adapter "Designsystem" (spec §3): reads the design system from Divi's own APIs + * once per request (spec §2 principle 2 – no copy is ever stored), and the logo of + * Divi's theme options. Not final so abilities can be unit-tested with a mock. */ -class DesignSystemReader +class DesignSystemReader implements DiviAdapter { + /** Divi's theme options (one array option); customizer colors, fonts and + * the logo live there. */ + private const OPTIONS = 'et_divi'; + private ?DesignSystem $designSystem = null; + public static function manifest(): Manifest + { + return new Manifest( + Manifest::DESIGN, + calls: [ + new Call(GlobalData::class . '::get_global_colors', 0), + new Call(GlobalData::class . '::get_global_variables', 0), + new Call(GlobalPreset::class . '::get_data', 0), + ], + keys: [self::OPTIONS] + ); + } + public function read(): DesignSystem { if ($this->designSystem === null) { $colors = GlobalData::get_global_colors(); $variables = GlobalData::get_global_variables(); $presets = GlobalPreset::get_data(); - $options = get_option('et_divi', []); + $options = get_option(self::OPTIONS, []); $this->designSystem = DesignSystem::fromDivi( is_array($colors) ? $colors : [], is_array($variables) ? $variables : [], @@ -34,4 +55,15 @@ class DesignSystemReader return $this->designSystem; } + + /** The logo URL of Divi's theme options (Divi → Theme-Optionen → Logo), + * '' when none is set. */ + public static function diviLogo(): string + { + $options = get_option(self::OPTIONS); + + return is_array($options) && is_string($options['divi_logo'] ?? null) + ? $options['divi_logo'] + : ''; + } } diff --git a/src/Integrations/Divi/Release/DiviAdapters.php b/src/Integrations/Divi/Release/DiviAdapters.php index b1c0e22..96488b9 100644 --- a/src/Integrations/Divi/Release/DiviAdapters.php +++ b/src/Integrations/Divi/Release/DiviAdapters.php @@ -4,6 +4,7 @@ declare(strict_types=1); namespace LIT\MCP\Integrations\Divi\Release; +use LIT\MCP\Integrations\Divi\Design\DesignSystemReader; use LIT\MCP\Integrations\Divi\Library\DiviLibraryStore; use LIT\MCP\Integrations\Divi\Library\DiviRoles; use LIT\MCP\Integrations\Divi\Pages\DiviPages; @@ -13,7 +14,8 @@ use LIT\MCP\Integrations\Divi\ThemeBuilder\DiviThemeBuilder; defined('ABSPATH') || exit; -/** The Divi boundary (spec §3): the only classes that touch Divi, in the order reports list them. */ +/** The Divi boundary (spec §3): the only classes that touch Divi, in the order + * reports list them. */ final class DiviAdapters { /** @var list> */ @@ -22,6 +24,7 @@ final class DiviAdapters DiviLibraryStore::class, DiviRoles::class, DiviThemeBuilder::class, + DesignSystemReader::class, DiviModules::class, DynamicContentMap::class, ]; diff --git a/src/Integrations/Media/MediaUsageFinder.php b/src/Integrations/Media/MediaUsageFinder.php index 421dd54..cccd27d 100644 --- a/src/Integrations/Media/MediaUsageFinder.php +++ b/src/Integrations/Media/MediaUsageFinder.php @@ -5,6 +5,7 @@ declare(strict_types=1); namespace LIT\MCP\Integrations\Media; use Closure; +use LIT\MCP\Integrations\Divi\Design\DesignSystemReader; use LIT\MCP\Integrations\WordPress\PostStatuses; use LIT\MCP\Log; @@ -256,10 +257,14 @@ class MediaUsageFinder ); } - $divi = get_option('et_divi'); - $diviLogo = is_array($divi) && is_string($divi['divi_logo'] ?? null) ? $divi['divi_logo'] : ''; + $diviLogo = DesignSystemReader::diviLogo(); if ($diviLogo !== '' && ContentMediaReferences::matches($fileRegex, $diviLogo) !== false) { - $found['site:divi_logo'] = self::siteUsage('Divi-Logo', 'option', 'divi_logo', self::POSSIBLE); + $found['site:divi_logo'] = self::siteUsage( + 'Divi-Logo', + 'option', + 'divi_logo', + self::POSSIBLE + ); } $this->addWidgetUsages($found, $attachmentId, $fileRegex); diff --git a/tests/Unit/Integrations/Divi/Design/DesignSystemReaderTest.php b/tests/Unit/Integrations/Divi/Design/DesignSystemReaderTest.php new file mode 100644 index 0000000..68680d4 --- /dev/null +++ b/tests/Unit/Integrations/Divi/Design/DesignSystemReaderTest.php @@ -0,0 +1,46 @@ +area); + self::assertSame( + ['GlobalData::get_global_colors()', 'GlobalData::get_global_variables()', + 'GlobalPreset::get_data()'], + array_map(static fn (Call $call): string => $call->label(), $manifest->calls) + ); + self::assertSame(['et_divi'], $manifest->keys); + } + + public function testDiviLogoReadsTheThemeOptions(): void + { + Functions\when('get_option')->alias( + static fn (string $name): mixed => $name === 'et_divi' ? + ['divi_logo' => 'https://k.de/logo.svg'] : false + ); + + self::assertSame('https://k.de/logo.svg', DesignSystemReader::diviLogo()); + } + + public function testDiviLogoIsEmptyWithoutOptions(): void + { + Functions\when('get_option')->justReturn(false); + + self::assertSame('', DesignSystemReader::diviLogo()); + } +} diff --git a/tests/Unit/Integrations/Divi/Release/DiviBoundaryTest.php b/tests/Unit/Integrations/Divi/Release/DiviBoundaryTest.php index cc3a9d5..c0699fe 100644 --- a/tests/Unit/Integrations/Divi/Release/DiviBoundaryTest.php +++ b/tests/Unit/Integrations/Divi/Release/DiviBoundaryTest.php @@ -20,13 +20,12 @@ use ReflectionClass; final class DiviBoundaryTest extends TestCase { /** - * Files that still touch Divi outside an adapter (paths below src/). Tasks 3–7 shrink this list to - * DiviCompatibility.php; Task 8 removes it together with testLegacyFilesStillTouchDivi. + * Files that still touch Divi outside an adapter (paths below src/). Tasks 3–7 + * shrink this list to DiviCompatibility.php; Task 8 removes it together with + * testLegacyFilesStillTouchDivi. */ private const LEGACY = [ - 'Integrations/Divi/Design/DesignSystemReader.php', 'Integrations/Divi/DiviCompatibility.php', - 'Integrations/Media/MediaUsageFinder.php', ]; public function testOnlyAdaptersTouchDivi(): void