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.
11 KiB
Plugin Development
Plugin structuur
Elke plugin heeft zijn eigen map onder plugins/. De mapnaam moet overeenkomen met de hoofd plugin class naam.
plugins/MijnPlugin/
├── MijnPlugin.php # Hoofd plugin class (naam = mapnaam)
├── plugin.json # Plugin metadata + instellingen-schema
├── README.md # Plugin documentatie
├── config.json # Optionele runtime configuratie (overschrijft defaults)
├── assets/ # Optionele CSS/JS/SCSS
│ ├── css/
│ └── scss/
└── language/ # Vertalingen
├── nl/
│ ├── admin.php # Admin labels (systeem plugins)
│ └── site.php # Front-end labels (content plugins)
└── en/
├── admin.php
└── site.php
plugin.json
{
"name": "Mijn Plugin",
"version": "1.0.0",
"author": "Jouw Naam",
"description": "Beschrijving",
"type": "content",
"essential": false,
"hasConfig": false,
"default_language": "nl",
"settings": []
}
Velden
| Veld | Waarde | Beschrijving |
|---|---|---|
name |
string | Weergavenaam in admin |
version |
string | Versienummer |
author |
string | Auteur |
description |
string | Korte beschrijving |
type |
"system" of "content" |
Systeem (blauwe badge) of content (groene badge) |
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 |
default_language |
string | Fallback taal voor plugin-vertalingen (bijv. nl) |
settings |
array | Instellingen-schema (zie hieronder) |
Instellingen-schema
Als hasConfig: true, definieer je instellingen in het settings array:
{
"settings": [
{
"key": "max_items",
"type": "number",
"default": 10,
"label_key": "setting_max_items",
"help_key": "setting_max_items_help"
},
{
"key": "required_roles",
"type": "multi-select",
"default": ["admin"],
"options": {
"admin": "Admin",
"content-manager": "Content Beheerder"
},
"label_key": "setting_required_roles",
"help_key": "setting_required_roles_help",
"option_label_key": "role_options"
}
]
}
Per-instelling velden
| Veld | Beschrijving |
|---|---|
key |
Instelling-sleutel (opgeslagen in config.json) |
type |
text, checkbox, number, select, multi-select |
default |
Standaardwaarde |
label |
Hardcoded label (fallback als geen label_key of als vertaling ontbreekt) |
help |
Hardcoded help-tekst (fallback als geen help_key) |
label_key |
Sleutel in plugin language/<lang>/admin.php voor vertaald label |
help_key |
Sleutel in plugin language/<lang>/admin.php voor vertaalde help-tekst |
option_label_key |
Sleutel naar een array met vertaalde optie-labels voor select/multi-select |
options |
Opties voor select/multi-select ({waarde: label}) |
De admin laadt defaults uit plugin.json en overschrijft ze met waarden uit config.json. De plugin leest de uiteindelijke waarden via PluginManager::getPluginConfig() of een eigen getPluginConfig() methode.
Plugin class voorbeeld
<?php
class MijnPlugin
{
private ?PluginAPIInterface $api = null;
public function setAPI(PluginAPIInterface $api): void
{
$this->api = $api;
}
public function getConfig(): array
{
return [
'title' => 'Mijn Plugin',
'type' => 'content',
'viewable' => true,
];
}
public function getSidebarContent(): string
{
// Plugin vertalingen ophalen (content plugin = front-end taal)
$t = $this->api ? $this->api->getPluginTranslations('MijnPlugin') : [];
$title = $this->api ? $this->api->getCurrentPageTitle() : '';
$label = $t['current_page'] ?? 'Huidige pagina';
return '<p>' . htmlspecialchars($label) . ': ' . htmlspecialchars($title) . '</p>';
}
}
De plugin class wordt automatisch geladen door PluginManager als de plugin in enabled_plugins staat in config.json.
API gebruiken
De API wordt geïnjecteerd via setAPI(). In de front-end context is dit een CMSAPI instance, in de admin context een AdminPluginAPI instance. Beide implementeren PluginAPIInterface.
// Front-end API (CMSAPI) - voor content plugins
$this->api->getCurrentPageTitle();
$this->api->getMenu();
$this->api->getConfig('site_title');
$this->api->getCurrentLanguage();
$this->api->isHomepage();
$this->api->createUrl('over-ons');
$this->api->getPluginTranslations('MijnPlugin'); // front-end taal
$this->api->t('current_page', 'MijnPlugin'); // vertaalhelper
// Admin API (AdminPluginAPI) - voor systeem plugins
$this->api->getConfig('analytics.enabled');
$this->api->getContentDir();
$this->api->getEnabledPlugins();
$this->api->getAdminLanguage(); // actieve admin taal
$this->api->getPluginTranslations('MijnPlugin'); // admin taal
$this->api->t('menu_label', 'MijnPlugin'); // vertaalhelper
Taal support (i18n)
Elke plugin kan vertalingen leveren in language/<lang>/. Systeem plugins gebruiken admin.php, content plugins gebruiken site.php.
Taalbestand formaat
language/nl/admin.php:
<?php
return [
// Menu
'menu_label' => 'Mijn Plugin',
// Pagina
'page_title' => 'Mijn Plugin',
'current_page' => 'Huidige pagina',
// Instellingen (label_key/help_key verwijzen hiernaar)
'setting_max_items' => 'Maximaal aantal items',
'setting_max_items_help' => 'Aantal items dat getoond wordt.',
'role_options' => [
'admin' => 'Admin',
'content-manager' => 'Content Beheerder',
],
];
Fallback chain
Plugin-vertalingen worden opgelost in deze volgorde:
- Geselecteerde taal —
language/<geselecteerd>/admin.php(ofsite.php) - Plugin default_language —
plugin.jsondefault_language(bijv.nl) - CMS site default —
config.language.default - Lege array — de sleutel wordt ongewijzigd getoond
Systeem vs content plugins
- Systeem plugins (
type: "system") volgen de geselecteerde admin taal (config.admin_language). Vertalingen staan inlanguage/<lang>/admin.php. - Content plugins (
type: "content") volgen de geselecteerde content taal (uit de URL ofconfig.language.default). Vertalingen staan inlanguage/<lang>/site.php.
Vertalingen ophalen in een plugin
// In handleAdminRoute() of getSidebarContent():
$t = $this->api->getPluginTranslations('MijnPlugin');
$tr = function (string $key) use ($t): string {
return $t[$key] ?? $key;
};
echo htmlspecialchars($tr('page_title'));
Of gebruik de enkele-sleutel helper:
echo htmlspecialchars($this->api->t('page_title', 'MijnPlugin'));
Menu label vertalen
In getAdminMenu(), voeg label_key toe. De admin sidebar toont de vertaling via plugin_menu_label():
public function getAdminMenu(): array
{
return [
[
'plugin' => 'MijnPlugin',
'route' => 'mijn-plugin',
'label' => 'Mijn Plugin', // fallback
'label_key' => 'menu_label', // verwijst naar language/<lang>/admin.php
'icon' => 'bi-puzzle',
'section' => 'general', // of 'system'
'permission' => 'mijn-plugin',
],
];
}
Instellingen-labels vertalen
In plugin.json settings, gebruik label_key/help_key/option_label_key in plaats van hardcoded label/help:
{
"key": "max_items",
"label_key": "setting_max_items",
"help_key": "setting_max_items_help",
"type": "number",
"default": 10
}
De admin handlePluginsConfig() lost deze op via de plugin-vertalingen; als een vertaling ontbreekt valt het terug op label/help uit plugin.json.
Hooks
Plugins kunnen de volgende methodes implementeren voor automatische hook-registratie:
Actions (geen return waarde):
onPageLoad- Bij laden van een paginaonBeforeRender- Voor het renderenonAfterRender- Na het renderenonSearch- Bij zoekenonMenuBuild- Bij opbouwen menu
Filters (return aangepaste waarde):
onContentFilter- Content filterenonTitleFilter- Titel filterenonMenuFilter- Menu filteren
Admin integratie
Plugins kunnen eigen admin-pagina's toevoegen via getAdminMenu() en handleAdminRoute():
public function getAdminMenu(): array
{
$config = $this->getPluginConfig();
$requiredRoles = $config['required_roles'] ?? ['admin'];
return [
[
'plugin' => 'MijnPlugin',
'route' => 'mijn-plugin',
'label' => 'Mijn Plugin',
'label_key' => 'menu_label',
'icon' => 'bi-puzzle',
'section' => 'general', // of 'system'
'permission' => 'mijn-plugin',
'required_roles' => $requiredRoles,
],
];
}
public function handleAdminRoute(string $action): ?string
{
$t = $this->api ? $this->api->getPluginTranslations('MijnPlugin') : [];
$tr = function (string $key) use ($t): string {
return $t[$key] ?? $key;
};
return '<h2>' . htmlspecialchars($tr('page_title')) . '</h2>';
}
Alleen plugins die in enabled_plugins staan worden in de admin sidebar getoond.
Runtime config in een plugin
Plugins kunnen hun runtime config ophalen (defaults uit plugin.json settings + overrides uit config.json):
private function getPluginConfig(): array
{
$pluginDir = dirname(__DIR__);
$pluginJsonFile = $pluginDir . '/plugin.json';
$configJsonFile = $pluginDir . '/config.json';
$defaults = [];
if (file_exists($pluginJsonFile)) {
$pluginJson = json_decode(file_get_contents($pluginJsonFile), true) ?? [];
foreach ($pluginJson['settings'] ?? [] as $setting) {
if (isset($setting['key'])) {
$defaults[$setting['key']] = $setting['default'] ?? null;
}
}
}
$overrides = [];
if (file_exists($configJsonFile)) {
$overrides = json_decode(file_get_contents($configJsonFile), true) ?? [];
}
return array_merge($defaults, $overrides);
}
Of via de gedeelde methode op PluginManager:
$config = $this->pluginManager->getPluginConfig('MijnPlugin');
CSS toevoegen
Plugins kunnen een CSS-URL leveren via getCssUrl():
public function getCssUrl(): string
{
return '/plugins/MijnPlugin/assets/css/style.css';
}
De URL wordt doorgegeven aan de front-end template via plugin_css_urls.