v2.6.0: Content backup/git versioning, plugin type system, docs update

New features:
- ContentBackup class with ZIP backup/restore and git versioning
- Admin backup & restore page (content-backup.twig) with git init/commit/log/restore
- Plugin type system: system (blue) vs content (green) with visual badges
- PluginAPIInterface + AdminPluginAPI for plugin architecture
- Essential plugin flag (cannot edit/deactivate/delete)

Improvements:
- Consolidated enabled_plugins config (removed plugins.enabled)
- Removed Analytics/Logging toggles from admin config page
- Fixed Dashboard plugin Twig comments rendered as text
- Updated 20 guide files (NL+EN): configuratie, plugins, plugin-development,
  core-classes, theme-json, layouts, scss-styling, admin-beheerder, nieuw-thema, architectuur
- Improved accessibility test script (grep -E, min/max checks)

Cleanup:
- Removed unused classes: ARIAComponents, AccessibilityManager, ContentSecurityPolicy, etc.
- Removed vendor packages: mustache/mustache, php-mqtt/client
- Removed old templates: logs.twig, statistics.twig (now plugins)
- Moved language files to language/ directory

Tests:
- Pentest: 30/30 passed, 0 vulnerabilities
- WCAG 2.1 AA: 25/25 passed, 100% compliance
This commit is contained in:
2026-08-15 19:21:04 +02:00
parent cd498c8c3a
commit 1492dcf71f
207 changed files with 3742 additions and 22688 deletions
+66 -6
View File
@@ -4,7 +4,7 @@ class PluginManager
{
private array $plugins = [];
private string $pluginsPath;
private ?CMSAPI $api = null;
private $api = null;
private array $enabledPlugins = [];
private array $actions = [];
private array $filters = [];
@@ -16,7 +16,7 @@ class PluginManager
$this->loadPlugins();
}
public function setAPI(CMSAPI $api): void
public function setAPI($api): void
{
$this->api = $api;
@@ -50,10 +50,6 @@ class PluginManager
$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'];
@@ -193,4 +189,68 @@ class PluginManager
}
return $urls;
}
/**
* Collect admin menu items from all enabled plugins that implement getAdminMenu().
* Each plugin returns [['route' => 'plugin-name/action', 'label' => 'Label', 'icon' => 'bi-icon']].
*
* @return array Admin menu items from all plugins
*/
public function getAdminMenuItems(): array
{
$items = [];
foreach ($this->plugins as $pluginName => $plugin) {
if (method_exists($plugin, 'getAdminMenu')) {
$pluginItems = $plugin->getAdminMenu();
if (is_array($pluginItems)) {
foreach ($pluginItems as $item) {
$items[] = $item;
}
}
}
}
return $items;
}
/**
* Dispatch an admin route to a plugin that handles it.
* Plugins implement handleAdminRoute(string $action): ?string (returns rendered HTML or null).
*
* @param string $pluginName Plugin name
* @param string $action Action/sub-route within the plugin
* @return string|null Rendered HTML, or null if plugin doesn't handle it
*/
public function dispatchAdminRoute(string $pluginName, string $action): ?string
{
$plugin = $this->getPlugin($pluginName);
if ($plugin === null) {
return null;
}
if (method_exists($plugin, 'handleAdminRoute')) {
return $plugin->handleAdminRoute($action);
}
return null;
}
/**
* Find which plugin handles a given admin route.
*
* @param string $route The admin route (e.g. 'statistics' or 'statistics/details')
* @return array|null ['plugin' => name, 'action' => action] or null
*/
public function resolveAdminRoute(string $route): ?array
{
foreach ($this->getAdminMenuItems() as $item) {
$itemRoute = $item['route'] ?? '';
// Check if the requested route starts with this plugin's route
if ($route === $itemRoute || str_starts_with($route, $itemRoute . '/')) {
$action = substr($route, strlen($itemRoute) + 1);
return [
'plugin' => $item['plugin'] ?? '',
'action' => $action,
];
}
}
return null;
}
}