# 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//admin.php` voor vertaald label | | `help_key` | Sleutel in plugin `language//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 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 '

' . htmlspecialchars($label) . ': ' . htmlspecialchars($title) . '

'; } } ``` 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//`. Systeem plugins gebruiken `admin.php`, content plugins gebruiken `site.php`. ### Taalbestand formaat `language/nl/admin.php`: ```php '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//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//admin.php`. - **Content plugins** (`type: "content"`) volgen de geselecteerde **content taal** (uit de URL of `config.language.default`). Vertalingen staan in `language//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//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 '

' . htmlspecialchars($tr('page_title')) . '

'; } ``` 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`.