Inhaltsverzeichnis

Zurück: edwiki

DokuWiki-Plugin-Leitfaden (edwiki 2.0)

Arbeitsgrundlage für Claude bei jeder DokuWiki-Plugin-Arbeit in diesem Projekt (structauto u. a.). Angepasst aus tracker-user/dokuwiki-claude (GPL-2.0, https://github.com/tracker-user/dokuwiki-claude) – Umgebung, Browser-Vorgaben und Übersetzungen auf Olivers Setup umgestellt, Windows/Docker-Teile entfernt.

1. Umgebung (Oliver)

2. Arbeitsweise

3. Plugin-Aufbau

<name>/
├── plugin.info.txt           # Manifest (Pflicht)
├── syntax.php                # Wiki-Syntax
├── action.php                # Event-Hooks, AJAX, UI-Einbindung
├── admin.php                 # Admin-Seite
├── helper.php                # reine Logik/API, von anderen Plugins nutzbar
├── renderer.php / remote.php / auth.php / cli.php
├── <typ>/<komp>.php          # mehrere Komponenten eines Typs
├── script.js / style.css     # werden GLOBAL auf jeder Seite gebündelt (sparsam!)
├── conf/default.php          # Standardwerte ($conf[...])
├── conf/metadata.php         # ACP-Metadaten ($meta[...]; kein require_once!)
├── lang/<iso>/lang.php       # UI-Texte ($lang[...]; keine Wiki-Syntax)
├── lang/<iso>/settings.php   # Labels für den Konfigurationsmanager
└── deleted.files             # optional: beim Update zu löschende Dateien

Benennung: <typ>.php → Klasse <typ>_plugin_<name>. Mehrere Komponenten: <typ>/<komp>.php → <typ>_plugin_<name>_<komp> (Syntax-Modus dann plugin_<name>_<komp>). Plugin-Name nur a-z0-9, kein Unterstrich.

Basisklassen (\dokuwiki\Extension\): SyntaxPlugin, ActionPlugin, AdminPlugin, Plugin (für Helper), RemotePlugin, AuthPlugin, CLIPlugin, EventHandler. Renderer erben von Doku_Renderer.

4. PHP-Konventionen

Pflicht

Stil: [] statt array(), ?? für Fallbacks, kein @, in_array(..., true), str_starts_with(), keine var_dump/print_r/error_log-Reste.

Rechte-Muster: Helper sind reine Logik und bekommen Fakten als Parameter; die Action-Schicht sammelt sie ($INPUT->server->str('REMOTE_USER'), auth_isadmin()).

5. JS & CSS

6. DokuWiki-API kompakt

Syntax-Plugins

Events

Konfiguration (conf/metadata.php) $meta['k'] = ['<klasse>', '_param' => …]; – Klassen: string, numeric/numericopt, onoff (Default als int 0/1, nicht bool), multichoice (_choices), multicheckbox, regex, fieldset … Labels in lang/<iso>/settings.php.

Lokalisierung: getLang('k'), locale_xhtml('datei'); JS-Texte über $lang['js']['k'] → LANG.plugins.<name>.k. Fehlende Schlüssel fallen auf Englisch zurück.

Caching & Metadaten

Globale & Konstanten: $ID, $INFO, $ACT (kann ein Array sein → !is_array() prüfen), $conf, $INPUT, $USERINFO, $JSINFO. DOKU_INC, DOKU_PLUGIN, DOKU_BASE, DOKU_URL.

Formulare: dokuwiki\Form\Form benutzen – Sicherheitstoken wird automatisch eingefügt.

7. Sicherheit (die teuersten Lektionen)

  1. Escapen: hsc() für HTML, rawurlencode() für URL-Werte; nutzergelieferte URLs nur mit ^https?:// erlauben (javascript: blocken).
  2. Attribut-Injection – häufigste XSS-Ursache in Syntax-Plugins: Ein Wert aus dem Wiki-Markup landet ungeprüft in class=, style=, id=, title= usw. Ein " bricht aus dem Attribut aus. Regel: (a) Wert per Regex-Capture-Group mit strengem Zeichensatz holen, beide Enden verankern (^…$), keine Quantoren, die leer matchen können (*, ?, \d*); nie nur trim(); (b) bei der Ausgabe zusätzlich hsc(). Redakteure gelten als nicht vertrauenswürdig.
  3. Struct-Werte sind Nutzereingaben: Alles, was aus Struct-Feldern in HTML (Infobox) geschrieben wird, escapen – oder, wie bei structauto, als Wikitext über p_render() laufen lassen.
  4. Regex-Nachbearbeitung von HTML (z. B. RENDERER_CONTENT_POSTPROCESS): nur Strukturen entfernen, nie Nutzerdaten unescaped wieder einsetzen.
  5. Daten ins Inline-JSINFO-Script: json_encode(..., JSON_HEX_TAG) verwenden. JSON_UNESCAPED_SLASHES allein macht </script>-Ausbruch möglich → gespeichertes XSS.
  6. JS: Fremddaten (AJAX-Antworten) mit textContent/.text() einsetzen, nie innerHTML/.html().
  7. HTML-Kommentare: Nutzerdaten nie in <!-- … --> ausgeben – Browser beenden Kommentare auch bei --!>.
  8. CSRF ≠ Berechtigung: Zustandsändernde Aktionen brauchen beides: checkSecurityToken() und auth_quickaclcheck($id) gegen die nötige Stufe (AUTH_EDIT, AUTH_DELETE, AUTH_ADMIN). Benutzer-ID über $INPUT->server->str('REMOTE_USER').
  9. Keine eigenständigen PHP-Skripte, die selbst inc/init.php laden und Anfrage-Daten ausgeben – die umgehen ACL und CSRF. Immer über action.php / AJAX_CALL_UNKNOWN.
  10. Keine Stacktraces/Serverpfade an normale Nutzer ausgeben.
  11. Ausnahme Editor-Round-Trip: In Daten, die zurück ins Markup geschrieben werden (WYSIWYG), nicht escapen – nur an der HTML-Grenze, sonst wird & bei jedem Speichern zu &amp;.

8. Review-Checkliste

  1. Fatale Fehler auf aktueller DokuWiki-Version (Namespace-Änderungen)
  2. DOKU_INC-Guard auf Logik-Dateien (nicht conf/lang)
  3. Sichtbarkeit auf allen Methoden, protected bevorzugt, kein ?>
  4. Docblocks auf public/protected
  5. Keine $_REQUEST/$_POST/$_GET, kein @
  6. Globales JS/CSS nur wenn nötig, sonst bedingt laden
  7. Datei-Schreibzugriffe korrekt gelockt (nie io_saveFile() extra locken)
  8. Alle Ausgaben escaped (hsc()), Attribut-Werte per Allowlist-Regex
  9. Null-Sicherheit bei Array-Zugriffen (?? [])
  10. conf/metadata.php ohne require_once; default.php ↔ metadata.php Schlüssel synchron
  11. Keine Debug-Reste, TODO/FIXME markieren
  12. CSRF-Token und ACL-Prüfung bei zustandsändernden Aktionen
  13. URLs rawurlencode(), nur http(s)
  14. plugin.info.txt Datum/Version aktuell
  15. Übersetzungen en + de vollständig; hartkodierte Texte nach lang/en/lang.php auslagern
  16. README stimmt mit Code überein (Code hat Vorrang)

9. Häufige Stolperfallen

10. plugin.info.txt & Veröffentlichung

11. Tests (optional)

Nur wenn beim Review ohnehin nötig: <plugin>/_test/, Namespace \dokuwiki\plugin\<name>\test\, erbt DokuWikiTest, @group plugin_<name>, protected $pluginsEnabled = ['<name>'];. Ein GeneralTest.php prüft plugin.info.txt und conf-Schlüssel-Sync. Läuft nur in einer lokalen DokuWiki-Installation, nicht auf lima-city.