3 Commits
Author SHA1 Message Date
E.Noorlander f7e8837a1c v2.6.6 (Lyra): Essential plugins altijd laden (forceer in core)
Bug: beschermde/essentiële 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.
2026-08-27 19:43:42 +00:00
E.Noorlander 0e345c96d7 Fix: content/.gitkeep uit git tracking halen (content/ is gitignored, blokkeerde reset op read-only omgevingen) 2026-08-27 09:18:18 +00:00
E.Noorlander 34bb8c6f21 Fix: gitignore runtime-bestand login_attempts.json (botste bij merge) 2026-08-27 09:12:12 +00:00
10 changed files with 167 additions and 23 deletions
+1
View File
@@ -14,6 +14,7 @@ Thumbs.db
admin/storage/cache/ admin/storage/cache/
admin/storage/geoip/ admin/storage/geoip/
admin/storage/stats.json admin/storage/stats.json
admin/storage/logs/login_attempts.json
var/ var/
# Runtime-compiled theme assets (generated by scssphp, read-only) # Runtime-compiled theme assets (generated by scssphp, read-only)
+5
View File
@@ -37,6 +37,11 @@
- [ ] AGENTS.md + config.json.example bijwerken - [ ] AGENTS.md + config.json.example bijwerken
- [ ] Verificatie: php -l, curl met Host-header, domein-switch in admin testen - [ ] Verificatie: php -l, curl met Host-header, domein-switch in admin testen
## 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)
- [x] Verslag gemaakt (docs/release-notes/v2.6.6.md)
## v2.6.5 (2026-08-27) ✅ ## v2.6.5 (2026-08-27) ✅
- [x] Bug: dashboard toonde 0 content (AdminPluginAPI::getContentDir() gaf relatief pad "content" terug zonder normalisatie tegen project-root; Apache CWD=public/ → 0 bestanden) - [x] Bug: dashboard toonde 0 content (AdminPluginAPI::getContentDir() gaf relatief pad "content" terug zonder normalisatie tegen project-root; Apache CWD=public/ → 0 bestanden)
- [x] Dynamische pad-resolutie: PluginAPIInterface uitgebreid met getProjectRoot()/getContentDir()/getPluginsDir()/getVersionInfo(); CMSAPI en AdminPluginAPI implementeren deze nu universeel; Navigation plugin en Logs plugin halen paden via de API i.p.v. hardcoded dirname(__DIR__) - [x] Dynamische pad-resolutie: PluginAPIInterface uitgebreid met getProjectRoot()/getContentDir()/getPluginsDir()/getVersionInfo(); CMSAPI en AdminPluginAPI implementeren deze nu universeel; Navigation plugin en Logs plugin halen paden via de API i.p.v. hardcoded dirname(__DIR__)
-1
View File
@@ -1 +0,0 @@
{"admi":{"count":1,"last_attempt":1771257322},"":{"count":4,"last_attempt":1787141227}}
+34 -5
View File
@@ -122,11 +122,34 @@ class PluginManager
} }
} }
/**
* Controleer of een plugin als 'essential' is gemarkeerd in zijn plugin.json.
*
* Essential plugins worden altijd geladen, ongeacht de enabled_plugins-lijst,
* en kunnen niet worden uitgeschakeld, bewerkt of verwijderd via de admin.
*
* @since 2.6.6
*
* @param string $pluginName Plugin-naam (directorynaam).
* @return bool True indien de plugin in plugin.json essential: true heeft.
*/
public function isEssentialPlugin(string $pluginName): bool
{
$pluginJsonFile = $this->pluginsPath . '/' . $pluginName . '/plugin.json';
if (!file_exists($pluginJsonFile)) {
return false;
}
$pluginJson = json_decode(file_get_contents($pluginJsonFile), true);
return is_array($pluginJson) && ($pluginJson['essential'] ?? false) === true;
}
/** /**
* Laad alle ingeschakelde plugins uit de pluginsdirectory. * Laad alle ingeschakelde plugins uit de pluginsdirectory.
* *
* Per plugin wordt het hoofdbestand (<naam>.php) geïncludeerd, de plugin-class * Essential plugins (plugin.json `essential: true`) worden altijd geladen,
* geïnstantieerd, en action- en filter-hooks automatisch geregistreerd. * ook als ze niet in enabled_plugins staan. Per plugin wordt het hoofdbestand
* (<naam>.php) geïncludeerd, de plugin-class geïnstantieerd, en action- en
* filter-hooks automatisch geregistreerd.
* *
* @since 2.6.5 * @since 2.6.5
* *
@@ -139,13 +162,19 @@ class PluginManager
} }
$pluginDirs = glob($this->pluginsPath . '/*', GLOB_ONLYDIR); $pluginDirs = glob($this->pluginsPath . '/*', GLOB_ONLYDIR);
foreach ($pluginDirs as $pluginDir) { foreach ($pluginDirs as $pluginDir) {
$pluginName = basename($pluginDir); $pluginName = basename($pluginDir);
if (!in_array($pluginName, $this->enabledPlugins, true)) { // Essential plugins are always loaded, regardless of enabled_plugins.
$isEnabled = in_array($pluginName, $this->enabledPlugins, true);
if (!$isEnabled && !$this->isEssentialPlugin($pluginName)) {
continue; continue;
} }
// Keep the effective enabled list consistent for getEnabledPlugins()/isEnabled().
if (!$isEnabled) {
$this->enabledPlugins[] = $pluginName;
}
$pluginFile = $pluginDir . '/' . $pluginName . '.php'; $pluginFile = $pluginDir . '/' . $pluginName . '.php';
View File
+81
View File
@@ -0,0 +1,81 @@
# v2.6.6 (Lyra) — Essential-plugins altijd laden
Releasedatum: 2026-08-27
Codename: Lyra
Status: stable
## Samenvatting
Bug-fix: beschermde en essentiële plugins (`Navigation`, `Dashboard`)
werden op de live site als "Inactief" getoond en konden niet meer
aangezet worden via de admin-interface, terwijl ze verplichte
functionaliteit leveren (sidebar-navigatie voor handleidingen, dashboard).
Oorzaak: zodra zo'n plugin uit `enabled_plugins` in `config.json` raakte
(bijv. door een merge of handmatige wijziging), blokkeerde de admin-code
elke toggle en toonde de Twig-template alleen een schild-icoon — geen
"Activeren"-knop. De core laadde ze vervolgens ook niet meer.
Oplossing: de core **forceert** het laden van essential plugins
(plugin.json `essential: true`) ongeacht de `enabled_plugins`-lijst.
De admin-UI toont ze altijd als "Actief" en de toggle-handler staat
aanzetten wél toe (uitzetten blijft geblokkeerd).
## Wijzigingen
### `cms/core/plugin/PluginManager.php`
- Nieuwe methode `isEssentialPlugin(string $pluginName): bool` — leest
`plugin.json` `essential: true`.
- `loadPlugins()` laadt essential plugins **altijd**, ook als ze niet in
`enabled_plugins` staan. De interne lijst wordt consistent gehouden
voor `getEnabledPlugins()` / `isEnabled()`.
### `public/admin.php`
- Nieuwe helper `isEssentialPluginByName(string $pluginName): bool`
pad-onafhankelijke check op `plugin.json` `essential: true`.
- `isProtectedPlugin()` dekt nu ook essential plugins (naast de
hardcoded `getProtectedPlugins()`-lijst). Alle bestaande call-sites
(delete, bewerken, file-upload, file-delete, file-move, dir-create,
dir-rename, dir-delete, config-move) profiteren automatisch.
- `handlePlugins()`: `enabled` wordt `true` voor protected/essential
plugins, zodat de UI altijd "Actief" toont.
- `handlePluginsToggle()`: aanzetten van protected/essential plugins is
weer mogelijk; alleen uitzetten blijft geblokkeerd met een admin-log.
### `guide/nl/codepress-developer/plugin-development.md`
### `guide/en/codepress-developer/plugin-development.md`
- Beschrijving van `essential`-veld bijgewerkt: "altijd geladen" +
"kan niet worden uitgeschakeld/bewerkt/verwijderd".
## Getroffen plugins
| Plugin | `essential` in plugin.json | Hardcoded protected | Gedrag na fix |
|----------|----------------------------|---------------------|---------------|
| `Dashboard` | ja | nee | Altijd geladen, niet uit te schakelen |
| `Navigation` | ja | ja (redundant) | Altijd geladen, niet uit te schakelen |
| `HTMLBlock` | nee | nee | Normaal (toggle werkt) |
| `Statistics` | nee | nee | Normaal (toggle werkt) |
| `Logs` | nee | nee | Normaal (toggle werkt) |
| `GeoIPInfo` | nee | nee | Normaal (toggle werkt) |
## 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 `PluginManager` met lege `enabled_plugins`: alleen
`Dashboard` + `Navigation` worden geladen (essential). Met
`["HTMLBlock"]`: `Dashboard` + `Navigation` + `HTMLBlock`.
- Admin-UI simulatie met lege `enabled_plugins`: `Dashboard` en
`Navigation` tonen `enabled=true` (vroeger `false`).
## Upgrade-instructies
1. Pull de nieuwe versie.
2. Geen verdere actie nodig — essential plugins staan vanaf nu altijd aan.
3. Als je `config.json` handmatig hebt aangepast en `Navigation` of
`Dashboard` uit `enabled_plugins` hebt gehaald: je hoeft dit niet meer
terug te draaien; de core laadt ze toch. Ze verschijnen weer als
"Actief" in de admin-UI.
@@ -47,7 +47,7 @@ plugins/MyPlugin/
| `author` | string | Author | | `author` | string | Author |
| `description` | string | Short description | | `description` | string | Short description |
| `type` | `"system"` or `"content"` | System (blue badge) or content (green badge) | | `type` | `"system"` or `"content"` | System (blue badge) or content (green badge) |
| `essential` | boolean | Essential plugins cannot be edited/deleted | | `essential` | boolean | Essential plugins are always loaded (regardless of `enabled_plugins`) and cannot be disabled, edited or deleted |
| `hasConfig` | boolean | Shows a Config button in admin | | `hasConfig` | boolean | Shows a Config button in admin |
| `default_language` | string | Fallback language for plugin translations (e.g. `nl`) | | `default_language` | string | Fallback language for plugin translations (e.g. `nl`) |
| `settings` | array | Settings schema (see below) | | `settings` | array | Settings schema (see below) |
@@ -47,7 +47,7 @@ plugins/MijnPlugin/
| `author` | string | Auteur | | `author` | string | Auteur |
| `description` | string | Korte beschrijving | | `description` | string | Korte beschrijving |
| `type` | `"system"` of `"content"` | Systeem (blauwe badge) of content (groene badge) | | `type` | `"system"` of `"content"` | Systeem (blauwe badge) of content (groene badge) |
| `essential` | boolean | Essentiële plugins kunnen niet worden bewerkt/verwijderd | | `essential` | boolean | Essentiële plugins worden altijd geladen (ongeacht `enabled_plugins`) en kunnen niet worden uitgeschakeld, bewerkt of verwijderd |
| `hasConfig` | boolean | Toont een Config-knop in admin | | `hasConfig` | boolean | Toont een Config-knop in admin |
| `default_language` | string | Fallback taal voor plugin-vertalingen (bijv. `nl`) | | `default_language` | string | Fallback taal voor plugin-vertalingen (bijv. `nl`) |
| `settings` | array | Instellingen-schema (zie hieronder) | | `settings` | array | Instellingen-schema (zie hieronder) |
+42 -13
View File
@@ -179,7 +179,7 @@ function getSidebarColor($config) {
* *
* @since 2.6.5 * @since 2.6.5
* *
* @return array<int,string> Namen van beschermde plugins (bijv. ['Navigation']). * @return array<int,string> Namen van beschermd geworden plugins (bijv. ['Navigation']).
*/ */
// Essential plugins that cannot be disabled or deleted // Essential plugins that cannot be disabled or deleted
function getProtectedPlugins(): array { function getProtectedPlugins(): array {
@@ -187,7 +187,34 @@ function getProtectedPlugins(): array {
} }
/** /**
* Controleert of een pluginnaam op de lijst van beschermde plugins staat. * Controleert of een plugin in zijn plugin.json `essential: true` heeft.
*
* Essential plugins worden altijd geladen en kunnen niet worden uitgeschakeld,
* bewerkt of verwijderd via de admin. De check is pad-onafhankelijk en leest
* de plugin.json uit de standaard plugins-directory.
*
* @since 2.6.6
*
* @param string $pluginName Naam van de plugin om te controleren.
* @return bool True wanneer de plugin essential is, anders false.
*/
function isEssentialPluginByName(string $pluginName): bool {
if ($pluginName === '') {
return false;
}
$pluginJsonFile = __DIR__ . '/../plugins/' . $pluginName . '/plugin.json';
if (!file_exists($pluginJsonFile)) {
return false;
}
$data = json_decode(file_get_contents($pluginJsonFile), true);
return is_array($data) && ($data['essential'] ?? false) === true;
}
/**
* Controleert of een plugin beschermd is (niet uit te schakelen/verwijderen).
*
* Een plugin is beschermd wanneer deze op de hardcoded lijst staat
* (getProtectedPlugins()) OF in zijn plugin.json `essential: true` heeft.
* *
* @since 2.6.5 * @since 2.6.5
* *
@@ -195,7 +222,7 @@ function getProtectedPlugins(): array {
* @return bool True wanneer de plugin beschermd is, anders false. * @return bool True wanneer de plugin beschermd is, anders false.
*/ */
function isProtectedPlugin(string $pluginName): bool { function isProtectedPlugin(string $pluginName): bool {
return in_array($pluginName, getProtectedPlugins(), true); return in_array($pluginName, getProtectedPlugins(), true) || isEssentialPluginByName($pluginName);
} }
/** /**
@@ -3795,7 +3822,7 @@ function handlePlugins($auth, $config, $twig, $user, $csrf): void
$pluginData = [ $pluginData = [
'name' => $pluginName, 'name' => $pluginName,
'enabled' => in_array($pluginName, $enabledPlugins), 'enabled' => in_array($pluginName, $enabledPlugins) || isProtectedPlugin($pluginName),
'protected' => isProtectedPlugin($pluginName), 'protected' => isProtectedPlugin($pluginName),
'type' => 'content', 'type' => 'content',
]; ];
@@ -5216,19 +5243,21 @@ function handlePluginsToggle($auth, $config): void
$plugin = $_POST['plugin'] ?? ''; $plugin = $_POST['plugin'] ?? '';
$plugin = preg_replace('/[^a-zA-Z0-9_-]/', '', $plugin); $plugin = preg_replace('/[^a-zA-Z0-9_-]/', '', $plugin);
// Block toggling protected plugins $configFile = $config['config_json'];
if (isProtectedPlugin($plugin)) { $siteConfig = file_exists($configFile) ? json_decode(file_get_contents($configFile), true) : [];
$enabledPlugins = $siteConfig['enabled_plugins'] ?? [];
$currentlyEnabled = in_array($plugin, $enabledPlugins, true);
// Protected/essential plugins mogen niet uitgeschakeld worden (maar wel
// aangezet worden als ze per ongeluk uit enabled_plugins zijn geraakt).
if (isProtectedPlugin($plugin) && $currentlyEnabled) {
adminLog($config, 'warning', $user['username'] . ' probeerde essentiële plugin ' . $plugin . ' te deactiveren'); adminLog($config, 'warning', $user['username'] . ' probeerde essentiële plugin ' . $plugin . ' te deactiveren');
header('Location: /admin/plugins'); header('Location: /admin/plugins');
exit; exit;
} }
$configFile = $config['config_json']; if ($currentlyEnabled) {
$siteConfig = file_exists($configFile) ? json_decode(file_get_contents($configFile), true) : [];
$enabledPlugins = $siteConfig['enabled_plugins'] ?? [];
if (in_array($plugin, $enabledPlugins)) {
$enabledPlugins = array_diff($enabledPlugins, [$plugin]); $enabledPlugins = array_diff($enabledPlugins, [$plugin]);
adminLog($config, 'info', $user['username'] . ' deactiveerde plugin ' . $plugin); adminLog($config, 'info', $user['username'] . ' deactiveerde plugin ' . $plugin);
} else { } else {
+2 -2
View File
@@ -15,8 +15,8 @@
*/ */
return [ return [
'version' => '2.6.5', 'version' => '2.6.6',
'release_date' => '2026-08-26', 'release_date' => '2026-08-27',
'codename' => 'Lyra', 'codename' => 'Lyra',
'status' => 'stable', 'status' => 'stable',