207 lines
7.0 KiB
PHP
207 lines
7.0 KiB
PHP
<?php
|
||
|
||
declare(strict_types=1);
|
||
|
||
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;
|
||
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\Release\DiviFeatureGate;
|
||
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;
|
||
use LIT\MCP\Updater\GiteaUpdater;
|
||
use LIT\MCP\Updater\PackageSignature;
|
||
|
||
defined('ABSPATH') || exit;
|
||
|
||
/**
|
||
* Wires WP-LIT-MCP into WordPress.
|
||
*/
|
||
final class Plugin
|
||
{
|
||
public const VERSION = '1.0.0';
|
||
|
||
private static string $pluginFile = '';
|
||
private static ?SettingsPage $settingsPage = null;
|
||
|
||
public static function boot(string $pluginFile): void
|
||
{
|
||
self::$pluginFile = $pluginFile;
|
||
|
||
// Before the dependency check: a plugin that is inactive for a missing dependency must still be updatable.
|
||
(new GiteaUpdater(plugin_basename($pluginFile), dirname($pluginFile), new PackageSignature()))->register();
|
||
|
||
$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();
|
||
$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();
|
||
|
||
(new AbilityRegistrar(
|
||
$registry,
|
||
$policy,
|
||
[new DiviFeatureGate($changeLog)]
|
||
))->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();
|
||
}
|
||
|
||
/**
|
||
* 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 {
|
||
// E3: lit-site-status and the Divi tab name the tools each feature affects; the registry exists only after
|
||
// the Divi integration, so it is asked at call time.
|
||
$registry = null;
|
||
$allAbilities = static function () use (&$registry): array {
|
||
return $registry instanceof IntegrationRegistry ? $registry->abilities() : [];
|
||
};
|
||
$divi = new DiviIntegration($schema, $changeLog, $tools, $allAbilities);
|
||
|
||
$registry = 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()
|
||
);
|
||
|
||
return $registry;
|
||
}
|
||
|
||
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());
|
||
}
|
||
|
||
// Divi's post types and builder check come in statically: the schema exists before the Divi integration.
|
||
return new ContentSchema(
|
||
$sources,
|
||
DiviIntegration::builderEnabledFor(...),
|
||
DiviIntegration::internalPostTypes()
|
||
);
|
||
}
|
||
}
|