Files
E.Noorlander 5edc929c13 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.
2026-08-18 13:49:52 +00:00

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 kunnen niet worden bewerkt/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:

  1. Geselecteerde taallanguage/<geselecteerd>/admin.php (of site.php)
  2. Plugin default_languageplugin.json default_language (bijv. nl)
  3. CMS site defaultconfig.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

// 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 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():

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.