From d039114f2174b9e23c08c44fe0d14f0edb00179a Mon Sep 17 00:00:00 2001 From: Edwin Noorlander Date: Thu, 27 Aug 2026 20:13:20 +0000 Subject: [PATCH] v2.6.7 (Lyra): Essential plugins consistent tonen + docblock-vereiste Vervolg op v2.6.6: vier plekken lazen nog ruwe enabled_plugins zonder essential-forcering. handleDashboard(), handleContentFiles/Edit() en AdminPluginAPI::getEnabledPlugins() forceren nu essential plugins consistent met PluginManager. Dode functie countEnabledPlugins() verwijderd. AGENTS.md aangevuld met verplichte docblock-sectie. --- TODO.md | 8 +++ cms/core/plugin/AdminPluginAPI.php | 34 +++++++++++- docs/release-notes/v2.6.7.md | 88 ++++++++++++++++++++++++++++++ public/admin.php | 28 +++------- version.php | 2 +- 5 files changed, 139 insertions(+), 21 deletions(-) create mode 100644 docs/release-notes/v2.6.7.md diff --git a/TODO.md b/TODO.md index fb269ce..5797de3 100644 --- a/TODO.md +++ b/TODO.md @@ -37,6 +37,14 @@ - [ ] AGENTS.md + config.json.example bijwerken - [ ] Verificatie: php -l, curl met Host-header, domein-switch in admin testen +## v2.6.7 (2026-08-27) ✅ +- [x] Bug: dashboard toonde essential plugins (Dashboard, Navigation) als inactief — handleDashboard() admin.php:670 las ruwe config zonder essential-forcering; gefixt met isProtectedPlugin() +- [x] Bug: AdminPluginAPI::getEnabledPlugins() retourneerde ruwe config.enabled_plugins zonder essential-forcering — bevraagt nu PluginManager (single source of truth); fallback doet zelf essential-scan +- [x] Bug: content-editor plugin-selector (handleContentFiles/handleContentEdit) miste Navigation als die uit enabled_plugins raakte — essential/protected content-plugins worden nu altijd getoond (isProtectedPlugin()) +- [x] Opschonen: dode functie countEnabledPlugins() verwijderd (nergens aangeroepen) +- [x] Docs: AGENTS.md aangevuld met verplichte docblock-sectie (WordPress-stijl, @since, @param, @return, {@type}-structuur) +- [x] Verslag gemaakt (docs/release-notes/v2.6.7.md) + ## v2.6.6 (2026-08-27) ✅ - [x] Bug: essential/protected plugins (Dashboard, Navigation) konden niet meer geactiveerd worden als ze uit enabled_plugins raakten — core forceert nu laden van essential plugins (plugin.json `essential: true`); admin-UI toont ze altijd als Actief; toggle-handler staat aanzetten wél toe, uitzetten blijft geblokkeerd; isProtectedPlugin() dekt nu ook essential (alle delete/bewerk/file-handlers automatisch beschermd) - [x] Docs: guide plugin-development (NL/EN) beschrijving `essential`-veld bijgewerkt ("altijd geladen" + niet uit te schakelen/bewerken/verwijderen) diff --git a/cms/core/plugin/AdminPluginAPI.php b/cms/core/plugin/AdminPluginAPI.php index 73513db..3824748 100644 --- a/cms/core/plugin/AdminPluginAPI.php +++ b/cms/core/plugin/AdminPluginAPI.php @@ -141,13 +141,45 @@ class AdminPluginAPI implements PluginAPIInterface /** * Haal de lijst met ingeschakelde plugins op. * + * Essential plugins (plugin.json `essential: true`) worden altijd + * geretourneerd, ook als ze niet in config.enabled_plugins staan, en + * komen consistent met de PluginManager op de eerste positie. Als de + * PluginManager is geïnjecteerd wordt dien geautoriseerde lijst + * gebruikt (single source of truth); anders valt de methode terug op + * config.enabled_plugins aangevuld met een lokale essential-scan. + * * @since 2.6.5 * * @return array Lijst met ingeschakelde plugin-namen. */ public function getEnabledPlugins(): array { - return $this->config['enabled_plugins'] ?? []; + if ($this->pluginManager !== null) { + return $this->pluginManager->getEnabledPlugins(); + } + $enabled = $this->config['enabled_plugins'] ?? []; + if (!is_array($enabled)) { + $enabled = []; + } + // Fallback: essential plugins forceren als de PluginManager niet + // geïnjecteerd is (houdt de lijst consistent met loadPlugins()). + $pluginsDir = $this->projectRoot . '/plugins'; + if (is_dir($pluginsDir)) { + foreach (glob($pluginsDir . '/*', GLOB_ONLYDIR) as $pluginDir) { + $pluginName = basename($pluginDir); + $pluginJsonFile = $pluginDir . '/plugin.json'; + if (!file_exists($pluginJsonFile)) { + continue; + } + $data = json_decode(file_get_contents($pluginJsonFile), true); + if (is_array($data) && ($data['essential'] ?? false) === true + && !in_array($pluginName, $enabled, true) + ) { + $enabled[] = $pluginName; + } + } + } + return array_values($enabled); } /** diff --git a/docs/release-notes/v2.6.7.md b/docs/release-notes/v2.6.7.md new file mode 100644 index 0000000..5775113 --- /dev/null +++ b/docs/release-notes/v2.6.7.md @@ -0,0 +1,88 @@ +# v2.6.7 (Lyra) — Essential plugins consistent tonen + docblock-vereiste + +Releasedatum: 2026-08-27 +Codename: Lyra +Status: stable + +## Samenvatting + +Vervolg op v2.6.6 (essential plugins altijd laden). In v2.6.6 forcerde +alleen `PluginManager::loadPlugins()` het laden van essential plugins — +maar vier andere plekken in de code lazen nog de ruwe +`config.enabled_plugins` zonder essential-forcering, waardoor +essential/protected plugins (Dashboard, Navigation) op diverse plekken +als "Inactief" of onzichtbaar toonden, ondanks dat ze effectief draaiden. + +Deze release maakt alle plekken die plugin-status bepalen consistent met +`PluginManager`. Daarnaast is de docblock-vereiste formeel vastgelegd in +`AGENTS.md`. + +## Wijzigingen + +### `cms/core/plugin/AdminPluginAPI.php` +- `getEnabledPlugins()` bevraagt nu de geïnjecteerde PluginManager (single + source of truth) wanneer aanwezig. Fallback (zonder PluginManager) doet + zelf een essential-scan over de plugins-directory, consistent met + `PluginManager::loadPlugins()`. Essential plugins worden altijd + geretourneerd, ook als ze niet in `config.enabled_plugins` staan. + +### `public/admin.php` +- `handleDashboard()` (plugin-overzicht op `/admin/dashboard`): `enabled` + wordt `true` voor protected/essential plugins via `isProtectedPlugin()`. + Eerder toonde het dashboard essential plugins als inactief als ze uit + `enabled_plugins` raakten. +- `handleContentFiles()` en `handleContentEdit()` (content-editor + plugin-multiselect): essential/protected content-plugins worden altijd + getoond in de selector (via `isProtectedPlugin()`). Eerder ontbrak + `Navigation` uit de keuzelijst als die niet in `enabled_plugins` stond, + terwijl hij effectief draaide. +- Dode functie `countEnabledPlugins()` verwijderd (nergens aangeroepen). + +### `AGENTS.md` +- Nieuwe sectie `Docblocks (verplicht)`: elke plugin, theme, class, method + en function (ook private/protected) krijgt een WordPress-stijl docblock. + Bevat het vereiste formaat met `@since`, `@param`, `@return` en de + `{ @type }` array-structuur. Controle bij elke verandering. + +## Consistentie-check (alle 17 plekken) + +Alle plekken die plugin-status bepalen zijn gecontroleerd. Na v2.6.7 leest +geen enkele plek meer de ruwe `enabled_plugins` zonder essential-forcering: + +| Plek | Voor v2.6.7 | Na v2.6.7 | +|------|-------------|-----------| +| `PluginManager::loadPlugins()` | ✅ v2.6.6 | ✅ | +| `PluginManager::isEnabled()` | ✅ | ✅ | +| `CodePressCMS` (front-end) | ✅ (via PM) | ✅ | +| `handleDashboard()` admin.php:670 | ❌ ruw | ✅ geforceerd | +| `handleContentFiles()` admin.php:1272 | ❌ ruw | ✅ geforceerd | +| `handleContentEdit()` admin.php:1973 | ❌ ruw | ✅ geforceerd | +| `handlePlugins()` admin.php:3827 | ✅ v2.6.6 | ✅ | +| `handlePluginsToggle()` admin.php:5252 | ✅ v2.6.6 | ✅ | +| `AdminPluginAPI::getEnabledPlugins()` | ❌ ruw | ✅ via PM | +| `Dashboard.php` (plugin) | via API | ✅ via gefixte API | +| guide-sidebar (admin.php:5650) | ✅ direct require | ✅ | +| `config.php` default | ✅ bevat Navigation | ✅ | +| `countEnabledPlugins()` | dode code | verwijderd | + +## Tests uitgevoerd + +- `php -l` over alle PHP-bestanden (zonder vendor): 0 syntax-errors. +- Functionele tests (`cli/test/functional/run-tests.sh` tegen Apache): + 16/16 PASS (100%). +- Pentest (`cli/test/pentest/pentest.sh`): 29/29 SAFE, 0 vulnerabilities. +- Accessibility / WCAG 2.1 AA (`cli/test/accessibility.sh`): 25/25 PASS. +- Runtime-check `AdminPluginAPI::getEnabledPlugins()` met lege + `enabled_plugins`: retourneert `Dashboard`+`Navigation` (zowel via PM + als via fallback). +- Runtime-check `handleDashboard()` simulatie met lege + `enabled_plugins`: `Dashboard`+`Navigation` tonen `enabled=true`. +- Runtime-check content-editor selector met lege `enabled_plugins`: + `Navigation` zichtbaar (essential content-plugin). + +## Upgrade-instructies + +1. Pull de nieuwe versie. +2. Geen verdere actie nodig — alle plekken tonen essential plugins nu + consistent als actief, ongeacht de staat van `enabled_plugins` in + `config.json`. \ No newline at end of file diff --git a/public/admin.php b/public/admin.php index 065083b..6b68aad 100644 --- a/public/admin.php +++ b/public/admin.php @@ -666,13 +666,15 @@ function handleDashboard($auth, $config, $twig, $user, $csrf, $siteConfig): void ]; // Build plugin overview (name => enabled status) + // Essential/protected plugins worden altijd als actief getoond, + // ongeacht enabled_plugins (consistent met PluginManager::loadPlugins()). $pluginOverview = []; $enabledPlugins = $siteConfig['enabled_plugins'] ?? []; if (is_dir($pluginsDir)) { foreach (glob($pluginsDir . '/*', GLOB_ONLYDIR) as $pluginDir) { $pluginName = basename($pluginDir); $pluginOverview[$pluginName] = [ - 'enabled' => in_array($pluginName, $enabledPlugins, true), + 'enabled' => in_array($pluginName, $enabledPlugins, true) || isProtectedPlugin($pluginName), ]; } } @@ -1264,10 +1266,12 @@ function handleContentFiles($auth, $config, $twig, $user, $csrf, $siteConfig): v $contentPlugins = getContentPlugins($pluginsDir); // Keep only enabled plugins so the selector reflects what actually runs. // enabled_plugins lives in config.json ($siteConfig), not in app.php. + // Essential/protected plugins worden altijd getoond (consistent met + // PluginManager::loadPlugins() en handlePlugins()). $enabledPlugins = $siteConfig['enabled_plugins'] ?? []; $availablePlugins = []; foreach ($contentPlugins as $p) { - if (in_array($p['name'], $enabledPlugins, true)) { + if (in_array($p['name'], $enabledPlugins, true) || isProtectedPlugin($p['name'])) { $availablePlugins[] = $p; } } @@ -1963,12 +1967,14 @@ function handleContentEdit($auth, $config, $twig, $user, $csrf, $siteConfig): vo // Get available content plugins (only enabled content-type plugins). // enabled_plugins lives in config.json ($siteConfig), not in app.php. + // Essential/protected plugins worden altijd getoond (consistent met + // PluginManager::loadPlugins() en handlePlugins()). $pluginsDir = $config['plugins_dir']; $contentPlugins = getContentPlugins($pluginsDir); $enabledPlugins = $siteConfig['enabled_plugins'] ?? []; $availablePlugins = []; foreach ($contentPlugins as $p) { - if (in_array($p['name'], $enabledPlugins, true)) { + if (in_array($p['name'], $enabledPlugins, true) || isProtectedPlugin($p['name'])) { $availablePlugins[] = $p; } } @@ -5938,22 +5944,6 @@ function countDirs(string $dir): int return $count; } -/** - * Telt het aantal ingeschakelde plugins in de site-config. - * - * @since 2.6.5 - * - * @param string $pluginsDir Plugins-directory (ongebruikt, voor compatibiliteit). - * @param string $configJson Pad naar config.json. - * @return int Aantal ingeschakelde plugins. - */ -function countEnabledPlugins(string $pluginsDir, string $configJson): int -{ - $config = file_exists($configJson) ? json_decode(file_get_contents($configJson), true) : []; - $enabled = $config['enabled_plugins'] ?? []; - return count($enabled); -} - /** * Berekent recursief de totale bestandsgrootte (in bytes) van een directory. * diff --git a/version.php b/version.php index 6b26da7..5cc3c7e 100644 --- a/version.php +++ b/version.php @@ -15,7 +15,7 @@ */ return [ - 'version' => '2.6.6', + 'version' => '2.6.7', 'release_date' => '2026-08-27', 'codename' => 'Lyra', 'status' => 'stable',