Benutzer-Werkzeuge

Webseiten-Werkzeuge


ki:edwiki:plugin:leitfaden

Unterschiede

Hier werden die Unterschiede zwischen zwei Versionen angezeigt.

Link zu dieser Vergleichsansicht

Beide Seiten der vorigen RevisionVorhergehende Überarbeitung
ki:edwiki:plugin:leitfaden [2026/10/11 09:04] – gelöscht - Externe Bearbeitung (Unbekanntes Datum) 127.0.0.1ki:edwiki:plugin:leitfaden [2026/10/11 09:04] (aktuell) – ↷ Seite von ki:edwiki:plugin-leitfaden nach ki:edwiki:plugin:leitfaden verschoben und umbenannt nemwar
Zeile 1: Zeile 1:
 +Zurück: [[ki:stand: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) =====
 +
 +  * **Entwicklung/Test:** lima-city-Webspace (Freehosting) – **kein Shell-Zugang, keine Server-Logs**; Dateien per Upload/FTP. Einige ''%%Options%%''-Zeilen in ''%%.htaccess%%'' müssen dort auskommentiert werden.
 +  * **Produktiv:** edwiki.de auf UD MEDIA (Klassik-Hosting) – PHP-Version noch per ''%%phpinfo()%%'' verifizieren.
 +  * **DokuWiki-Version:** auf dem Testwiki prüfen (''%%?do=check%%'') und hier eintragen; bis dahin auf die aktuelle Version „Mort“ (2026-07-14) hin entwickeln, aber alte API-Pfade nicht blind entfernen.
 +  * **PHP:** Ziel 8.x – exakte Untergrenze (''%%phpmin%%'') erst nach Prüfung beider Hoster festlegen.
 +  * **Lokale Dev-Maschine:** CachyOS (Linux). Optional lokales Test-DokuWiki per Docker, ist aber kein Muss.
 +  * **Browser-Untergrenze:** moderne Browser (aktuelles Firefox/Chrome) – **keine** Altbrowser-Einschränkungen nötig.
 +  * **Sprachen:** jedes Plugin bekommt **''%%en%%'' + ''%%de%%''**.
 +  * **Debugging ohne Logs:** ''%%?do=check%%'' für Umgebung/Rechte, ''%%allowdebug%%'' + ''%%Logger::debug()%%'' (Ausgabe im LogViewer unter Admin), notfalls temporäre ''%%msg()%%''-Ausgaben – vor dem Commit wieder entfernen.
 +
 +===== 2. Arbeitsweise =====
 +
 +  * **Erst planen, dann coden:** Idee/Plan besprechen, Code erst nach ausdrücklicher Freigabe („leg los“).
 +  * **Nicht raten, nachschlagen:** DokuWiki-APIs, Hooks und Klassen gegen den echten Quellcode prüfen (github.com/dokuwiki/dokuwiki bzw. struct-Plugin-Quellcode), nicht aus dem Gedächtnis.
 +  * **Nach Änderungen an JS/CSS/lang** Cache brechen (siehe Abschnitt 9).
 +  * **Zeilenenden:** LF in allen Dateien.
 +
 +===== 3. Plugin-Aufbau =====
 +
 +<code>
 +<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
 +</code>
 +
 +**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**
 +
 +  * ''%%if (!defined('DOKU_INC')) die();%%'' auf allen Logik-Dateien (action/syntax/helper/admin …), **nicht** auf ''%%conf/%%'' und ''%%lang/%%''.
 +  * Sichtbarkeit immer explizit; ''%%protected%%'' statt ''%%private%%'' bevorzugen.
 +  * Kein schließendes ''%%?>%%''. Code-Stil PSR-12.
 +  * Docblocks (''%%@param%%'', ''%%@return%%'', Zweck) auf public/protected-Methoden.
 +  * Eingaben nur über ''%%$INPUT->post->str()/int()/arr()%%'', ''%%$INPUT->get->str()%%'' – nie ''%%$_REQUEST/$_POST/$_GET%%''.
 +  * Seiten-IDs mit ''%%cleanID()%%'' säubern.
 +  * Dateien: ''%%io_saveFile()%%'' ist bereits atomar – **nicht** zusätzlich locken (Deadlock). Für Lesen-Ändern-Schreiben eigenes ''%%io_lock()%%''/''%%io_unlock()%%'' um die ganze Sequenz.
 +  * JSON speichern mit ''%%JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE%%''.
 +
 +**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 =====
 +
 +  * Vanilla JS, kein Framework. AJAX per ''%%fetch()%%'' mit ''%%JSINFO.sectok%%''. jQuery nur als ''%%jQuery()%%'', nie ''%%$()%%''.
 +  * Alle Bundles laden ''%%defer%%''. Inline-JS nur über ''%%tpl_inlineScript()%%'' (setzt CSP-Nonce), nie handgeschriebenes ''%%<script>%%''.
 +  * JSINFO-Werte im Action-Plugin bei ''%%DOKUWIKI_STARTED%%'' unter einem ''%%plugin_<name>%%''-Unterschlüssel setzen.
 +  * CSS: DokuWiki-Platzhalter (''%%__background__%%'', ''%%__text__%%'', ''%%__border__%%'', ''%%__link__%%'' bzw. ''%%@ini_…%%''), Klassen/IDs mit Plugin-Namen präfixen, IDs als ''%%<name>__id%%'' (doppelter Unterstrich).
 +  * **Plugin-CSS/JS lädt auf jeder Seite.** Feature-spezifisches per ''%%TPL_METAHEADER_OUTPUT%%'' bedingt einbinden.
 +  * **Stolperfalle LESS:** Alles CSS läuft durch lesserphp. ''%%rgba(var(--x), 0.3)%%'' wird zu Schwarz kompiliert → so schreiben: ''%%background: ~"rgba(var(--x), 0.3)";%%'' (relevant fürs Schwarz/Orange-Theme mit CSS-Variablen).
 +
 +===== 6. DokuWiki-API kompakt =====
 +
 +**Syntax-Plugins**
 +
 +  * ''%%getType%%'': ''%%container%%'', ''%%baseonly%%'', ''%%formatting%%'', ''%%substition%%'' (Tippfehler ist korrekt!), ''%%protected%%'', ''%%disabled%%'', ''%%paragraphs%%''. ''%%getPType%%'': ''%%normal%%'' / ''%%block%%'' / ''%%stack%%''. ''%%getSort%%'': niedrigste gewinnt.
 +  * Patterns: ''%%addSpecialPattern%%'' (einmalig), ''%%addEntryPattern%%''+''%%addExitPattern%%'' (Paar). Keine Delimiter, ''%%(?:…)%%'' für Alternativen, keine Rückverweise, nur ''%%(?i)%%''.
 +  * ''%%handle()%%'' macht die schwere Arbeit – **Ergebnis wird gecacht**; Daten nur über das Rückgabe-Array an ''%%render()%%''.
 +  * ''%%render()%%'': auf ''%%$format == 'xhtml'%%'' prüfen, an ''%%$renderer->doc%%'' anhängen, mit ''%%hsc()%%'' escapen. ''%%$format == 'metadata'%%'' behandeln und Links via ''%%$renderer->internallink()%%'' registrieren (Backlinks).
 +  * **Seiten-Flags** (z. B. „diese Seite hat eine Infobox“) im //aktuellen// Metadaten-Zweig setzen: ''%%$renderer->meta['plugin']['<name>'] = true;%%'' in ''%%render()%%'' bei ''%%metadata%%''. Dann verschwindet das Flag von selbst, wenn die Syntax entfernt wird. Persistent gesetzte Flags bleiben für immer kleben.
 +  * ''%%p_render()%%''/''%%render()%%'' (Wikitext parsen) ist teuer – sparsam einsetzen und Ergebnis cachen.
 +
 +**Events**
 +
 +  * ''%%$controller->register_hook($name, 'BEFORE'|'AFTER', $this, 'methode', $param, $seq)%%''; Handler müssen **public** sein.
 +  * ''%%$seq%%'': −3999…−1 früher, 0 Standard, 1…3999 später.
 +  * ''%%$e->preventDefault()%%'' (nur BEFORE) überspringt die Standardaktion; ''%%$e->stopPropagation()%%'' überspringt spätere Handler.
 +  * Häufige Events: ''%%DOKUWIKI_STARTED%%'', ''%%TPL_METAHEADER_OUTPUT%%'', ''%%TPL_ACT_RENDER%%'', ''%%RENDERER_CONTENT_POSTPROCESS%%'', ''%%PARSER_CACHE_USE%%'', ''%%ACTION_ACT_PREPROCESS%%'', ''%%AJAX_CALL_UNKNOWN%%'', ''%%MENU_ITEMS_ASSEMBLY%%''.
 +  * Externe Bibliotheken im Konstruktor oder bei Bedarf laden, **nie** am Dateianfang. Konstruktoren von Syntax-Plugins laufen bei jedem Parse – keine Seiteneffekte dort.
 +
 +**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**
 +
 +  * Cache nicht pauschal abschalten – Abhängigkeiten per ''%%PARSER_CACHE_USE%%'' ergänzen. ''%%~~NOCACHE~~%%'' erzwingt Neu-Rendern.
 +  * **Wichtig für structauto:** Wenn eine Seite Daten //anderer// Seiten/Struct-Tabellen anzeigt, muss der Cache wissen, wann er ungültig wird (sonst veraltete Infoboxen).
 +  * ''%%p_get_metadata($id, $key, $render)%%'' / ''%%p_set_metadata(...)%%''; Plugin-Daten unter ''%%plugin.<name>%%''.
 +
 +**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) =====
 +
 +  - **Escapen:** ''%%hsc()%%'' für HTML, ''%%rawurlencode()%%'' für URL-Werte; nutzergelieferte URLs nur mit ''%%^https?://%%'' erlauben (''%%javascript:%%'' blocken).
 +  - **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.
 +  - **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.
 +  - **Regex-Nachbearbeitung von HTML** (z. B. ''%%RENDERER_CONTENT_POSTPROCESS%%''): nur Strukturen entfernen, nie Nutzerdaten unescaped wieder einsetzen.
 +  - **Daten ins Inline-''%%JSINFO%%''-Script:** ''%%json_encode(..., JSON_HEX_TAG)%%'' verwenden. ''%%JSON_UNESCAPED_SLASHES%%'' allein macht ''%%</script>%%''-Ausbruch möglich → gespeichertes XSS.
 +  - **JS:** Fremddaten (AJAX-Antworten) mit ''%%textContent%%''/''%%.text()%%'' einsetzen, nie ''%%innerHTML%%''/''%%.html()%%''.
 +  - **HTML-Kommentare:** Nutzerdaten nie in ''%%<!-- … -->%%'' ausgeben – Browser beenden Kommentare auch bei ''%%--!>%%''.
 +  - **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')%%''.
 +  - **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%%''.
 +  - **Keine Stacktraces/Serverpfade** an normale Nutzer ausgeben.
 +  - **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 =====
 +
 +  - Fatale Fehler auf aktueller DokuWiki-Version (Namespace-Änderungen)
 +  - DOKU_INC-Guard auf Logik-Dateien (nicht conf/lang)
 +  - Sichtbarkeit auf allen Methoden, ''%%protected%%'' bevorzugt, kein ''%%?>%%''
 +  - Docblocks auf public/protected
 +  - Keine ''%%$_REQUEST/$_POST/$_GET%%'', kein ''%%@%%''
 +  - Globales JS/CSS nur wenn nötig, sonst bedingt laden
 +  - Datei-Schreibzugriffe korrekt gelockt (nie ''%%io_saveFile()%%'' extra locken)
 +  - Alle Ausgaben escaped (''%%hsc()%%''), Attribut-Werte per Allowlist-Regex
 +  - Null-Sicherheit bei Array-Zugriffen (''%%?? []%%'')
 +  - ''%%conf/metadata.php%%'' ohne ''%%require_once%%''; ''%%default.php%%'' ↔ ''%%metadata.php%%'' Schlüssel synchron
 +  - Keine Debug-Reste, TODO/FIXME markieren
 +  - CSRF-Token **und** ACL-Prüfung bei zustandsändernden Aktionen
 +  - URLs ''%%rawurlencode()%%'', nur ''%%http(s)%%''
 +  - ''%%plugin.info.txt%%'' Datum/Version aktuell
 +  - Übersetzungen ''%%en%%'' + ''%%de%%'' vollständig; hartkodierte Texte nach ''%%lang/en/lang.php%%'' auslagern
 +  - README stimmt mit Code überein (Code hat Vorrang)
 +
 +===== 9. Häufige Stolperfallen =====
 +
 +  * **Cache-Buster:** Nach Änderungen an ''%%script.js%%''/''%%style.css%%''/''%%lang%%'' liefern Browser weiter das alte Bündel, weil Plugin-Dateien nicht in die Bündel-URL einfließen. Lösung: im Konfigurationsmanager irgendeine Einstellung speichern (oder ''%%conf/local.php%%'' neu hochladen/anfassen). Das bricht auch den Metadaten-Render-Cache.
 +  * **Metadaten-Cache:** Code-Änderungen am Plugin invalidieren gerenderte Metadaten nicht – gleicher Trick wie oben.
 +  * **''%%~~MARKER~~%%'' für den Rest der Seite:** ''%%addSpecialPattern('~~MARKER~~[\s\S]*', …)%%'' fängt alles bis Seitenende als ein ungeparstes Token (Lexer läuft mit DOTALL).
 +  * **''%%class_exists()%%''** löst Autoload aus; ''%%class_exists($c, false)%%'' nur, wenn man das explizit nicht will.
 +  * **''%%metaFN($id, '.myplugin')%%''** liefert einen Pfad in ''%%data/meta/%%'' passend zur Seite.
 +  * **Admin-Tabellen mit Zeilen-Buttons:** keine verschachtelten ''%%<form>%%'' (HTML5 ''%%form=%%''-Attribut nutzen), kein Feld namens ''%%page%%''.
 +
 +===== 10. plugin.info.txt & Veröffentlichung =====
 +
 +  * Eigene Plugins: ''%%date%%'' = heutiges Datum (YYYY-MM-DD), ''%%phpmin X.Y%%'' falls nötig, keine Kommentare in der Datei.
 +  * Repo-Schema: ''%%github.com/<user>/dokuwiki-<name>%%'', Branch ''%%main%%''.
 +  * Plugin-Seite auf dokuwiki.org: Seiten-ID exakt ''%%plugin:<name>%%''; ''%%downloadurl%%'' → ''%%…/zipball/main%%''; Typ-Bitmaske: 1 Syntax, 2 Admin, 4 Action, 8 Render, 16 Helper, 64 Remote, 128 Auth, 256 CLI (structauto = Syntax + Action = ''%%Syntax, Action%%'').
 +  * Vorlagen dafür: ''%%PUBLISHING.md%%'', ''%%PLUGIN_REPO_REFERENCE.md%%'', ''%%PROMPT_NEW_PLUGIN_PAGE.md%%'' im Quell-Repo.
 +  * WYSIWYG (ProseMirror-Editor)-Unterstützung bei Bedarf: ''%%PROSEMIRROR_PLUGIN_SUPPORT.md%%'' im Quell-Repo. Eigenständige Knoten gehen über die öffentliche API; Inline-Formatierungen mit Attributen erfordern Änderungen am ProseMirror-Plugin selbst.
 +
 +===== 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.