v2.6.1d (Lyra): Plugin i18n, plugin editor vernieuwd, media invoegen
Plugin internationalisatie: plugins hebben eigen language/ mappen. Systeem plugins volgen admin taal (admin.php), content plugins volgen content taal (site.php). Fallback chain: geselecteerd -> plugin default_language -> CMS default. PluginManager/AdminPluginAPI/CMSAPI uitgebreid met getPluginTranslations()/t(). plugin.json settings ondersteunen label_key/help_key/option_label_key. Plugin uniformiteit: alle 6 plugins hebben uniforme structuur (README.md, assets/.gitkeep, language/nl|en/). plugins/README.md herschreven. guide plugin-development.md (NL+EN) volledig herschreven. Plugin editor vernieuwd: geneste bestandsbrowser zijbalk, nieuw bestand aanmaken, uploaden naar assets/, verwijderen en verplaatsen. Nieuwe routes: plugins-file-upload, plugins-file-delete, plugins-file-move. Path-traversal bescherming + protected plugins geblokkeerd. Media invoegen in editor: nieuw /admin/media-list JSON endpoint + herbruikbare _media-modal.twig include. Plugin-context scant assets/ map. editor-toolbar.js modeMap uitgebreid voor css/scss/js/json. Plugin overzicht knoppen: alleen iconen met title/aria-label. Tests: pentest 30/30, WCAG 2.1 AA 25/25.
This commit is contained in:
@@ -4,7 +4,7 @@
|
||||
codepress/
|
||||
├── cms/core/ # Core engine
|
||||
│ ├── class/ # CodePressCMS, ThemeManager, Logger, Analytics
|
||||
│ ├── plugin/ # PluginManager, CMSAPI, AdminPluginAPI
|
||||
│ ├── plugin/ # PluginManager, PluginAPIInterface, CMSAPI, AdminPluginAPI
|
||||
│ ├── config.php # Config loader
|
||||
│ └── index.php # Bootstrap
|
||||
├── admin/ # Admin console
|
||||
@@ -13,12 +13,22 @@ codepress/
|
||||
│ └── theme/default/views/ # Twig templates
|
||||
├── themes/ # Themes (default, ...)
|
||||
├── plugins/ # Plugins (Dashboard, HTMLBlock, Navigation, ...)
|
||||
│ └── <Plugin>/ # Each plugin has:
|
||||
│ ├── <Plugin>.php # Main class
|
||||
│ ├── plugin.json # Metadata + settings
|
||||
│ ├── README.md # Documentation
|
||||
│ ├── assets/ # CSS/JS/SCSS
|
||||
│ └── language/ # Plugin translations (nl/, en/, ...; admin.php/site.php)
|
||||
├── content/ # Content files (.md, .php, .html)
|
||||
├── language/ # Language files (nl/, en/, de/)
|
||||
├── language/ # Core CMS language files (nl/, en/, de/; site.php + admin.php)
|
||||
├── guide/ # Guides (nl/, en/)
|
||||
├── public/ # Web root
|
||||
│ ├── index.php # Website entry point
|
||||
│ └── admin.php # Admin entry point + router
|
||||
├── config.json # Site configuration
|
||||
└── version.php # Version info
|
||||
```
|
||||
```
|
||||
|
||||
## Plugin i18n
|
||||
|
||||
Plugins have their own `language/` directory with translations. System plugins follow the admin language (`language/<lang>/admin.php`), content plugins follow the content language (`language/<lang>/site.php`). Fallback chain: selected language → plugin `default_language` → CMS site default → empty array. See `guide/en/codepress-developer/plugin-development.md` for details.
|
||||
@@ -2,14 +2,24 @@
|
||||
|
||||
## Plugin structure
|
||||
|
||||
Each plugin has its own folder under `plugins/`. The folder name must match the main plugin class name.
|
||||
|
||||
```
|
||||
plugins/MyPlugin/
|
||||
├── MyPlugin.php # Main plugin class (name = folder name)
|
||||
├── plugin.json # Plugin metadata
|
||||
├── config.json # Optional configuration
|
||||
└── assets/ # Optional CSS/JS
|
||||
├── css/
|
||||
└── scss/
|
||||
├── MyPlugin.php # Main plugin class (name = folder name)
|
||||
├── plugin.json # Plugin metadata + settings schema
|
||||
├── README.md # Plugin documentation
|
||||
├── config.json # Optional runtime configuration (overrides defaults)
|
||||
├── assets/ # Optional CSS/JS/SCSS
|
||||
│ ├── css/
|
||||
│ └── scss/
|
||||
└── language/ # Translations
|
||||
├── nl/
|
||||
│ ├── admin.php # Admin labels (system plugins)
|
||||
│ └── site.php # Front-end labels (content plugins)
|
||||
└── en/
|
||||
├── admin.php
|
||||
└── site.php
|
||||
```
|
||||
|
||||
## plugin.json
|
||||
@@ -22,7 +32,9 @@ plugins/MyPlugin/
|
||||
"description": "Description",
|
||||
"type": "content",
|
||||
"essential": false,
|
||||
"hasConfig": false
|
||||
"hasConfig": false,
|
||||
"default_language": "nl",
|
||||
"settings": []
|
||||
}
|
||||
```
|
||||
|
||||
@@ -37,6 +49,54 @@ plugins/MyPlugin/
|
||||
| `type` | `"system"` or `"content"` | System (blue badge) or content (green badge) |
|
||||
| `essential` | boolean | Essential plugins cannot be edited/deleted |
|
||||
| `hasConfig` | boolean | Shows a Config button in admin |
|
||||
| `default_language` | string | Fallback language for plugin translations (e.g. `nl`) |
|
||||
| `settings` | array | Settings schema (see below) |
|
||||
|
||||
## Settings schema
|
||||
|
||||
When `hasConfig: true`, define settings in the `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 Manager"
|
||||
},
|
||||
"label_key": "setting_required_roles",
|
||||
"help_key": "setting_required_roles_help",
|
||||
"option_label_key": "role_options"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### Per-setting fields
|
||||
|
||||
| Field | Description |
|
||||
|-------|-------------|
|
||||
| `key` | Setting key (stored in `config.json`) |
|
||||
| `type` | `text`, `checkbox`, `number`, `select`, `multi-select` |
|
||||
| `default` | Default value |
|
||||
| `label` | Hardcoded label (fallback when no `label_key` or when translation missing) |
|
||||
| `help` | Hardcoded help text (fallback when no `help_key`) |
|
||||
| `label_key` | Key in the plugin's `language/<lang>/admin.php` for a translated label |
|
||||
| `help_key` | Key in the plugin's `language/<lang>/admin.php` for translated help text |
|
||||
| `option_label_key` | Key pointing to an array of translated option labels for `select`/`multi-select` |
|
||||
| `options` | Options for `select`/`multi-select` (`{value: label}`) |
|
||||
|
||||
The admin loads defaults from `plugin.json` and overrides them with values from `config.json`. The plugin reads the resolved values via `PluginManager::getPluginConfig()` or its own `getPluginConfig()` method.
|
||||
|
||||
## Plugin class example
|
||||
|
||||
@@ -63,8 +123,11 @@ class MyPlugin
|
||||
|
||||
public function getSidebarContent(): string
|
||||
{
|
||||
// Fetch plugin translations (content plugin = front-end language)
|
||||
$t = $this->api ? $this->api->getPluginTranslations('MyPlugin') : [];
|
||||
$title = $this->api ? $this->api->getCurrentPageTitle() : '';
|
||||
return '<p>Current page: ' . htmlspecialchars($title) . '</p>';
|
||||
$label = $t['current_page'] ?? 'Current page';
|
||||
return '<p>' . htmlspecialchars($label) . ': ' . htmlspecialchars($title) . '</p>';
|
||||
}
|
||||
}
|
||||
```
|
||||
@@ -76,20 +139,122 @@ The plugin class is automatically loaded by `PluginManager` when the plugin is l
|
||||
The API is injected via `setAPI()`, not via a static method. In the front-end context this is a `CMSAPI` instance, in the admin context an `AdminPluginAPI` instance. Both implement `PluginAPIInterface`.
|
||||
|
||||
```php
|
||||
// Front-end API (CMSAPI)
|
||||
// Front-end API (CMSAPI) - for content plugins
|
||||
$this->api->getCurrentPageTitle();
|
||||
$this->api->getMenu();
|
||||
$this->api->getConfig('site_title');
|
||||
$this->api->getCurrentLanguage();
|
||||
$this->api->isHomepage();
|
||||
$this->api->createUrl('about-us');
|
||||
$this->api->getPluginTranslations('MyPlugin'); // front-end language
|
||||
$this->api->t('current_page', 'MyPlugin'); // translation helper
|
||||
|
||||
// Admin API (AdminPluginAPI)
|
||||
// Admin API (AdminPluginAPI) - for system plugins
|
||||
$this->api->getConfig('analytics.enabled');
|
||||
$this->api->getContentDir();
|
||||
$this->api->getEnabledPlugins();
|
||||
$this->api->getAdminLanguage(); // active admin language
|
||||
$this->api->getPluginTranslations('MyPlugin'); // admin language
|
||||
$this->api->t('menu_label', 'MyPlugin'); // translation helper
|
||||
```
|
||||
|
||||
## Language support (i18n)
|
||||
|
||||
Each plugin can provide translations in `language/<lang>/`. System plugins use `admin.php`, content plugins use `site.php`.
|
||||
|
||||
### Language file format
|
||||
|
||||
`language/en/admin.php`:
|
||||
|
||||
```php
|
||||
<?php
|
||||
return [
|
||||
// Menu
|
||||
'menu_label' => 'My Plugin',
|
||||
|
||||
// Page
|
||||
'page_title' => 'My Plugin',
|
||||
'current_page' => 'Current page',
|
||||
|
||||
// Settings (label_key/help_key point here)
|
||||
'setting_max_items' => 'Maximum number of items',
|
||||
'setting_max_items_help' => 'Number of items shown.',
|
||||
'role_options' => [
|
||||
'admin' => 'Admin',
|
||||
'content-manager' => 'Content Manager',
|
||||
],
|
||||
];
|
||||
```
|
||||
|
||||
### Fallback chain
|
||||
|
||||
Plugin translations are resolved in this order:
|
||||
|
||||
1. **Selected language** — `language/<selected>/admin.php` (or `site.php`)
|
||||
2. **Plugin default_language** — `plugin.json` `default_language` (e.g. `nl`)
|
||||
3. **CMS site default** — `config.language.default`
|
||||
4. **Empty array** — the key is shown unchanged
|
||||
|
||||
### System vs content plugins
|
||||
|
||||
- **System plugins** (`type: "system"`) follow the selected **admin language** (`config.admin_language`). Translations live in `language/<lang>/admin.php`.
|
||||
- **Content plugins** (`type: "content"`) follow the selected **content language** (from the URL or `config.language.default`). Translations live in `language/<lang>/site.php`.
|
||||
|
||||
### Fetching translations in a plugin
|
||||
|
||||
```php
|
||||
// In handleAdminRoute() or getSidebarContent():
|
||||
$t = $this->api->getPluginTranslations('MyPlugin');
|
||||
$tr = function (string $key) use ($t): string {
|
||||
return $t[$key] ?? $key;
|
||||
};
|
||||
|
||||
echo htmlspecialchars($tr('page_title'));
|
||||
```
|
||||
|
||||
Or use the single-key helper:
|
||||
|
||||
```php
|
||||
echo htmlspecialchars($this->api->t('page_title', 'MyPlugin'));
|
||||
```
|
||||
|
||||
### Translating the menu label
|
||||
|
||||
In `getAdminMenu()`, add `label_key`. The admin sidebar shows the translation via `plugin_menu_label()`:
|
||||
|
||||
```php
|
||||
public function getAdminMenu(): array
|
||||
{
|
||||
return [
|
||||
[
|
||||
'plugin' => 'MyPlugin',
|
||||
'route' => 'my-plugin',
|
||||
'label' => 'My Plugin', // fallback
|
||||
'label_key' => 'menu_label', // points to language/<lang>/admin.php
|
||||
'icon' => 'bi-puzzle',
|
||||
'section' => 'general', // or 'system'
|
||||
'permission' => 'my-plugin',
|
||||
],
|
||||
];
|
||||
}
|
||||
```
|
||||
|
||||
### Translating setting labels
|
||||
|
||||
In `plugin.json` `settings`, use `label_key`/`help_key`/`option_label_key` instead of hardcoded `label`/`help`:
|
||||
|
||||
```json
|
||||
{
|
||||
"key": "max_items",
|
||||
"label_key": "setting_max_items",
|
||||
"help_key": "setting_max_items_help",
|
||||
"type": "number",
|
||||
"default": 10
|
||||
}
|
||||
```
|
||||
|
||||
The admin's `handlePluginsConfig()` resolves these via the plugin translations; if a translation is missing it falls back to `label`/`help` from `plugin.json`.
|
||||
|
||||
## Hooks
|
||||
|
||||
Plugins can implement the following methods for automatic hook registration:
|
||||
@@ -115,25 +280,72 @@ Plugins can add custom admin pages via `getAdminMenu()` and `handleAdminRoute()`
|
||||
```php
|
||||
public function getAdminMenu(): array
|
||||
{
|
||||
$config = $this->getPluginConfig();
|
||||
$requiredRoles = $config['required_roles'] ?? ['admin'];
|
||||
|
||||
return [
|
||||
[
|
||||
'plugin' => 'MyPlugin',
|
||||
'route' => 'my-plugin',
|
||||
'label' => 'My Plugin',
|
||||
'label_key' => 'menu_label',
|
||||
'icon' => 'bi-puzzle',
|
||||
'section' => 'general', // or 'system'
|
||||
'section' => 'general', // or 'system'
|
||||
'permission' => 'my-plugin',
|
||||
'required_roles' => $requiredRoles,
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
public function handleAdminRoute(string $action): ?string
|
||||
{
|
||||
return '<h2>My Plugin admin page</h2>';
|
||||
$t = $this->api ? $this->api->getPluginTranslations('MyPlugin') : [];
|
||||
$tr = function (string $key) use ($t): string {
|
||||
return $t[$key] ?? $key;
|
||||
};
|
||||
|
||||
return '<h2>' . htmlspecialchars($tr('page_title')) . '</h2>';
|
||||
}
|
||||
```
|
||||
|
||||
Only plugins listed in `enabled_plugins` are shown in the admin sidebar.
|
||||
|
||||
### Runtime config in a plugin
|
||||
|
||||
Plugins can read their runtime config (defaults from `plugin.json` `settings` + overrides from `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);
|
||||
}
|
||||
```
|
||||
|
||||
Or via the shared method on PluginManager:
|
||||
|
||||
```php
|
||||
$config = $this->pluginManager->getPluginConfig('MyPlugin');
|
||||
```
|
||||
|
||||
## Adding CSS
|
||||
|
||||
Plugins can provide a CSS URL via `getCssUrl()`:
|
||||
@@ -143,4 +355,6 @@ public function getCssUrl(): string
|
||||
{
|
||||
return '/plugins/MyPlugin/assets/css/style.css';
|
||||
}
|
||||
```
|
||||
```
|
||||
|
||||
The URL is passed to the front-end template via `plugin_css_urls`.
|
||||
@@ -4,7 +4,7 @@
|
||||
codepress/
|
||||
├── cms/core/ # Core engine
|
||||
│ ├── class/ # CodePressCMS, ThemeManager, Logger, Analytics
|
||||
│ ├── plugin/ # PluginManager, CMSAPI, AdminPluginAPI
|
||||
│ ├── plugin/ # PluginManager, PluginAPIInterface, CMSAPI, AdminPluginAPI
|
||||
│ ├── config.php # Config loader
|
||||
│ └── index.php # Bootstrap
|
||||
├── admin/ # Admin console
|
||||
@@ -13,12 +13,22 @@ codepress/
|
||||
│ └── theme/default/views/ # Twig templates
|
||||
├── themes/ # Thema's (default, ...)
|
||||
├── plugins/ # Plugins (Dashboard, HTMLBlock, Navigation, ...)
|
||||
│ └── <Plugin>/ # Elke plugin heeft:
|
||||
│ ├── <Plugin>.php # Hoofd class
|
||||
│ ├── plugin.json # Metadata + instellingen
|
||||
│ ├── README.md # Documentatie
|
||||
│ ├── assets/ # CSS/JS/SCSS
|
||||
│ └── language/ # Plugin vertalingen (nl/, en/, ...; admin.php/site.php)
|
||||
├── content/ # Content bestanden (.md, .php, .html)
|
||||
├── language/ # Taalbestanden (nl/, en/, de/)
|
||||
├── language/ # Core CMS taalbestanden (nl/, en/, de/; site.php + admin.php)
|
||||
├── guide/ # Handleidingen (nl/, en/)
|
||||
├── public/ # Web root
|
||||
│ ├── index.php # Website entry point
|
||||
│ └── admin.php # Admin entry point + router
|
||||
├── config.json # Site configuratie
|
||||
└── version.php # Versie info
|
||||
```
|
||||
```
|
||||
|
||||
## Plugin i18n
|
||||
|
||||
Plugins hebben hun eigen `language/` map met vertalingen. Systeem plugins volgen de admin taal (`language/<lang>/admin.php`), content plugins volgen de content taal (`language/<lang>/site.php`). Fallback chain: geselecteerde taal → plugin `default_language` → CMS site default → lege array. Zie `guide/nl/codepress-developer/plugin-development.md` voor details.
|
||||
@@ -2,14 +2,24 @@
|
||||
|
||||
## 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
|
||||
├── config.json # Optionele configuratie
|
||||
└── assets/ # Optionele CSS/JS
|
||||
├── css/
|
||||
└── scss/
|
||||
├── 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
|
||||
@@ -22,7 +32,9 @@ plugins/MijnPlugin/
|
||||
"description": "Beschrijving",
|
||||
"type": "content",
|
||||
"essential": false,
|
||||
"hasConfig": false
|
||||
"hasConfig": false,
|
||||
"default_language": "nl",
|
||||
"settings": []
|
||||
}
|
||||
```
|
||||
|
||||
@@ -36,7 +48,55 @@ plugins/MijnPlugin/
|
||||
| `description` | string | Korte beschrijving |
|
||||
| `type` | `"system"` of `"content"` | Systeem (blauwe badge) of content (groene badge) |
|
||||
| `essential` | boolean | Essentiële plugins kunnen niet worden bewerkt/verwijderd |
|
||||
| `hasConfig` | boolean | Toont een Config-knop in de admin |
|
||||
| `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
|
||||
|
||||
@@ -63,36 +123,141 @@ class MijnPlugin
|
||||
|
||||
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() : '';
|
||||
return '<p>Huidige pagina: ' . htmlspecialchars($title) . '</p>';
|
||||
$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`.
|
||||
|
||||
## CMSAPI gebruiken
|
||||
## API gebruiken
|
||||
|
||||
De API wordt geïnjecteerd via `setAPI()`, niet via een statische methode. In de front-end context is dit een `CMSAPI` instance, in de admin context een `AdminPluginAPI` instance. Beide implementeren `PluginAPIInterface`.
|
||||
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)
|
||||
// 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)
|
||||
// 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 automatiche hook-registratie:
|
||||
Plugins kunnen de volgende methodes implementeren voor automatische hook-registratie:
|
||||
|
||||
**Actions** (geen return waarde):
|
||||
|
||||
@@ -115,25 +280,72 @@ Plugins kunnen eigen admin-pagina's toevoegen via `getAdminMenu()` en `handleAdm
|
||||
```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'
|
||||
'section' => 'general', // of 'system'
|
||||
'permission' => 'mijn-plugin',
|
||||
'required_roles' => $requiredRoles,
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
public function handleAdminRoute(string $action): ?string
|
||||
{
|
||||
return '<h2>Mijn Plugin admin pagina</h2>';
|
||||
$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()`:
|
||||
@@ -143,4 +355,6 @@ public function getCssUrl(): string
|
||||
{
|
||||
return '/plugins/MijnPlugin/assets/css/style.css';
|
||||
}
|
||||
```
|
||||
```
|
||||
|
||||
De URL wordt doorgegeven aan de front-end template via `plugin_css_urls`.
|
||||
Reference in New Issue
Block a user