refactor: ToolAvailability ersetzt die sechs Referenz-Closures in Plugin (Frage vor der Verdrahtung wirft statt still false); DiviIntegration bekommt den Tools-Tab-Zustand als Objekt; Plugin::integrations() mit Verdrahtungstest für Reihenfolge, einmaliges Booten und spätes Binden
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
6c59934caa
commit
e6e0f914ba
@@ -5,6 +5,7 @@ declare(strict_types=1);
|
||||
namespace LIT\MCP\Integrations\Divi;
|
||||
|
||||
use Closure;
|
||||
use LIT\MCP\Abilities\ToolAvailability;
|
||||
use LIT\MCP\Admin\DiviTab;
|
||||
use LIT\MCP\Admin\SettingsPage;
|
||||
use LIT\MCP\ChangeLog\ChangeLog;
|
||||
@@ -83,40 +84,42 @@ final class DiviIntegration implements Integration, Bootable
|
||||
private ThemeBuilderAccess $themeBuilderAccess;
|
||||
private DesignSystemWriter $designWriter;
|
||||
|
||||
/** @var (Closure(): bool)|null Whether lit/divi-insert is enabled (ruling P9, E9, audit M2); null = always. */
|
||||
private ?Closure $diviWritable;
|
||||
|
||||
/** @var (Closure(): bool)|null Whether lit/library-insert is enabled (audit L5); null = always. */
|
||||
private ?Closure $libraryInsertable;
|
||||
|
||||
/**
|
||||
* @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.
|
||||
* @param ToolAvailability|null $tools The Tools-tab state, asked at call time: the groups "Divi-Bibliothek" and
|
||||
* "Divi Theme Builder" gate their entries in the Divi tools (ruling P9, E9), lit/divi-insert gates
|
||||
* lit/divi-node-copy, lit-library-insert and content-create's divi input (audit M2), lit/library-insert
|
||||
* gates lit/divi-node-copy from a library entry (audit L5). null = no Tools tab: everything allowed.
|
||||
*/
|
||||
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
|
||||
private ?ToolAvailability $tools = null
|
||||
) {
|
||||
$this->modules = new DiviModules();
|
||||
$this->design = new DesignSystemReader();
|
||||
$this->designWriter = new DesignSystemWriter();
|
||||
$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')
|
||||
);
|
||||
$roles = new DiviRoles();
|
||||
$this->access = new LibraryAccess(new DiviLibraryStore(), $roles, $libraryWritable, $libraryVisible);
|
||||
$this->access = new LibraryAccess(
|
||||
new DiviLibraryStore(),
|
||||
$roles,
|
||||
$this->gate(static fn (ToolAvailability $t): bool => $t->groupWritable(LibraryAccess::GROUP)),
|
||||
$this->gate(static fn (ToolAvailability $t): bool => $t->groupEnabled(LibraryAccess::GROUP))
|
||||
);
|
||||
$this->themeBuilderAccess = new ThemeBuilderAccess(
|
||||
new DiviThemeBuilder(),
|
||||
$roles,
|
||||
$themeBuilderWritable,
|
||||
$themeBuilderVisible
|
||||
$this->gate(static fn (ToolAvailability $t): bool => $t->groupWritable(ThemeBuilderAccess::GROUP)),
|
||||
$this->gate(static fn (ToolAvailability $t): bool => $t->groupEnabled(ThemeBuilderAccess::GROUP))
|
||||
);
|
||||
}
|
||||
|
||||
@@ -181,20 +184,12 @@ final class DiviIntegration implements Integration, Bootable
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
* Divi short form for lit/content-create; available() reports whether Divi 5 is loaded, writable() follows
|
||||
* lit/divi-insert's Tools-tab state (security M2).
|
||||
*/
|
||||
public function contentComposer(?Closure $writable = null): ContentComposer
|
||||
public function contentComposer(): ContentComposer
|
||||
{
|
||||
return new DiviContentComposer(
|
||||
$this->schema,
|
||||
$this->composition(),
|
||||
new DiviSaver(),
|
||||
$this->design,
|
||||
$writable
|
||||
);
|
||||
return $this->shortForm($this->diviWritable);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -211,7 +206,7 @@ final class DiviIntegration implements Integration, Bootable
|
||||
|
||||
// Spec §6: the admin check runs as a REST request (same context as MCP calls). Its short form has no
|
||||
// Tools-tab gate: the check tests Divi, not this site's tool switches.
|
||||
$check = fn (): CompatibilityCheck => CompatibilityCheck::create($this->contentComposer());
|
||||
$check = fn (): CompatibilityCheck => CompatibilityCheck::create($this->shortForm(null));
|
||||
(new DiviCheckRoute($check))->register();
|
||||
DiviCheckCommand::register($check);
|
||||
|
||||
@@ -318,6 +313,26 @@ final class DiviIntegration implements Integration, Bootable
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* A gate on the Tools-tab state for the classes that take a (Closure(): bool)|null: asked at call time, null
|
||||
* without $tools (always allowed).
|
||||
*
|
||||
* @param Closure(ToolAvailability): bool $ask
|
||||
* @return (Closure(): bool)|null
|
||||
*/
|
||||
private function gate(Closure $ask): ?Closure
|
||||
{
|
||||
$tools = $this->tools;
|
||||
|
||||
return $tools === null ? null : static fn (): bool => $ask($tools);
|
||||
}
|
||||
|
||||
/** @param (Closure(): bool)|null $writable */
|
||||
private function shortForm(?Closure $writable): DiviContentComposer
|
||||
{
|
||||
return new DiviContentComposer($this->schema, $this->composition(), new DiviSaver(), $this->design, $writable);
|
||||
}
|
||||
|
||||
/**
|
||||
* The one wiring of the composition for every tool that composes (spec D7): presets and Divi's option groups come
|
||||
* from the design system writer, so a stack or group preset in any write tool resolves against the live book.
|
||||
|
||||
Reference in New Issue
Block a user