diff --git a/src/Integrations/Divi/DiviIntegration.php b/src/Integrations/Divi/DiviIntegration.php index 1b606ef..d2e84fa 100644 --- a/src/Integrations/Divi/DiviIntegration.php +++ b/src/Integrations/Divi/DiviIntegration.php @@ -36,6 +36,7 @@ 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; @@ -170,6 +171,17 @@ final class DiviIntegration implements Integration ); } + /** 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 + ); + } + private function writer(DiviPageLoader $loader): DiviWriter { return new DiviWriter( diff --git a/src/Integrations/Divi/ThemeBuilder/TbListAbility.php b/src/Integrations/Divi/ThemeBuilder/TbListAbility.php new file mode 100644 index 0000000..d678905 --- /dev/null +++ b/src/Integrations/Divi/ThemeBuilder/TbListAbility.php @@ -0,0 +1,187 @@ + 'object', + 'properties' => [ + 'template_id' => ['type' => 'integer', 'minimum' => 1], + 'conditions' => ['type' => 'boolean', 'description' => 'Divis Bedingungen mitliefern.'], + ], + 'additionalProperties' => false, + ]; + } + + public function outputSchema(): ?array + { + $objects = ['type' => 'array', 'items' => ['type' => 'object']]; + + return [ + 'type' => 'object', + 'properties' => [ + 'theme_builder_id' => ['type' => 'integer'], + 'draft_pending' => ['type' => 'boolean'], + 'templates' => $objects, + 'detached' => $objects, + 'conditions' => $objects, + 'notes' => ['type' => 'array', 'items' => ['type' => 'string']], + ], + 'required' => ['theme_builder_id', 'draft_pending', 'templates', 'detached', 'notes'], + ]; + } + + public function annotations(): array + { + return Annotations::read(); + } + + public function isDirect(): bool + { + return false; + } + + public function permission(array $input): bool|WP_Error + { + return $this->access->canUse() ?? true; + } + + public function execute(array $input): mixed + { + $refusal = $this->access->canUse(); + if ($refusal !== null) { + return $refusal; + } + + $store = $this->access->store(); + $editable = fn (Template $template): bool => $this->access->canEditTemplate($template->id); + $live = array_values(array_filter($store->liveTemplates(), $editable)); + $detached = []; + foreach ($store->detachedIds(self::DETACHED_LIMIT) as $id) { + $template = $store->template($id, false); + if ($template !== null && $editable($template)) { + $detached[] = $template; + } + } + // Only what the user may see counts for shared_with (B11). + $shared = array_merge($live, $detached); + + if (isset($input['template_id'])) { + $id = (int) PostGuard::id($input['template_id']); + $only = static fn (array $list): array => array_values( + array_filter($list, static fn (Template $template): bool => $template->id === $id) + ); + $live = $only($live); + $detached = $only($detached); + if ($live === [] && $detached === []) { + return Errors::notFound( + sprintf('Theme-Builder-Vorlage %d gibt es nicht.', $id), + 'Vorlagen mit lit/tb-list ohne template_id auflisten.' + ); + } + } + + $draft = $store->draftId() > 0; + $notes = []; + if ($draft) { + $notes[] = 'Der Theme Builder hat ungespeicherte Änderungen (Entwurf); bis sie dort gespeichert oder ' + . 'verworfen sind, ändert LIT MCP keine Vorlagen.'; + } + if ($detached !== []) { + $notes[] = 'detached: Diese Vorlagen hat ein späteres Speichern im Theme Builder entfernt – oder jemand ' + . 'hat sie dort bewusst gelöscht; Divi löscht als ungenutzt markierte Vorlagen und Layouts 7 Tage ' + . 'nach der Markierung. Nur auf ausdrücklichen Wunsch wieder zuordnen (bleibt deaktiviert): ' + . 'lit/tb-assign mit reattach: true.'; + } + + $result = [ + 'theme_builder_id' => $store->liveId(), + 'draft_pending' => $draft, + 'templates' => array_map(fn (Template $t): array => $this->describe($t, $shared), $live), + 'detached' => array_map(fn (Template $t): array => $this->describe($t, $shared), $detached), + 'notes' => $notes, + ]; + if (($input['conditions'] ?? false) === true) { + $result['conditions'] = $this->conditions->available(); + } + + return $result; + } + + /** + * @param list