pluginsPath = $pluginsPath; $this->enabledPlugins = $enabledPlugins; $this->loadPlugins(); } public function setAPI(CMSAPI $api): void { $this->api = $api; foreach ($this->plugins as $plugin) { if (method_exists($plugin, 'setAPI')) { $plugin->setAPI($api); } } } private function loadPlugins(): void { if (!is_dir($this->pluginsPath)) { return; } $pluginDirs = glob($this->pluginsPath . '/*', GLOB_ONLYDIR); foreach ($pluginDirs as $pluginDir) { $pluginName = basename($pluginDir); if (!in_array($pluginName, $this->enabledPlugins, true)) { continue; } $pluginFile = $pluginDir . '/' . $pluginName . '.php'; if (file_exists($pluginFile)) { require_once $pluginFile; $className = $pluginName; if (class_exists($className)) { $this->plugins[$pluginName] = new $className(); if ($this->api && method_exists($this->plugins[$pluginName], 'setAPI')) { $this->plugins[$pluginName]->setAPI($this->api); } // Auto-register hooks from plugin methods $hookMethods = ['onPageLoad', 'onBeforeRender', 'onAfterRender', 'onSearch', 'onMenuBuild']; foreach ($hookMethods as $hook) { if (method_exists($this->plugins[$pluginName], $hook)) { $this->addAction($hook, [$this->plugins[$pluginName], $hook]); } } // Register filter methods $filterMethods = ['onContentFilter', 'onTitleFilter', 'onMenuFilter']; foreach ($filterMethods as $filter) { if (method_exists($this->plugins[$pluginName], $filter)) { $this->addFilter($filter, [$this->plugins[$pluginName], $filter]); } } } } } } public function addAction(string $hook, callable $callback, int $priority = 10): void { $this->actions[$hook][$priority][] = $callback; } public function addFilter(string $hook, callable $callback, int $priority = 10): void { $this->filters[$hook][$priority][] = $callback; } public function doAction(string $hook, ...$args): void { if (!isset($this->actions[$hook])) return; ksort($this->actions[$hook]); foreach ($this->actions[$hook] as $callbacks) { foreach ($callbacks as $callback) { $callback(...$args); } } } public function applyFilters(string $hook, $value, ...$args) { if (!isset($this->filters[$hook])) return $value; ksort($this->filters[$hook]); foreach ($this->filters[$hook] as $callbacks) { foreach ($callbacks as $callback) { $value = $callback($value, ...$args); } } return $value; } public function getPlugin(string $name): ?object { return $this->plugins[$name] ?? null; } public function getAllPlugins(): array { return $this->plugins; } public function getEnabledPlugins(): array { return $this->enabledPlugins; } public function isEnabled(string $pluginName): bool { return in_array($pluginName, $this->enabledPlugins, true); } public function isPluginViewable(object $plugin): bool { if (method_exists($plugin, 'getConfig')) { $config = $plugin->getConfig(); if (isset($config['viewable']) && $config['viewable'] === false) { return false; } // System plugins don't show in sidebar if (isset($config['type']) && $config['type'] === 'system') { return false; } } return true; } public function getSidebarContent(?array $allowedPlugins = null): string { $sidebarContent = ''; // If allowedPlugins is specified, iterate in that order (user-defined order) if ($allowedPlugins !== null) { foreach ($allowedPlugins as $pluginName) { $plugin = $this->plugins[$pluginName] ?? null; if (!$plugin || !$this->isPluginViewable($plugin) || !method_exists($plugin, 'getSidebarContent')) { continue; } $content = $plugin->getSidebarContent(); if (trim($content) === '') { continue; } $title = 'Plugin'; if (method_exists($plugin, 'getConfig')) { $config = $plugin->getConfig(); $title = $config['title'] ?? 'Plugin'; } $sidebarContent .= '