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 ===== / ├── 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 ├── /.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//lang.php # UI-Texte ($lang[...]; keine Wiki-Syntax) ├── lang//settings.php # Labels für den Konfigurationsmanager └── deleted.files # optional: beim Update zu löschende Dateien **Benennung:** ''%%.php%%'' → Klasse ''%%_plugin_%%''. Mehrere Komponenten: ''%%/.php%%'' → ''%%_plugin__%%'' (Syntax-Modus dann ''%%plugin__%%''). 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 ''%%%%''-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 ''%%&%%''. ===== 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 ''%%
%%'' (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//dokuwiki-%%'', Branch ''%%main%%''. * Plugin-Seite auf dokuwiki.org: Seiten-ID exakt ''%%plugin:%%''; ''%%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: ''%%/_test/%%'', Namespace ''%%\dokuwiki\plugin\\test\%%'', erbt ''%%DokuWikiTest%%'', ''%%@group plugin_%%'', ''%%protected $pluginsEnabled = [''];%%''. 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.