Files
WP-LIT-MCP/src/Plugin.php
T
2026-09-27 03:12:57 +02:00

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);
}
}