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.
360 lines
11 KiB
Markdown
360 lines
11 KiB
Markdown
# 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
|
|
|
|
```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:
|
|
|
|
```json
|
|
{
|
|
"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
|
|
<?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`.
|
|
|
|
```php
|
|
// 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
|
|
<?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:
|
|
|
|
1. **Geselecteerde taal** — `language/<geselecteerd>/admin.php` (of `site.php`)
|
|
2. **Plugin default_language** — `plugin.json` `default_language` (bijv. `nl`)
|
|
3. **CMS site default** — `config.language.default`
|
|
4. **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 in `language/<lang>/admin.php`.
|
|
- **Content plugins** (`type: "content"`) volgen de geselecteerde **content taal** (uit de URL of `config.language.default`). Vertalingen staan in `language/<lang>/site.php`.
|
|
|
|
### Vertalingen ophalen in een plugin
|
|
|
|
```php
|
|
// 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:
|
|
|
|
```php
|
|
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()`:
|
|
|
|
```php
|
|
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`:
|
|
|
|
```json
|
|
{
|
|
"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 pagina
|
|
- `onBeforeRender` - Voor het renderen
|
|
- `onAfterRender` - Na het renderen
|
|
- `onSearch` - Bij zoeken
|
|
- `onMenuBuild` - Bij opbouwen menu
|
|
|
|
**Filters** (return aangepaste waarde):
|
|
|
|
- `onContentFilter` - Content filteren
|
|
- `onTitleFilter` - Titel filteren
|
|
- `onMenuFilter` - Menu filteren
|
|
|
|
## Admin integratie
|
|
|
|
Plugins kunnen eigen admin-pagina's toevoegen via `getAdminMenu()` en `handleAdminRoute()`:
|
|
|
|
```php
|
|
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`):
|
|
|
|
```php
|
|
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:
|
|
|
|
```php
|
|
$config = $this->pluginManager->getPluginConfig('MijnPlugin');
|
|
```
|
|
|
|
## CSS toevoegen
|
|
|
|
Plugins kunnen een CSS-URL leveren via `getCssUrl()`:
|
|
|
|
```php
|
|
public function getCssUrl(): string
|
|
{
|
|
return '/plugins/MijnPlugin/assets/css/style.css';
|
|
}
|
|
```
|
|
|
|
De URL wordt doorgegeven aan de front-end template via `plugin_css_urls`. |