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:
Jan-Niclas Loosen
2026-10-18 21:56:36 +02:00
co-authored by Claude Opus 5.5
parent 6c59934caa
commit e6e0f914ba
6 changed files with 376 additions and 109 deletions
+35 -58
View File
@@ -6,6 +6,7 @@ namespace LIT\MCP;
use LIT\MCP\Abilities\AbilityRegistrar;
use LIT\MCP\Abilities\ExposurePolicy;
use LIT\MCP\Abilities\ToolAvailability;
use LIT\MCP\Admin\AccessTab;
use LIT\MCP\Admin\AdminNotices;
use LIT\MCP\Admin\ConnectionRepository;
@@ -23,9 +24,7 @@ use LIT\MCP\ChangeLog\ChangeLogInstaller;
use LIT\MCP\Guides\InstructionsBuilder;
use LIT\MCP\Integrations\Core\CoreIntegration;
use LIT\MCP\Integrations\Divi\DiviIntegration;
use LIT\MCP\Integrations\Divi\Library\LibraryAccess;
use LIT\MCP\Integrations\Divi\Release\DiviToolGate;
use LIT\MCP\Integrations\Divi\ThemeBuilder\ThemeBuilderAccess;
use LIT\MCP\Integrations\IntegrationRegistry;
use LIT\MCP\Integrations\Media\MediaIntegration;
use LIT\MCP\Integrations\Pods\PodsFieldSource;
@@ -74,66 +73,14 @@ final class Plugin
(new ChangeLogCleanup($changeLog, $limits))->register();
$schema = self::contentSchema();
$registry = null;
$policy = null;
// Ruling P9: with the group "Divi-Bibliothek" disabled, the Divi tools do not see library entries.
$libraryVisible = static function () use (&$policy): bool {
return $policy instanceof ExposurePolicy && $policy->groupEnabled(LibraryAccess::GROUP);
};
// Security M2: content-create's divi input follows lit/divi-insert (asked at execute time,
// when registry and policy exist), so the Divi group's switches stop every Divi layout write.
$diviWritable = static function () use (&$registry, &$policy): bool {
return $registry instanceof IntegrationRegistry
&& $policy instanceof ExposurePolicy
&& $policy->allows('lit/divi-insert', $registry->abilities());
};
// The Divi tools write to library entries only while the group "Divi-Bibliothek" may write (E9).
$libraryWritable = static function () use (&$policy): bool {
return $policy instanceof ExposurePolicy && $policy->groupWritable(LibraryAccess::GROUP);
};
// Audit L5: lit/divi-node-copy from a library entry follows lit/library-insert's Tools-tab state.
$libraryInsertable = static function () use (&$registry, &$policy): bool {
return $registry instanceof IntegrationRegistry
&& $policy instanceof ExposurePolicy
&& $policy->allows('lit/library-insert', $registry->abilities());
};
// Etappe 3b: the group "Divi Theme Builder" gates TB layouts in the Divi tools like the library group (E9).
$themeBuilderVisible = static function () use (&$policy): bool {
return $policy instanceof ExposurePolicy && $policy->groupEnabled(ThemeBuilderAccess::GROUP);
};
$themeBuilderWritable = static function () use (&$policy): bool {
return $policy instanceof ExposurePolicy && $policy->groupWritable(ThemeBuilderAccess::GROUP);
};
$divi = new DiviIntegration(
$schema,
$changeLog,
$libraryWritable,
libraryVisible: $libraryVisible,
diviWritable: $diviWritable,
libraryInsertable: $libraryInsertable,
themeBuilderWritable: $themeBuilderWritable,
themeBuilderVisible: $themeBuilderVisible
);
$registry = new IntegrationRegistry(
new CoreIntegration(self::pluginDir() . '/docs/guides', [$divi->environmentComponent()]),
new WordPressIntegration(
$schema,
$changeLog,
$divi->contentComposer($diviWritable),
$divi->libraryIdsVisible(...),
$divi->builderDetector()
),
new MediaIntegration($schema, $changeLog, $limits, $divi->mediaUsageSources()),
$divi,
$divi->library(),
$divi->themeBuilder()
);
$tools = new ToolAvailability();
$registry = self::integrations($schema, $changeLog, $limits, $tools);
$policy = ExposurePolicy::fromOption()->withGroups($registry->groupMap(...));
$tools->bind($registry, $policy);
// Integration-owned hooks (Divi: upgrader hook, check route, CLI command, tab "Divi") – before the settings
// page reads its tabs.
$registry->boot();
$policy = ExposurePolicy::fromOption()->withGroups($registry->groupMap(...));
(new AbilityRegistrar(
$registry,
$policy,
@@ -172,6 +119,36 @@ final class Plugin
\WPMedia\MCP\OAuth\Bootstrap::instance();
}
/**
* The integrations in registry order – the order of the Tools tab, the guide and the server instructions. The
* Divi integration and its groups share $tools, which Plugin binds once registry and policy exist.
*
* @internal Public for PluginTest only.
*/
public static function integrations(
ContentSchema $schema,
ChangeLog $changeLog,
Limits $limits,
ToolAvailability $tools
): IntegrationRegistry {
$divi = new DiviIntegration($schema, $changeLog, $tools);
return new IntegrationRegistry(
new CoreIntegration(self::pluginDir() . '/docs/guides', [$divi->environmentComponent()]),
new WordPressIntegration(
$schema,
$changeLog,
$divi->contentComposer(),
$divi->libraryIdsVisible(...),
$divi->builderDetector()
),
new MediaIntegration($schema, $changeLog, $limits, $divi->mediaUsageSources()),
$divi,
$divi->library(),
$divi->themeBuilder()
);
}
public static function settingsPage(): ?SettingsPage
{
return self::$settingsPage;