Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
179 lines
6.2 KiB
PHP
179 lines
6.2 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace LIT\MCP;
|
|
|
|
use LIT\MCP\Abilities\AbilityRegistrar;
|
|
use LIT\MCP\Abilities\ExposurePolicy;
|
|
use LIT\MCP\Admin\AccessTab;
|
|
use LIT\MCP\Admin\AdminNotices;
|
|
use LIT\MCP\Admin\ConnectionRepository;
|
|
use LIT\MCP\Admin\ConnectionStatus;
|
|
use LIT\MCP\Admin\ConnectionTab;
|
|
use LIT\MCP\Admin\ConnectionUsage;
|
|
use LIT\MCP\Admin\LimitsTab;
|
|
use LIT\MCP\Admin\LogTab;
|
|
use LIT\MCP\Admin\SettingsPage;
|
|
use LIT\MCP\Admin\SiteHealth;
|
|
use LIT\MCP\Admin\ToolsTab;
|
|
use LIT\MCP\ChangeLog\ChangeLog;
|
|
use LIT\MCP\ChangeLog\ChangeLogCleanup;
|
|
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\IntegrationRegistry;
|
|
use LIT\MCP\Integrations\Media\MediaIntegration;
|
|
use LIT\MCP\Integrations\Pods\PodsFieldSource;
|
|
use LIT\MCP\Integrations\WordPress\Schema\ContentSchema;
|
|
use LIT\MCP\Integrations\WordPress\Schema\RegisteredMetaSource;
|
|
use LIT\MCP\Integrations\WordPress\Schema\SampledMetaSource;
|
|
use LIT\MCP\Integrations\WordPress\WordPressIntegration;
|
|
use LIT\MCP\Server\AuthorizeGate;
|
|
use LIT\MCP\Server\DiscoveryCanonical;
|
|
use LIT\MCP\Server\RequestContext;
|
|
use LIT\MCP\Server\ServerRegistrar;
|
|
use LIT\MCP\Server\VisibilityFilter;
|
|
|
|
defined('ABSPATH') || exit;
|
|
|
|
/**
|
|
* Wires WP-LIT-MCP into WordPress.
|
|
*/
|
|
final class Plugin
|
|
{
|
|
public const VERSION = '0.3.0';
|
|
|
|
private static string $pluginFile = '';
|
|
private static ?SettingsPage $settingsPage = null;
|
|
|
|
public static function boot(string $pluginFile): void
|
|
{
|
|
self::$pluginFile = $pluginFile;
|
|
|
|
$missing = (new Dependencies())->missing();
|
|
if ($missing !== []) {
|
|
self::noticeMissing($missing);
|
|
return;
|
|
}
|
|
|
|
$access = new Access();
|
|
$access->register();
|
|
(new RequestContext())->register();
|
|
(new VisibilityFilter())->register();
|
|
(new DiscoveryCanonical())->register();
|
|
(new AuthorizeGate())->register();
|
|
|
|
$limits = new Limits();
|
|
$changeLog = new ChangeLog();
|
|
(new ChangeLogInstaller($changeLog))->maybeInstall();
|
|
(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);
|
|
};
|
|
$divi = new DiviIntegration(
|
|
$schema,
|
|
$changeLog,
|
|
$libraryWritable,
|
|
libraryVisible: $libraryVisible,
|
|
diviWritable: $diviWritable
|
|
);
|
|
$registry = new IntegrationRegistry(
|
|
new CoreIntegration(self::pluginDir() . '/docs/guides'),
|
|
new WordPressIntegration($schema, $changeLog, $divi->contentComposer($diviWritable)),
|
|
new MediaIntegration($schema, $changeLog, $limits),
|
|
$divi,
|
|
$divi->library($diviWritable)
|
|
);
|
|
|
|
$policy = ExposurePolicy::fromOption()->withGroups($registry->groupMap(...));
|
|
(new AbilityRegistrar($registry, $policy))->register();
|
|
(new ServerRegistrar(
|
|
$registry,
|
|
new InstructionsBuilder($registry, static fn (): array => $schema->postTypeNames(), $policy),
|
|
null,
|
|
null,
|
|
null,
|
|
$policy
|
|
))->register();
|
|
|
|
$status = new ConnectionStatus();
|
|
(new SiteHealth($status))->register();
|
|
$tabs = [
|
|
new ConnectionTab($status),
|
|
new AccessTab($access, new ConnectionRepository()),
|
|
new ToolsTab($registry, $policy),
|
|
new LogTab($changeLog, $limits, $registry),
|
|
new LimitsTab(),
|
|
];
|
|
self::$settingsPage = new SettingsPage(
|
|
$tabs,
|
|
plugins_url('assets/img/loosen-it-logo.svg', self::$pluginFile),
|
|
plugins_url('assets', self::$pluginFile),
|
|
self::VERSION
|
|
);
|
|
(new ConnectionUsage())->register();
|
|
if (is_admin()) {
|
|
self::$settingsPage->register();
|
|
(new AdminNotices($status))->register();
|
|
}
|
|
|
|
\WPMedia\MCP\OAuth\Bootstrap::instance();
|
|
}
|
|
|
|
public static function settingsPage(): ?SettingsPage
|
|
{
|
|
return self::$settingsPage;
|
|
}
|
|
|
|
public static function pluginDir(): string
|
|
{
|
|
return dirname(self::$pluginFile);
|
|
}
|
|
|
|
/**
|
|
* @param list<string> $missing
|
|
*/
|
|
public static function noticeMissing(array $missing): void
|
|
{
|
|
add_action('admin_notices', static function () use ($missing): void {
|
|
printf(
|
|
'<div class="notice notice-error"><p>%s</p></div>',
|
|
esc_html(sprintf(
|
|
/* translators: %s: comma separated list of missing components */
|
|
__('WP-LIT-MCP ist inaktiv. Es fehlt: %s', 'wp-lit-mcp'),
|
|
implode(', ', $missing)
|
|
))
|
|
);
|
|
});
|
|
}
|
|
|
|
private static function contentSchema(): ContentSchema
|
|
{
|
|
$sources = [new RegisteredMetaSource(), new SampledMetaSource()];
|
|
if (PodsFieldSource::isAvailable()) {
|
|
array_unshift($sources, new PodsFieldSource());
|
|
}
|
|
|
|
return new ContentSchema($sources);
|
|
}
|
|
}
|