Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d039114f21 | ||
|
|
f7e8837a1c | ||
|
|
0e345c96d7 | ||
|
|
34bb8c6f21 |
@@ -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)
|
||||||
|
|||||||
@@ -37,6 +37,19 @@
|
|||||||
- [ ] 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.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)
|
||||||
|
- [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 +0,0 @@
|
|||||||
{"admi":{"count":1,"last_attempt":1771257322},"":{"count":4,"last_attempt":1787141227}}
|
|
||||||
@@ -141,13 +141,45 @@ class AdminPluginAPI implements PluginAPIInterface
|
|||||||
/**
|
/**
|
||||||
* Haal de lijst met ingeschakelde plugins op.
|
* 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
|
* @since 2.6.5
|
||||||
*
|
*
|
||||||
* @return array<int,string> Lijst met ingeschakelde plugin-namen.
|
* @return array<int,string> Lijst met ingeschakelde plugin-namen.
|
||||||
*/
|
*/
|
||||||
public function getEnabledPlugins(): array
|
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);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -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';
|
||||||
|
|
||||||
|
|||||||
@@ -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.
|
||||||
@@ -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`.
|
||||||
@@ -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) |
|
||||||
|
|||||||
+51
-32
@@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -639,13 +666,15 @@ function handleDashboard($auth, $config, $twig, $user, $csrf, $siteConfig): void
|
|||||||
];
|
];
|
||||||
|
|
||||||
// Build plugin overview (name => enabled status)
|
// Build plugin overview (name => enabled status)
|
||||||
|
// Essential/protected plugins worden altijd als actief getoond,
|
||||||
|
// ongeacht enabled_plugins (consistent met PluginManager::loadPlugins()).
|
||||||
$pluginOverview = [];
|
$pluginOverview = [];
|
||||||
$enabledPlugins = $siteConfig['enabled_plugins'] ?? [];
|
$enabledPlugins = $siteConfig['enabled_plugins'] ?? [];
|
||||||
if (is_dir($pluginsDir)) {
|
if (is_dir($pluginsDir)) {
|
||||||
foreach (glob($pluginsDir . '/*', GLOB_ONLYDIR) as $pluginDir) {
|
foreach (glob($pluginsDir . '/*', GLOB_ONLYDIR) as $pluginDir) {
|
||||||
$pluginName = basename($pluginDir);
|
$pluginName = basename($pluginDir);
|
||||||
$pluginOverview[$pluginName] = [
|
$pluginOverview[$pluginName] = [
|
||||||
'enabled' => in_array($pluginName, $enabledPlugins, true),
|
'enabled' => in_array($pluginName, $enabledPlugins, true) || isProtectedPlugin($pluginName),
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1237,10 +1266,12 @@ function handleContentFiles($auth, $config, $twig, $user, $csrf, $siteConfig): v
|
|||||||
$contentPlugins = getContentPlugins($pluginsDir);
|
$contentPlugins = getContentPlugins($pluginsDir);
|
||||||
// Keep only enabled plugins so the selector reflects what actually runs.
|
// Keep only enabled plugins so the selector reflects what actually runs.
|
||||||
// enabled_plugins lives in config.json ($siteConfig), not in app.php.
|
// 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'] ?? [];
|
$enabledPlugins = $siteConfig['enabled_plugins'] ?? [];
|
||||||
$availablePlugins = [];
|
$availablePlugins = [];
|
||||||
foreach ($contentPlugins as $p) {
|
foreach ($contentPlugins as $p) {
|
||||||
if (in_array($p['name'], $enabledPlugins, true)) {
|
if (in_array($p['name'], $enabledPlugins, true) || isProtectedPlugin($p['name'])) {
|
||||||
$availablePlugins[] = $p;
|
$availablePlugins[] = $p;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1936,12 +1967,14 @@ function handleContentEdit($auth, $config, $twig, $user, $csrf, $siteConfig): vo
|
|||||||
|
|
||||||
// Get available content plugins (only enabled content-type plugins).
|
// Get available content plugins (only enabled content-type plugins).
|
||||||
// enabled_plugins lives in config.json ($siteConfig), not in app.php.
|
// 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'];
|
$pluginsDir = $config['plugins_dir'];
|
||||||
$contentPlugins = getContentPlugins($pluginsDir);
|
$contentPlugins = getContentPlugins($pluginsDir);
|
||||||
$enabledPlugins = $siteConfig['enabled_plugins'] ?? [];
|
$enabledPlugins = $siteConfig['enabled_plugins'] ?? [];
|
||||||
$availablePlugins = [];
|
$availablePlugins = [];
|
||||||
foreach ($contentPlugins as $p) {
|
foreach ($contentPlugins as $p) {
|
||||||
if (in_array($p['name'], $enabledPlugins, true)) {
|
if (in_array($p['name'], $enabledPlugins, true) || isProtectedPlugin($p['name'])) {
|
||||||
$availablePlugins[] = $p;
|
$availablePlugins[] = $p;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -3795,7 +3828,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 +5249,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 {
|
||||||
@@ -5909,22 +5944,6 @@ function countDirs(string $dir): int
|
|||||||
return $count;
|
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.
|
* Berekent recursief de totale bestandsgrootte (in bytes) van een directory.
|
||||||
*
|
*
|
||||||
|
|||||||
+2
-2
@@ -15,8 +15,8 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
return [
|
return [
|
||||||
'version' => '2.6.5',
|
'version' => '2.6.7',
|
||||||
'release_date' => '2026-08-26',
|
'release_date' => '2026-08-27',
|
||||||
'codename' => 'Lyra',
|
'codename' => 'Lyra',
|
||||||
'status' => 'stable',
|
'status' => 'stable',
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user