Files
KantSites/docs/PLUGINS.md
T
kantarciandClaude Opus 5 30aac4389d Take design packages back out of plugins
A plugin can no longer replace the site palette. "Designpaket übernehmen"
wrote the plugin's colours, fonts and buttons into the website for good:
no confirmation, no way back, right next to a switch that is safe to flip.
Colours belong to the design screen, where they have a preview and one-step
undo, so the feature is gone rather than guarded (ADR 105).

Removed: PluginRegistry::themePackage() and applyTheme(), "theme" in the
manifest and in the plugin structure, the admin_plugin_theme route and the
Theme dependency of PluginAdminController, the button and the design-package
line on the "Plugins" screen, and plugins/nachtblau. A manifest now needs at
least one block, one route or one preset.

The test says the opposite of what it used to: switching a plugin on and off
leaves both the design and the content alone.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-16 07:39:31 +02:00

8.8 KiB
Raw Permalink Blame History

Plugins schreiben

Ein Plugin bringt eigene Bausteine, Vorlagen und Adressen in den Editor und auf die Website. Farben, Schriften und Buttons fasst es nie an – das Design gehört der Website (ADR 105). Es ist ein Ordner unter plugins/ mit einer plugin.json, Twig-Vorlagen und optional Stylesheets und Skripten. Eingeschaltet und eingestellt wird es in der Verwaltung unter Plugins (Administrator). Hintergrund und Grenzen: ADR 102 und ADR 103 in docs/ARCHITECTURE.md.

Ein Plugin bringt keinen PHP-Code mit. Alles ist Manifest plus Twig. Deshalb gibt es keine Hooks in Speichern oder Veröffentlichen und keine eigenen Tabellen – und deshalb kann ein Update des Kerns ein Plugin nicht brechen, solange die Übergaben unten stehen.

Der Ordner

plugins/
  vergleich/                      ← Name des Plugins: Kleinbuchstaben und Ziffern, mit Buchstabe beginnend
    plugin.json                   ← das Manifest
    blocks/bildvergleich.html.twig
    blocks/galerie.html.twig
    routes/daten.json.twig
    assets/bildvergleich.css      ← nur unter assets/ liegt, was die Website ausliefert
    assets/bildvergleich.js

Das mitgelieferte Plugin ist die Vorlage zum Abschreiben: plugins/vergleich (Bausteine, Container, Einstellungen, Adresse, Seitenvorlage).

Das Manifest

{
  "name": "vergleich",
  "title": "Bildvergleich",
  "description": "Vorher und nachher in einem Bild.",
  "version": "1.1.0",
  "requires": "1.17.0",
  "settings": {
    "labelBefore": { "kind": "text", "label": "Standardbeschriftung vorher", "default": "Vorher" }
  },
  "blocks": {
    "bildvergleich": {
      "label": "Bildvergleich",
      "icon": "⇔",
      "template": "blocks/bildvergleich.html.twig",
      "css": "assets/bildvergleich.css",
      "script": "assets/bildvergleich.js",
      "props": {
        "before":   { "kind": "media",   "label": "Bild vorher" },
        "position": { "kind": "number",  "label": "Startposition (%)", "min": 5, "max": 95, "default": 50 }
      }
    },
    "galerie": {
      "label": "Vergleichsgalerie",
      "template": "blocks/galerie.html.twig",
      "container": true,
      "children": ["vergleich.bildvergleich"],
      "props": { "columns": { "kind": "select", "label": "Spalten", "choices": { "2": "Zwei", "3": "Drei" } } }
    }
  },
  "presets": {
    "abschnitt": { "label": "Vorher-Nachher-Abschnitt", "blocks": [ { "id": "s", "type": "section", "children": [ ] } ] }
  },
  "routes": {
    "daten": { "path": "/vergleich/daten", "template": "routes/daten.json.twig", "type": "json" }
  }
}
  • name muss dem Ordnernamen entsprechen. Der Typ eines Bausteins ist name.baustein, hier vergleich.bildvergleich; so heißt er im Dokument und in der Ausgabe.
  • version in der Form 1.2.3. Sie hängt an den Adressen der Dateien (?v=1.1.0), damit der Browser nach einem Update die neue Fassung holt.
  • requires (optional): die kleinste KantSites-Fassung. Passt sie nicht, erscheint das Plugin in der Verwaltung mit dem Grund und lässt sich nicht einschalten.
  • Ein Manifest braucht mindestens einen Baustein, eine Adresse oder eine Vorlage. Fehlt eine genannte Datei, ist das Manifest ungültig.

Eigenschaften und Einstellungen (Schema)

Eigenschaften (props eines Bausteins) und Einstellungen (settings des Plugins) haben dasselbe Schema: eine Art (kind), eine Beschriftung (label), optional help und default.

Art Feld Prüfung Weitere Schlüssel
text einzeilig Text bis maxLength (Standard 200) maxLength
textarea mehrzeilig Text bis maxLength (Standard 2000) maxLength
number Zahl zwischen min und max min, max
boolean Kästchen an oder aus –
select Liste einer der Werte choices: {"wert": "Beschriftung"}
url einzeilig lokaler Pfad, #sprungmarke, https://, mailto:, tel: maxLength
media Bild aus der Mediathek Kennung eines Bildes oder leer –

Höchstens 30 Eigenschaften je Baustein. Unbekannte Eigenschaften in einem gespeicherten Dokument werden ignoriert (so kann ein Plugin eine Eigenschaft streichen), bekannte müssen passen, sonst lässt sich die Seite nicht speichern. Einstellungen bearbeitet der Administrator auf dem Bildschirm „Plugins“; sie werden genauso geprüft.

Container

"container": true macht einen Baustein zum Behälter. "children" (optional) zählt auf, was hinein darf – Kernbausteine wie text oder Bausteine des Plugins; ohne die Liste darf alles hinein. Der Editor zeigt die Regel im Werkzeugkasten, der Server prüft sie beim Speichern.

Seitenvorlagen (presets)

Fertige Bausteine, die der Editor als Ganzes einfügt – hinter den eingebauten Vorlagen mit demselben Knopf. blocks ist eine Liste von Bausteinen wie in einem Dokument (höchstens 64 KiB); Kennungen vergibt der Editor bei jedem Einfügen neu, du kannst also feste schreiben.

Eigene Adressen (routes)

path hat die Form einer Seite (/name oder /name/unter/seite, Slugs) und darf nichts belegen, was der Kern beantwortet (/admin, /media, /assets, /formular, /beitraege …). type ist html, json, text oder xml. Eine veröffentlichte Seite mit derselben Adresse geht vor. Ausgeschaltet antwortet die Adresse mit 404; alles außer html trägt noindex.

Was ein Plugin nicht kann: das Design

Es gibt kein theme im Manifest. Ein Plugin gestaltet seine eigenen Bausteine über deren Stylesheet und die Design-Tokens des Kerns; Palette, Schriften und Buttons der Website ändert nur der Betreiber unter Design. So färbt kein Schalter eine Website um, und nichts, was dort eingestellt wurde, geht durch ein Plugin verloren (ADR 105).

Die Vorlagen

Bausteine

Die Vorlage eines Bausteins bekommt genau sechs Dinge:

  • props – die geprüften Eigenschaften, jede vorhanden, fehlende mit ihrem Vorgabewert
  • settings – die Einstellungen des Plugins, jede vorhanden
  • children – bei einem Container die fertig gerenderten Kinder (leer sonst); ausgeben mit {{ children }}
  • block – der Baustein selbst; block.id gehört als id an das Wurzelelement, sonst greifen eigenes CSS und die Editorauswahl nicht
  • designClasses – Klassen aus Sichtbarkeit, Design-Tokens und Einblendungen, an die class des Wurzelelements anhängen
  • editing – true in der Editorvorschau, für Hinweise wie „bitte ein Bild wählen“

Dazu eine Funktion: kant_plugin_media(kennung) liefert ein Bild der Mediathek als {id, title, alt, variants: [{src, width, height}, …]} (kleinste Fassung zuerst) oder null.

{% set bild = props.before ? kant_plugin_media(props.before) : null %}
<div id="{{ block.id }}" class="block-bildvergleich{{ designClasses }}" data-position="{{ props.position }}">
  {% if bild %}<img src="{{ (bild.variants|last).src }}" alt="{{ props.alt ?: settings.labelBefore }}">{% endif %}
  {{ children }}
</div>

Adressen

Die Vorlage einer Adresse bekommt settings, query (die Abfrageparameter mit sauberen Namen, gekürzt) und route. Für json und xml schreibt sie die Antwort selbst, etwa mit json_encode.

Was keine Vorlage bekommt: Datenbank, Einstellungen der Website, Anfrage. Sie läuft mit den Rechten der Anwendung – ein Sandkasten ist das nicht, und so wird es auch nicht genannt.

Stylesheet und Skript

Beide werden nur auf Seiten geladen, auf denen der Baustein vorkommt, über /plugin-assets/<name>/assets/.... Die Content-Security-Policy erlaubt Skripte vom eigenen Ursprung, sobald die Seite eines braucht; Inline-Stile und Inline-Skripte bleiben verboten. Ein Skript setzt Werte deshalb über das CSSOM (element.style.setProperty(...)), nie über ein style-Attribut in der Vorlage. In der Editorvorschau wird das Stylesheet geladen, das Skript nicht – die Vorlage muss auch ohne Skript ein brauchbares Bild abgeben.

Was der Kern übernimmt

  • Speichern, Veröffentlichen, Fassungen, Undo/Redo – der Baustein ist ein gewöhnlicher Block.
  • Eigenes CSS je Block, Design-Tokens (Abstände, Ausrichtung, Ecken), Sichtbarkeit je Gerät, Einblendungen – alles über designClasses und die id.
  • Der Editor: Eintrag im Werkzeugkasten unter „Erweiterungen“, Eigenschaftenpanel aus dem Schema, Container-Regeln, Vorlagen, Vorschau über den Server.
  • Der Erhaltungsvertrag: ist das Plugin aus oder fehlt es, bleibt der Baustein im Dokument, der Editor zeigt einen Platzhalter, die Website einen HTML-Kommentar. Nichts geht verloren.

Updates

Mitgelieferte Plugins liegen im Releasearchiv und werden beim Archiv-Update Datei für Datei aktualisiert. Ein selbst installiertes Plugin bleibt dabei stehen, weil sein Ordner im Archiv nicht vorkommt. Beim Git-Update ist ein fremder Ordner unter plugins/ nicht von Git verwaltet; git checkout lässt ihn in Ruhe. Die Einstellungen liegen in der Datenbank und überleben beides.