Dies ist eine alte Version des Dokuments!
Inhaltsverzeichnis
Projekt: DokuWiki
Unternehmens-Wiki: Was man bei der Einführung eines Firmenwikis beachten muss…
===== Einführung und Realisierung eines Unternehmens-Wiki =====
Hier noch zwei YouTube-Videos zur Handhabung...
Webserver/htaccess/Passwortschutz
Webserver/htaccess
DokuWiki
DokuWiki ist eine einfach zu bedienende und äußerst vielseitige Open-Source-Wiki-Software, die keine Datenbank benötigt.
Es wird von den Nutzern wegen seiner sauberen und lesbaren Syntax geliebt. Die einfache Wartung, Sicherung und Integration machen es zu einem Favoriten für Administratoren. Eingebaute Zugriffskontrollen und Authentifizierungs-Connectoren machen DokuWiki besonders nützlich im Unternehmenskontext, und die große Anzahl von Plugins der lebendigen Community ermöglicht eine breite Palette von Anwendungsfällen jenseits eines traditionellen Wikis.
— Roland Otto 21.04.2026 17:42
DokuWiki (Erklärung)
Worum geht es? Dieses ganze Wiki läuft mit DokuWiki. Der Beitrag erklärt in einfachen Worten, was ein Wiki ist, was DokuWiki besonders macht und warum es sich für Schule, Kurs, Verein und Firma gleichermaßen eignet. Zum Nachschlagen und als Einstieg für alle, die selbst ein Wiki aufsetzen wollen.
📌 Auf einen Blick
| Was | Wie |
|---|---|
| Art | freie Wiki-Software (Open Source), seit 2004 |
| Erfinder | Andreas Gohr und die DokuWiki-Gemeinschaft |
| Programmiersprache | PHP – läuft auf fast jedem Webspace und auch auf einem Raspberry Pi |
| Daten | keine Datenbank – jede Seite ist eine einfache Textdatei |
| Rechte | eingebaute Zugriffskontrolle (ACL) für Benutzer und Gruppen |
| Erweiterbar | über tausend Plugins und viele Designs (Templates) |
| Lizenz | GNU GPL Version 2 – kostenlos, auch für Firmen |
📖 1. Was ist überhaupt ein Wiki?
Ein Wiki ist eine Webseite, die man direkt im Browser bearbeiten kann – ohne Programmierkenntnisse. Das bekannteste Beispiel ist Wikipedia. „Wiki“ kommt aus dem Hawaiischen und heißt „schnell“.
| Merkmal | Was bedeutet das im Alltag? |
|---|---|
| Jeder (Berechtigte) kann schreiben | Wissen steckt nicht mehr nur in einem Kopf oder einem Ordner |
| Versionsgeschichte | Jede Änderung wird gespeichert. Ein Fehler? → „Ältere Versionen“ → alte Fassung wiederherstellen |
| Verlinkung | Seiten verweisen aufeinander – es entsteht ein Netz statt eines Stapels |
| Suche | Volltextsuche über alle Seiten |
| Gleichzeitiges Arbeiten | Wer eine Seite bearbeitet, sperrt sie kurz – so überschreibt niemand die Arbeit eines anderen |
Merksatz: Ein Wiki ist ein gemeinsames Notizbuch mit eingebautem Gedächtnis – nichts geht verloren, alles lässt sich zurückdrehen.
📂 2. Die Besonderheit: Textdateien statt Datenbank
Die meisten Wiki- und Webseiten-Programme (z. B. MediaWiki oder WordPress) brauchen eine Datenbank. DokuWiki nicht: Jede Seite liegt als ganz normale Textdatei auf dem Server. Der Seitenname verrät sogar, wo:
Aus projekt-offen:software:dokuwiki wird die Datei data/pages/projekt-offen/software/dokuwiki.txt – jeder Doppelpunkt ist ein Ordner.
Was folgt daraus?
| Vorteil | Warum? |
|---|---|
| 💾 Sicherung ist einfach | Ordner data/ und conf/ kopieren – fertig. Kein Datenbank-Export |
| 🔧 Wenig Technik | Ein Webserver mit PHP genügt. Läuft auf billigem Webspace und auf dem Raspberry Pi im Homelab |
| 👀 Lesbar ohne Wiki | Die Textdateien kann man mit jedem Editor öffnen – auch in 20 Jahren noch |
| 🚚 Umzug in Minuten | Ordner auf den neuen Server kopieren, fertig |
| Nachteil | Wann spielt das eine Rolle? |
|---|---|
| Sehr große Wikis | Bei vielen zehntausend Seiten und sehr vielen gleichzeitigen Schreibern sind Datenbank-Wikis im Vorteil |
| Auswertungen | Abfragen wie „alle Seiten mit Feld X“ gehen nur über Plugins |
Für Schule, Kurs, Verein, Projekt oder Abteilung ist das so gut wie nie ein Problem.
✍️ 3. Schreiben im Wiki – die Syntax
DokuWiki-Seiten werden mit einer einfachen Auszeichnungssprache geschrieben. Man schreibt Text und markiert mit wenigen Zeichen, was fett, eine Überschrift oder ein Link sein soll. (Wer das nicht mag: In diesem Wiki gibt es zusätzlich einen Editor mit Knöpfen wie in Word.)
| Man tippt | Ergebnis |
|---|---|
**fett** | fett |
//kursiv// | kursiv |
__unterstrichen__ | unterstrichen |
''Code'' | Code |
[[projekt-offen:software:dokuwiki|Link auf eine Seite]] | Link auf eine Seite |
* Punkt (zwei Leerzeichen davor) | eine Aufzählung |
====== Überschrift ====== | eine große Überschrift |
^ Kopf ^ Kopf ^ und | Zelle | Zelle | | eine Tabelle wie diese hier |
Die vollständige Übersicht gibt es auf der Seite Formatierungshilfe in diesem Wiki und bei dokuwiki.org.
Tipp zum Lernen: Bei jeder Seite auf „Quelltext anzeigen“ bzw. „Seite bearbeiten“ klicken und schauen, wie etwas gemacht ist. So lernt man die Syntax am schnellsten – auch an dieser Seite.
🏢 4. Wofür man DokuWiki einsetzen kann
| Einsatz | Beispiel |
|---|---|
| 🏫 Schule und Kurs | Unterrichtsmaterial, Anleitungen, Linksammlungen für die Klasse |
| 🏢 Firma | Wissensdatenbank, Einarbeitung neuer Kollegen, Checklisten, Handbücher |
| 🛠️ Projekt | Protokolle, Aufgaben, Entscheidungen – nachvollziehbar mit Versionsgeschichte |
| 🏠 Homelab und privat | eigenes Notizbuch: welcher Dienst läuft wo, wie wurde er eingerichtet? |
| 🌐 Kleine Webseite | DokuWiki als einfaches CMS – wie dieses Wiki |
⚖️ 5. Warum hier DokuWiki – und nicht etwas anderes?
| DokuWiki | MediaWiki (Wikipedia) | WordPress | |
|---|---|---|---|
| Gedacht für | Wissen gemeinsam pflegen | sehr große, offene Wikis | Blogs und Webseiten |
| Datenbank nötig | ❌ nein | ✅ ja | ✅ ja |
| Rechte je Bereich | ✅ eingebaut | nur eingeschränkt | über Plugins |
| Versionsgeschichte | ✅ jede Seite | ✅ jede Seite | eingeschränkt |
| Aufwand für Betrieb | gering | mittel bis hoch | mittel |
Merksatz: DokuWiki ist das „kleine, robuste Werkzeug“: wenig Technik, einfache Sicherung, feine Rechte – genau richtig, wenn ein paar bis ein paar hundert Leute gemeinsam Wissen pflegen.
Worum geht es? Hier steht, wie das EURoBa-Wiki eingerichtet ist: wie Rechte vergeben werden, wie man DokuWiki anpasst und erweitert und worauf man beim Betrieb achten muss. Zum Nachschlagen für mich – und als Vorlage für eigene Wikis.
📌 Auf einen Blick
| Was | Wie hier gelöst |
|---|---|
| Sprache | Deutsch, Anrede mit „du“ |
| Aussehen | Standard-Template „dokuwiki“, ohne Änderungen am Template selbst |
| Bereiche | Namensräume mit der Endung -offen (für alle) und -geschlossen (nur für Gruppen) |
| Erweiterungen | fertige Plugins aus der Gemeinschaft + eigene EURoBa-Plugins |
| Sicherung | vor jeder Änderung: Seiten, Daten, Plugins und Konfiguration kopieren |
🔐 1. Wer darf was? – Zugriffsrechte (ACL)
ACL heißt Access Control List, auf Deutsch Zugriffsliste. Darin steht für jeden Bereich (Namensraum) und jede Gruppe, was erlaubt ist. Die Stufen bauen aufeinander auf:
Wer „Anlegen“ darf, darf auch bearbeiten und lesen. Die Tabelle zeigt ein vereinfachtes Beispiel mit erfundenen Gruppen.
So funktioniert das in der Praxis:
- Namensräume sind Ordner. Rechte gelten für einen Namensraum samt allem, was darin liegt (
projekt-offen:*). - Die genaueste Regel gewinnt. Gibt es eine Regel für
projekt-offen:*und eine fürprojekt-offen:software:*, zählt für Seiten untersoftwaredie zweite. - Gruppen statt Einzelpersonen. Ein neuer Kursteilnehmer kommt in die Gruppe seines Kurses – und hat sofort die richtigen Rechte.
- Der Name verrät den Schutz. Endet ein Namensraum auf
-offen, ist er öffentlich, bei-geschlossennicht. Man sieht schon am Link, ob ein Inhalt nach außen gehört.
Sicherheitsregel: So wenig Rechte wie möglich. Besucher bekommen nur „Lesen“, und nur dort, wo es gewollt ist. Schreiben dürfen nur angemeldete Benutzer in ihren Bereichen. Der Admin-Bereich bleibt für alle anderen gesperrt.
🎨 2. DokuWiki anpassen – vier Ebenen
Man kann DokuWiki auf vier Ebenen verändern, von „ein Häkchen setzen“ bis „selbst programmieren“:
| Ebene | Was man tut | Beispiel in diesem Wiki | Wissen nötig |
|---|---|---|---|
| 1. Konfiguration | Einstellungen im Admin-Bereich | Sprache „du“, Titel, Rechte einschalten | keins |
| 2. Plugins | fertige Erweiterungen installieren | Akkordeons, farbige Kästen (WRAP), Schlagwörter (Tags), Kommentare, Editor mit Knöpfen | wenig |
| 3. Templates | das Aussehen wählen und mit eigenem CSS anpassen | Standard-Template, kleine Anpassungen im Plugin-CSS | etwas CSS |
| 4. Eigene Plugins | selbst in PHP programmieren | Weisheit des Tages, Sternzeichen des Tages, automatische Jahre | PHP |
Merksatz: So weit oben wie möglich bleiben. Was mit einer Einstellung geht, braucht kein Plugin – und was ein fertiges Plugin kann, muss man nicht selbst programmieren. Eigene Plugins lohnen sich erst, wenn es nichts Passendes gibt.
Wie eigene Plugins aufgebaut sind, zeigen die Beiträge unter Spezial-Programmierungen weiter unten auf dieser Seite. Weitere Anregungen gibt es bei den Tipps und Tricks auf dokuwiki.org.
🛡️ 3. Sicher betreiben – die Checkliste
| Aufgabe | Warum? | Wie oft? |
|---|---|---|
💾 Sicherung von data/ und conf/ | Ohne Sicherung kein Zurück – bei Fehlern, Angriffen oder defektem Server | vor jeder Änderung, sonst regelmäßig |
| ⬆️ DokuWiki aktualisieren | Updates schließen Sicherheitslücken. DokuWiki meldet neue Versionen im Admin-Bereich | sobald ein Update erscheint |
| 🧩 Plugins prüfen | Alte, nicht mehr gepflegte Plugins sind ein Risiko | bei jedem Update |
| 🔒 HTTPS | Passwörter dürfen nie unverschlüsselt übertragen werden | immer |
| 👥 Benutzer aufräumen | Wer nicht mehr dabei ist, braucht keinen Zugang | z. B. am Kursende |
| 🚫 Nichts Geheimes auf -offen-Seiten | Öffentliche Seiten werden auch von Suchmaschinen gelesen | bei jeder neuen Seite |
Gut zu wissen: Weil DokuWiki nur aus Dateien besteht, ist eine Sicherung ein einfacher Kopiervorgang – per FTP vom Webspace oder per Skript vom Raspberry Pi. Zurückspielen heißt: die Dateien wieder an ihren Platz kopieren.
🎓 4. Ideen für den Unterricht
- Klassen-Wiki: Jede Gruppe pflegt eine Seite zu einem Thema. Über „Ältere Versionen“ sieht man, wer was beigetragen hat.
- Syntax-Rallye: Eine Seite mit Überschrift, Tabelle, Liste, Bild und Link nachbauen – nur mit der Formatierungshilfe.
- Rechte-Planspiel: Für eine erfundene Firma Namensräume und Gruppen planen: Wer darf was lesen und schreiben?
- Homelab-Projekt: DokuWiki auf einem Raspberry Pi installieren, Sicherung einrichten und das Zurückspielen üben.
- Plugin-Detektive: Herausfinden, mit welchen Plugins diese Seite gebaut ist (Tipp: Quelltext anzeigen).
⚖️ Lizenz
DokuWiki © 2004–2026 Andreas Gohr und die DokuWiki-Gemeinschaft.
DokuWiki ist unter der GNU General Public License Version 2 veröffentlicht. Das heißt in einfachen Worten:
| Man darf | Bedingung |
|---|---|
| DokuWiki kostenlos nutzen – privat, in der Schule und in der Firma | keine |
| den Programmcode lesen und verändern | keine |
| DokuWiki (auch verändert) weitergeben | auch dann wieder unter der GPL und mit Quellcode |
Wer sich unsicher ist, findet Antworten in den Fragen zur Lizenz. Wer DokuWiki beruflich nutzt, kann das Projekt mit einer Spende unterstützen.
Achtung, zwei verschiedene Dinge: Die GPL gilt für die Software DokuWiki. Für die Inhalte dieses Wikis (Texte, Bilder) gilt das Urheberrecht der jeweiligen Autoren.
🤝 Mitmachen
Open Source lebt davon, dass Nutzer etwas zurückgeben. Dafür muss man nicht programmieren können:
- Fehler und Wünsche melden oder Fragen im DokuWiki-Forum beantworten
- bei der Übersetzung der Oberfläche helfen
- Anleitungen und Plugin-Beschreibungen auf dokuwiki.org verbessern
- eigene Erfahrungen teilen – so wie diese Seite
— Roland Otto 11.11.2026
Spezial-Programmierungen
==== Überblick: Was war das Ziel? ====
Du wolltest in deiner DokuWiki‑Seite einen Satz wie
„Erfahrung von über 15 Jahren Unterricht bei der VHS.“
nicht jedes Jahr manuell anpassen, sondern automatisch berechnen lassen, ausgehend von einem festen Startdatum (z. B. 01.09.2010).
Konkret sollte also DokuWiki selbst ausrechnen:
- wie viele volle Jahre seit einem Startdatum vergangen sind
- und diese Zahl im Text anzeigen
Beispiel:
''Erfahrung: 15 Jahre Unterricht bei der VHS.''
statt:
''Erfahrung: {{vhsexperience>2010-09-01}}''
===== 1. Erste Idee: Fertige Plugins wie „date“ oder „year“ =====
==== 1.1 Das „date“-Plugin ====
Die ursprüngliche Idee war, ein vorhandenes Plugin zu nutzen, z. B. das Date‑Plugin.
Dieses Plugin bietet u. a. eine Syntax, um Datumsdifferenzen zu berechnen, z. B.:
''{{date>diff:2010-09-01:now:years}}''
Die Idee dahinter:
2010-09-01= Startdatumnow= aktuelles Datumyears= Ausgabe in Jahren
Erwartetes Ergebnis wäre z. B.:
''15''
oder eingebettet:
''Erfahrung: {{date>diff:2010-09-01:now:years}} Jahre Unterricht bei der VHS.''
==== 1.2 Warum das bei dir nicht funktioniert hat ====
In deinem Wiki wurde der Ausdruck aber nicht berechnet, sondern quasi „auseinandergerissen“ angezeigt, etwa so:
''Erfahrung: diff:2010-09-01:now:years Jahre Unterricht bei der VHS.''
Das ist ein typisches Zeichen dafür, dass:
- die äußere Plugin‑Syntax
...nicht erkannt wurde - das Plugin nicht installiert oder nicht geladen ist
- DokuWiki den Inhalt daher einfach als normalen Text behandelt
Zusätzlich ist das Date‑Plugin relativ alt und kann mit neueren DokuWiki‑Versionen Probleme haben (Inkompatibilität, nicht mehr gepflegt, etc.).
===== 2. Alternative: Year‑Plugin (Jahresdifferenz über das Jahr) =====
Eine einfachere Alternative ist das Year‑Plugin, das nur mit Jahreszahlen arbeitet.
Beispiel:
''{{year>2010}}''
Das berechnet:
- aktuelles Jahr – 2010
Also z. B. im Jahr 2026:
''{{year>2010}} → 16''
Eingebettet:
''Erfahrung: {{year>2010}} Jahre Unterricht bei der VHS.''
Vorteil:
- sehr simpel
- oft kompatibler als komplexere Plugins
Nachteil:
- rechnet nur mit Jahren, nicht mit exakten Daten
- berücksichtigt nicht, ob der Jahrestag im aktuellen Jahr schon erreicht ist
===== 3. Endgültige Lösung: Eigenes Plugin „EURoBa-VHS-Experience“ =====
Stand 11.11.2026: Das Plugin hieß zuerst vhsexperience. Seit dem 10.11.2026 heißt es eurobavhsexperience (angezeigter Name: EURoBa-VHS-Experience), denn alle selbstgeschriebenen Plugins dieses Wikis beginnen mit „EURoBa“. Dabei wurde auch ein Rechenfehler behoben (siehe 6.4). Die Schreibweise im Wiki ist unverändert – alle Seiten funktionieren weiter.
Da die fertigen Plugins nicht zuverlässig liefen bzw. nicht installiert waren, fiel die Entscheidung für eine eigene, maßgeschneiderte Lösung: ein kleines, eigenes Plugin.
Ziel:
- Syntax im Wiki: {{vhsexperience>2010-09-01}}
- Ausgabe im HTML:
15 Jahre
===== 4. Wie DokuWiki-Plugins grundsätzlich aufgebaut sind =====
==== 4.1 Plugin-Ordner ====
Plugins liegen in:
lib/plugins/<pluginname>/
Wichtig:
<pluginname>darf nur Kleinbuchstaben a–z und Ziffern 0–9 enthalten- keine Unterstriche, keine Bindestriche, keine Großbuchstaben
- gültig:
eurobavhsexperience– ungültig:vhs_experience,EURoBa-VHS - Der angezeigte Name (in
plugin.info.txt) darf dagegen frei gewählt werden, z. B. „EURoBa-VHS-Experience“.
==== 4.2 plugin.info.txt ====
In jedem Plugin-Ordner liegt eine Datei plugin.info.txt. Sie sagt DokuWiki, wie das Plugin heißt, wer es geschrieben hat und wofür es gedacht ist:
base eurobavhsexperience
author Roland Otto
email Roland@EURoBa.de
date 2026-11-10
name EURoBa-VHS-Experience
desc Berechnet automatisch die vollen Jahre seit einem Startdatum. Syntax: {{vhsexperience>JJJJ-MM-TT}}
url https://euroba.de
Wichtig: base muss exakt dem Ordnernamen entsprechen.
==== 4.3 syntax.php ====
Für eine eigene Syntax (also neue {{…}}-Konstrukte) braucht man eine syntax.php. Darin steckt eine PHP-Klasse, die von SyntaxPlugin erbt (früher hieß die Elternklasse DokuWiki_Syntax_Plugin – das funktioniert noch, ist aber veraltet).
===== 5. Das konkrete Plugin =====
==== 5.1 Ordnerstruktur ====
lib/
plugins/
eurobavhsexperience/
plugin.info.txt
syntax.php
==== 5.2 Der ursprüngliche Fehler: Ungültiger Pluginname ====
Der Ordner hieß zuerst lib/plugins/vhs_experience/. DokuWiki meldete:
Plugin name 'vhs_experience' is not a valid plugin name, only the characters a-z and 0-9 are allowed.
Der Unterstrich ist nicht erlaubt. Lösung: Ordner umbenennen, base in plugin.info.txt anpassen, Klassenname in syntax.php anpassen. Genau so lief später auch die Umbenennung in eurobavhsexperience.
===== 6. Funktionsweise des Plugins im Detail =====
==== 6.1 Grundgerüst der Klasse ====
use dokuwiki\Extension\SyntaxPlugin; if (!defined('DOKU_INC')) die(); // Schutz: Datei nur über DokuWiki aufrufbar class syntax_plugin_eurobavhsexperience extends SyntaxPlugin { // ... }
syntax_plugin_+ Ordnername → so findet DokuWiki die Klasse- Die Zeile mit
DOKU_INCverhindert, dass jemand die Datei direkt im Browser aufruft
==== 6.2 Typ und Verhalten ====
public function getType() { return 'substition'; } // ersetzt Wiki-Text durch eigene Ausgabe public function getPType() { return 'normal'; } // steht im Fließtext, kein eigener Absatz public function getSort() { return 155; } // Reihenfolge gegenüber anderen Syntax-Regeln
(Ja, substition ist bei DokuWiki tatsächlich so geschrieben – ohne zweites „tu“.)
==== 6.3 Erkennen der eigenen Syntax ====
public function connectTo($mode) { $this->Lexer->addSpecialPattern('\{\{vhsexperience>.*?\}\}', $mode, 'plugin_eurobavhsexperience'); }
- Damit sagt das Plugin zu DokuWiki: „Findest du im Text etwas wie {{vhsexperience>…}}, bin ich zuständig.“
- Der reguläre Ausdruck:
\{\{vhsexperience>am Anfang,.*?= beliebiger Inhalt (so wenig wie möglich),\}\}am Ende - Die Schreibweise
vhsexperienceim Wiki ist bewusst gleich geblieben – nur der dritte Wert (der interne Name) heißt jetztplugin_eurobavhsexperience.
==== 6.4 Zerlegen der Syntax: handle() – und ein lehrreicher Fehler ====
public function handle($match, $state, $pos, Doku_Handler $handler) { // "{{vhsexperience>" abschneiden (vorne) und "}}" (hinten) return ['startdate' => trim(substr($match, strlen('{{vhsexperience>'), -2))]; }
$match enthält z. B. {{vhsexperience>2010-09-01}} – übrig bleibt 2010-09-01.
Der Fehler in der ersten Fassung: Dort stand substr($match, 17, -2). {{vhsexperience> hat aber nur 16 Zeichen! Dadurch wurde die erste Ziffer der Jahreszahl mit abgeschnitten: aus 2010-09-01 wurde 010-09-01. Dass trotzdem meist die richtige Zahl herauskam, war reiner Zufall (PHP deutet „010“ großzügig).
Lehre: Keine Zahlen von Hand abzählen – strlen('{{vhsexperience>') rechnet die Länge selbst aus und stimmt immer.
==== 6.5 Ausgabe erzeugen: render() ====
public function render($mode, Doku_Renderer $renderer, $data) { if ($mode !== 'xhtml') return false; // nur für HTML-Seiten // Die Zahl ändert sich mit der Zeit → Seite NICHT fertig zwischenspeichern $renderer->info['cache'] = false; $start = strtotime($data['startdate']); // "2010-09-01" → Zeitstempel if (!$start) { $renderer->doc .= '<span class="error">Ungültiges Datum</span>'; return true; } $jetzt = time(); // Differenz der Jahreszahlen … $jahre = (int)date('Y', $jetzt) - (int)date('Y', $start); // … minus 1, falls der Jahrestag in diesem Jahr noch nicht erreicht ist $jahrestag = strtotime(date('Y', $jetzt) . '-' . date('m-d', $start)); if ($jetzt < $jahrestag) $jahre--; $renderer->doc .= $jahre . ' Jahre'; return true; }
Schritt für Schritt:
- Modus prüfen: Nur im HTML-Modus wird etwas ausgegeben (nicht z. B. im RSS-Feed).
- Zwischenspeicher abschalten: DokuWiki speichert fertige Seiten normalerweise zwischen. Ohne
$renderer→info['cache'] = falsestünde nach dem Jahrestag weiter die alte Zahl da – bis jemand die Seite bearbeitet. (Auch das fehlte in der ersten Fassung.) - Startdatum einlesen:
strtotime()wandelt den Text in einen Zeitstempel um; klappt das nicht → „Ungültiges Datum“. - Jahresdifferenz: z. B. 2026 − 2010 = 16
- Jahrestag prüfen: Jahrestag 2026 = 2026-09-01. Liegt heute davor, wird ein Jahr abgezogen → 15.
- Ausgabe:
15 Jahre
===== 7. Verwendung im Wiki =====
Erfahrung: {{vhsexperience>2010-09-01}} Unterricht bei der VHS.
ergibt (Stand November 2026):
Erfahrung: 16 Jahre Unterricht bei der VHS.
===== 8. Didaktische Kernaussagen =====
- Ziel: Wiederkehrende Angaben (z. B. „X Jahre Erfahrung“) sollen sich selbst aktualisieren.
- Fertige Plugins wie
dateoderyearkönnen das teilweise, sind aber manchmal nicht installiert, nicht kompatibel oder eingeschränkt. - Eigenes Plugin: volle Kontrolle, klein, überschaubar, gut dokumentierbar – und ein gutes Beispiel, wie DokuWiki-Syntax-Plugins funktionieren.
- Wichtige technische Punkte:
- Plugin-Ordnername: nur
a-zund0-9; angezeigter Name frei wählbar baseinplugin.info.txt= Ordnername- Klassenname
syntax_plugin_<ordnername> connectTo()registriert die Syntax,handle()zerlegt sie,render()erzeugt die Ausgabe- Zeichen nicht von Hand abzählen (
strlen()benutzen) - Zeitabhängige Ausgaben: Zwischenspeicher abschalten
- Umbenennen ohne Schaden: Ordner,
baseund Klassenname können sich ändern – solange das Muster inconnectTo()gleich bleibt, merken die Wiki-Seiten nichts davon.
— Roland Otto aktualisiert 11.11.2026
Worum geht es? Über jeder Seite dieses Wikis steht ein aufklappbarer Kasten „💡 Weisheit des Tages“: ein Spruch, der Autor mit Bild und eine Kurzbiografie. Mit dem Würfel „🎲 weitere Weisheiten?“ holt man sich einen zufälligen weiteren Spruch.
Dieser Beitrag erzählt, wie daraus im November 2026 ein sauberes, eigenes DokuWiki-Plugin wurde – mit Verwaltungsseite, Sicherungen und einem Wikipedia-Knopf. Zum Nachschlagen für mich und als Inspiration für eigene Projekte.
So sieht der Kasten aus: links der Spruch, rechts Bild und Würfel, unten die aufklappbare Kurzbiografie.
📌 Auf einen Blick
| Was | Wie |
|---|---|
| Plugin-Ordner | lib/plugins/eurobaweisheiten/ (angezeigt als „EURoBa-Weisheiten“) |
| Daten | eine Datei: data/meta/weisheiten.json (rund 360 Sprüche, 180 Autoren) |
| Pflege | Admin → „💡 EURoBa-Weisheiten verwalten“ |
| Sicherheit | nur Admins dürfen ändern, Schutz gegen fremde Formulare, automatische Sicherungen |
| Besonderheit | Bild + Biografie per Knopfdruck aus der deutschen Wikipedia |
🧩 1. Die Ausgangslage – warum überhaupt neu bauen?
Früher steckte alles in der Datei main.php des Wiki-Designs (Templates): Sprüche, Sternzeichen, eine eigene Verwaltung – zusammen 92 KB Programmcode in einer Datei, die eigentlich nur das Aussehen der Seite festlegen soll. Das hat funktioniert, aber:
| Problem | Was passierte |
|---|---|
| Template-Update | Jedes Update von DokuWiki überschrieb main.php → alles weg |
| Zeilenumbruch | Ein Spruch pro Zeile in einer Textdatei. Ein Gedicht mit Zeilenumbrüchen wurde „verschluckt“ |
| Dubletten | „Goethe“ und „Johann Wolfgang von Goethe“ als zwei verschiedene Autoren |
| Zähler | Bei vielen gleichzeitigen Besuchern überschrieben sich die Aufrufzähler gegenseitig |
| Ladezeit | Alle 364 Sprüche samt Biografien steckten in jeder Seite (ca. 600 KB) |
| Sicherheit | Kein Schutz dagegen, dass eine fremde Webseite heimlich Formulare abschickt |
Merksatz: Ein Template ist für das Aussehen da, ein Plugin für Funktionen. Wer das trennt, übersteht jedes Update.
🗂️ 2. Der Aufbau des Plugins
Ein DokuWiki-Plugin ist einfach ein Ordner mit festen Dateinamen. DokuWiki erkennt am Dateinamen, wofür eine Datei zuständig ist:
lib/plugins/eurobaweisheiten/ ├── plugin.info.txt ← Steckbrief: Name, Autor, Beschreibung ├── action.php ← hängt sich in DokuWiki ein: Widget auf jede Seite, Würfel-Antwort ├── helper.php ← ALLE Lese- und Schreibzugriffe auf die Daten an EINER Stelle ├── admin.php ← die Verwaltungsseite im Admin-Bereich ├── Wikipedia.php ← holt Vorschläge aus Wikipedia (speichert selbst nichts) ├── script.js ← Würfel-Knopf im Browser ├── style.css ← Aussehen von Widget und Verwaltung ├── conf/ ← Einstellungen (z. B. "Aufrufe zählen") └── lang/ ← Texte auf Deutsch (du/Sie) und Englisch
Wer redet mit wem? Alle Wege zu den Daten führen durch helper.php – dort sitzen Sperre und Sicherung.
Die Daten liegen bewusst nicht im Plugin-Ordner, sondern in DokuWikis Datenordner. So kann man das Plugin austauschen, ohne Daten zu verlieren:
Datei in data/meta/ | Inhalt | Wie oft geschrieben? |
|---|---|---|
weisheiten.json | alle Sprüche und Autoren | nur beim Speichern in der Verwaltung |
weisheiten_tag.json | welcher Spruch heute dran ist + wann jeder zuletzt kam | einmal pro Tag |
weisheiten_stats.json | Aufrufzähler je Spruch | bei jedem Seitenaufruf |
weisheiten_backup/ | die letzten 10 Fassungen von weisheiten.json | automatisch vor jeder Änderung |
Warum drei Dateien? Die große Datei soll nicht bei jedem Seitenaufruf neu geschrieben werden – genau das hatte den alten Zähler kaputt gemacht.
📄 3. Die Datendatei – JSON statt Textzeilen
JSON ist ein einfaches Textformat für strukturierte Daten. Jeder Spruch hat eine feste Nummer (ID), die sich nie ändert, und verweist über einen Schlüssel auf seinen Autor:
{ "autoren": { "albert_einstein": { // Schlüssel: klein, ohne Leerzeichen/Umlaute "name": "Albert Einstein", "anzeige": "Albert Einstein (1879–1955)", // so erscheint er im Widget "bio": "Albert Einstein war ein theoretischer Physiker. …", "bild": "albert_einstein.jpg", // liegt in data/media/autoren/ "aliase": ["A. Einstein"], // andere Schreibweisen → kein Doppel-Autor "quelle_bio": { "titel": "Albert Einstein", "lizenz": "CC BY-SA 4.0", "url": "https://de.wikipedia.org/…" } } }, "sprueche": [ { "id": 1, "autor": "albert_einstein", "text": "Phantasie ist wichtiger als Wissen." } ] }
Vorteil gegenüber „ein Spruch pro Zeile“: Ein Text darf jetzt beliebig viele Zeilenumbrüche enthalten (Gedichte!), und Felder wie „Aliase“ oder „Quelle“ lassen sich jederzeit ergänzen, ohne alte Daten umzubauen.
🪝 4. Wie kommt das Widget auf jede Seite? – „Haken“ (Events)
DokuWiki ruft an bestimmten Stellen alle Plugins auf, die sich dafür angemeldet haben. Diese Stellen heißen Events (Ereignisse). Unser Plugin meldet sich für „kurz bevor der Seiteninhalt ausgegeben wird“ an und setzt sein HTML davor. Die main.php braucht dafür keine einzige Zeile Code mehr (sie schrumpfte von 92 KB auf 2 KB).
// action.php – Anmeldung bei DokuWiki public function register(EventHandler $controller) { // "Bevor der Seiteninhalt angezeigt wird, rufe bitte widgetEinfuegen() auf" $controller->register_hook('TPL_CONTENT_DISPLAY', 'BEFORE', $this, 'widgetEinfuegen'); // "Kommt eine unbekannte AJAX-Anfrage, frag mich – vielleicht ist es der Würfel" $controller->register_hook('AJAX_CALL_UNKNOWN', 'BEFORE', $this, 'ajaxZufall'); } public function widgetEinfuegen(Event $event) { global $ACT; if ($ACT !== 'show') return; // nur beim normalen Anzeigen, nicht beim Bearbeiten $h = $this->loadHelper('eurobaweisheiten'); // den Daten-Helfer holen $s = $h->spruchDesTages(); if (!$s) return; // keine Daten → kein Widget, aber auch kein Fehler // $event->data ist das fertige HTML der Seite – unser Kasten kommt davor $event->data = $this->html($h->fuerAnzeige($s)) . $event->data; }
📅 5. Welcher Spruch ist heute dran?
Ziel: Jeder Spruch soll der Reihe nach drankommen, ohne Wiederholung – und ein neu eingetragener Spruch soll die Reihenfolge nicht durcheinanderbringen. Die Lösung ist erstaunlich einfach: Nimm den Spruch, der am längsten nicht gezeigt wurde.
// helper.php (vereinfacht) $heute = date('Y-m-d'); // z. B. "2026-11-11" if ($tag['datum'] === $heute) { // heute schon entschieden? return $this->spruchNachId($tag['id']); // → denselben Spruch wie vorhin } $bester = null; foreach ($daten['sprueche'] as $s) { $zuletzt = $verlauf[$s['id']] ?? ''; // '' = noch nie gezeigt // Texte wie "2026-03-01" kann man direkt vergleichen: früheres Datum = kleiner if ($bester === null || strcmp($zuletzt, $besterWert) < 0) { $bester = $s; $besterWert = $zuletzt; } } $verlauf[$bester['id']] = $heute; // merken: heute gezeigt // … und in weisheiten_tag.json speichern
Trick zum Merken: Ein Datum im Format JJJJ-MM-TT lässt sich wie ein Wort alphabetisch sortieren – und ist dann automatisch zeitlich sortiert. Deshalb nutzen Programmierer dieses Format so gern (auch für Dateinamen von Sicherungen!).
🎲 6. Der Würfel – AJAX statt „alles mitschicken“
Früher lagen alle Sprüche unsichtbar in jeder Seite, damit der Würfel einen auswählen konnte. Heute fragt der Browser erst beim Klick beim Server nach einem einzigen Spruch. Das nennt man AJAX (der Browser lädt Daten nach, ohne die Seite neu zu laden).
// script.js – im Browser fetch(DOKU_BASE + 'lib/exe/ajax.php', { // DokuWikis Adresse für AJAX-Anfragen method: 'POST', headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, body: 'call=weisheiten_zufall' // "Ich hätte gern einen Zufallsspruch" }) .then(function (antwort) { return antwort.json(); }) .then(function (spruch) { // textContent statt innerHTML: Text wird NIE als HTML/JavaScript ausgeführt box.querySelector('.wh-text').textContent = spruch.text; });
// action.php – auf dem Server public function ajaxZufall(Event $event) { if ($event->data !== 'weisheiten_zufall') return; // nicht unsere Anfrage → ignorieren $event->preventDefault(); // DokuWiki: "erledigt, kein Fehler" header('Content-Type: application/json; charset=utf-8'); echo json_encode($h->fuerAnzeige($h->zufall())); // genau EIN Spruch als JSON }
Ergebnis: Jede Seite ist rund 150 KB leichter.
🔒 7. Der Zähler – warum „Dateisperre“ wichtig ist
Stell dir vor, zwei Besucher öffnen gleichzeitig eine Seite. Beide lesen „Zähler = 41“, beide rechnen +1, beide schreiben „42“. Ein Aufruf ist verloren. Bei vielen Besuchern wird die Datei sogar kaputt. Die Lösung heißt flock (file lock) – wie das Schloss an einer Toilettentür: Wer drin ist, schließt ab; der Nächste wartet kurz.
$datei = fopen($pfad, 'c+'); // öffnen zum Lesen UND Schreiben (anlegen, falls nötig) flock($datei, LOCK_EX); // abschließen – andere warten jetzt $zaehler = json_decode(stream_get_contents($datei), true) ?: []; $zaehler[$id] = ($zaehler[$id] ?? 0) + 1; // eins dazuzählen ftruncate($datei, 0); // Datei leeren … rewind($datei); // … an den Anfang springen … fwrite($datei, json_encode($zaehler));// … neu schreiben flock($datei, LOCK_UN); // aufschließen fclose($datei);
💾 8. Sicher speichern – fünf Schritte gegen Datenverlust
Jede Änderung in der Verwaltung (Spruch anlegen, Autor ändern, löschen …) läuft durch eine einzige Funktion. So kann kein Weg die Sicherheitsschritte „vergessen“:
- Sperre setzen – zwei Admins gleichzeitig? Der zweite wartet.
- Daten frisch lesen – nicht mit einem veralteten Stand weiterarbeiten.
- Prüfen, ob die Datei in Ordnung war – ist sie kaputt, wird NICHT gespeichert (sonst würde eine leere Datei alle Sprüche ersetzen!).
- Sicherung anlegen – Kopie nach
weisheiten_backup/, die letzten 10 bleiben. - Erst in eine Zwischendatei schreiben, dann umbenennen – Umbenennen geschieht „in einem Rutsch“. Ein Besucher sieht also immer entweder die alte oder die neue Datei, nie eine halb geschriebene.
// Schritt 5 im Detail $tmp = $pfad . '.tmp'; file_put_contents($tmp, json_encode($daten, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT), LOCK_EX); rename($tmp, $pfad); // ersetzt die alte Datei in einem einzigen Schritt
🛠️ 9. Die Verwaltungsseite
Admin → „💡 EURoBa-Weisheiten verwalten“ hat drei Reiter:
| Reiter | Funktionen |
|---|---|
| Sprüche | neu anlegen, suchen, bearbeiten, löschen. Beim Anlegen warnt sie vor gleichen (100 %) oder ähnlichen (ab 85 %) Sprüchen. |
| Autoren | Name, Anzeigename, Biografie (mehrzeilig), Bild, andere Schreibweisen (Aliase). Dazu: 🔎 Wikipedia-Vorschlag, 🔀 Zusammenführen, 🗑️ Löschen samt Sprüchen |
| Prüfen | doppelte/ähnliche Sprüche, mögliche Autoren-Dubletten (mit Knopf „zusammenführen“), Autoren ohne Biografie oder Bild |
Die Verwaltung: oben die Kennzahlen (gelb würde „Lücken“ melden), darunter die Reiter, „Neuer Spruch“, Suche und die Liste – neueste zuerst, mit ✏️ Bearbeiten und 🗑️ Löschen.
Wie erkennt man „ähnliche“ Sprüche? Beide Texte werden vereinfacht (klein geschrieben, nur Buchstaben und Ziffern), dann vergleicht PHPs Funktion similar_text(), wie viel Prozent übereinstimmen:
// "Phantasie ist wichtiger als Wissen!" → "phantasie ist wichtiger als wissen" preg_match_all('/[\p{L}\p{N}]+/u', mb_strtolower($text, 'UTF-8'), $woerter); $einfach = implode(' ', $woerter[0]); similar_text($einfachNeu, $einfachAlt, $prozent); // $prozent z. B. 92.5 if ($prozent >= 85) { /* Warnung: "Ähnlicher Spruch vorhanden" */ }
Aliase: Tippt man bei einem neuen Spruch „Mark Aurel“ ein und beim Autor „Marcus Aurelius“ steht dieser Alias, wird der Spruch automatisch richtig zugeordnet – statt einen Doppel-Autor anzulegen.
Zusammenführen: Gibt es doch einmal „Goethe“ und „Johann Wolfgang von Goethe“, wandern mit einem Klick alle Sprüche zum einen Autor, der andere Name wird dort Alias, Biografie und Bild füllen nur Lücken.
Muster „Post/Redirect/Get“: Nach jedem Speichern leitet die Seite auf sich selbst um. Drückt man danach im Browser „Neu laden“, wird nicht noch einmal gespeichert. Einfacher Trick, der doppelte Einträge verhindert.
🔎 10. Bild und Biografie aus Wikipedia
Beim Autor gibt es den Knopf „🔎 Vorschlag aus Wikipedia holen“. Ablauf:
- Der Server fragt die Schnittstelle (API) der deutschen Wikipedia: Einleitung des Artikels, Artikelbild, Kurzbeschreibung.
- Bei Wikidata (der Datenbank hinter Wikipedia) fragt er: Ist das ein Mensch? und Wann geboren/gestorben? So wird z. B. „Ägypten“ als „keine Person“ erkannt, und der Anzeigename wird „Albert Einstein (1879–1955)“.
- Zum Bild holt er Urheber und Lizenz. Bilder ohne freie Lizenz werden gar nicht erst vorgeschlagen.
- Es erscheint eine Vorschau – es ist noch nichts gespeichert. Text und Anzeigename kann man noch ändern, per Häkchen wählt man aus.
- Erst „✅ Ausgewähltes übernehmen“ lädt das Bild herunter und speichert. Unter der Biografie erscheint ein dezenter Quellenhinweis („Quelle: Wikipedia – … (CC BY-SA 4.0)“), denn das verlangt die Lizenz.
Die Vorschau (orange Kante = noch nicht gespeichert): Anzeigename mit Lebensdaten aus Wikidata, bearbeitbare Biografie, Bild mit Urheber und Lizenz, Häkchen zur Auswahl – und unten Alternativen, falls es die falsche Person ist.
// Eine einzige Anfrage holt Einleitung, Bild und Wikidata-Nummer $antwort = $this->api('https://de.wikipedia.org/w/api.php', [ 'action' => 'query', 'titles' => 'Albert Einstein', 'redirects' => 1, // "Goethe" → "Johann Wolfgang von Goethe" 'prop' => 'extracts|pageimages|pageprops|info|description', 'exintro' => 1, // nur die Einleitung 'explaintext' => 1, // reiner Text statt HTML 'pilicense' => 'free', // nur frei lizenzierte Bilder 'ppprop' => 'disambiguation|wikibase_item', // Begriffsklärung? Wikidata-Nummer? ]);
Zwei lehrreiche Stolpersteine:
1. „Too Many Requests“ (HTTP 429): Wikipedia bremst Programme, die zu schnell hintereinander fragen. Lösung: höchstens eine Anfrage pro Sekunde, und bei 429 die vom Server genannte Wartezeit abwarten (Kopfzeile Retry-After).
2. Lokal ging es, auf dem Server nicht: Der Hoster hatte PHP so eingestellt (arg_separator.output = „&“), dass beim Zusammenbauen der Adresse & statt & zwischen die Parameter kam. Wikipedia verstand format=json nicht mehr und schickte eine HTML-Seite. Gefunden mit einem kleinen, vorübergehenden Test-Plugin; behoben mit einer einzigen Angabe:
http_build_query($parameter, '', '&'); // Trennzeichen AUSDRÜCKLICH angeben
Lehre: Nie darauf verlassen, dass der Server genauso eingestellt ist wie der eigene Rechner.
🛡️ 11. Sicherheit – die fünf wichtigsten Regeln im Code
| Regel | Umsetzung | Wogegen? |
|---|---|---|
| Nur Berechtigte | forAdminOnly() liefert true | Fremde ändern Sprüche |
| Formular-Token | jedes Formular trägt ein geheimes Token, checkSecurityToken() prüft es | eine fremde Webseite schickt heimlich Formulare in deinem Namen ab („CSRF“) |
| Ausgabe entschärfen | jeder Text läuft durch hsc() (PHP) bzw. textContent (JavaScript) | jemand schreibt <script> in einen Spruch („XSS“) |
| Feste Adressliste | Bilder nur von upload.wikimedia.org / thumb.wikimedia.org, max. 3 MB, nur JPG/PNG/WebP | der Server wird missbraucht, um beliebige Adressen abzurufen |
| Echten Dateityp prüfen | finfo schaut in die Datei, nicht auf die Endung | ein Programm wird als „bild.jpg“ getarnt |
📱 12. Ein Handy-Problem – und warum !important gefährlich ist
Am Handy war das Eingabefeld für Sprüche plötzlich winzig. Ursache: Das DokuWiki-Design enthält für kleine Bildschirme
.dokuwiki textarea { width: auto !important; } /* "auto" = Standardgröße */
!important überstimmt alle normalen Regeln. Die Lösung im Plugin – ohne das Design zu verändern (sonst wäre sie beim nächsten Update wieder weg):
/* spezifischer (.dokuwiki .wha form textarea) UND !important → gewinnt */ .dokuwiki .wha form textarea { display: block; /* Beschriftung steht darüber, nicht daneben */ width: 100% !important; /* volle Breite */ resize: vertical; /* unten rechts nach unten aufziehbar */ } @media (max-width: 700px) { .dokuwiki .wha form textarea { font-size: 16px; } /* iPhone zoomt sonst beim Antippen */ }
Links vorher: Das Spruch-Feld ist geschrumpft und steht neben seiner Beschriftung. Rechts nachher: volle Breite, unten rechts zum Aufziehen.
🧪 13. Testen, ohne den Server zu gefährden
- Ein portables PHP (nur ein Ordner, nicht installiert) liegt im Projekt. Damit laufen Testskripte direkt auf dem PC:
php.exe test_admin.php→ „48 OK, 0 Fehler“. - DokuWiki-Funktionen werden im Test durch Attrappen ersetzt (kleine Ersatzfunktionen). Auch Wikipedia wird im Test nachgebildet – so laufen die Tests ohne Internet und in Sekunden.
- Vor jedem Hochladen: Syntaxprüfung (
php -l), automatische Sicherung des Server-Stands, danach Kontrolle der Startseite.
🎓 14. Kernaussagen und Ideen für eigene Projekte
- Trennen: Aussehen (Template) ≠ Funktion (Plugin) ≠ Daten (data-Ordner).
- Eine Stelle für alle Datenzugriffe (hier
helper.php) – dann gibt es nur einen Ort für Sicherheit und Sicherungen. - Nichts geht verloren: Sperre, Zwischendatei, automatische Sicherungen, Schutz vor kaputten Dateien.
- Nur nachladen, was gebraucht wird (AJAX) – Seiten werden schneller.
- Nie fremdem Text trauen – immer entschärft ausgeben.
- Erst Vorschau, dann speichern – der Mensch entscheidet, nicht die Maschine.
Ideen zum Weiterbauen (für Schüler):
- „Witz des Tages“ oder „Vokabel des Tages“ nach demselben Muster
- Eine Bewertung mit ⭐ für jeden Spruch (Achtung: Dateisperre!)
- Eine Seite „Alle Sprüche von …“ für jeden Autor
- Sprüche nach Themen (Glück, Liebe, Arbeit) mit Schlagwörtern
— Roland Otto 11.11.2026
Worum geht es? Ganz oben auf jeder Seite steht ein aufklappbarer Kasten „♏️ Sternzeichen des Tages: Skorpion“ – mit Glückwunsch, Bild, kurzem Video, einer Sage aus der Mythologie und „Weiteren Infos“ (Element, Glückszahl, Glücksstein, zehn Tipps …).
Dieser Beitrag zeigt, wie dieses Widget als eigenes, kleines DokuWiki-Plugin funktioniert. Es ist deutlich einfacher als die „Weisheit des Tages“ – und deshalb ein ideales erstes Plugin zum Nachbauen.
Zur Einordnung: Astrologie ist keine Wissenschaft – das Widget ist Unterhaltung. Programmiertechnisch ist es aber ein schönes Beispiel: Daten + eine kleine Berechnung + Ausgabe.
Der aufgeklappte Kasten: Glückwunsch und Sage links, Bild und Video rechts, darunter „Weitere Infos“ zum Aufklappen.
📌 Auf einen Blick
| Was | Wie |
|---|---|
| Plugin-Ordner | lib/plugins/eurobasternzeichen/ (angezeigt als „EURoBa-Sternzeichen“) |
| Daten | daten.php im Plugin – die 12 Zeichen mit allen Texten |
| Bilder/Videos | DokuWiki-Medienordner sternzeichen (data/media/sternzeichen/) |
| Einstellung | Admin → Konfiguration → „Testbetrieb: nur Administratoren“ |
| Datenbank? | keine – nur PHP-Dateien |
🗂️ 1. Der Aufbau – nur eine Handvoll Dateien
lib/plugins/eurobasternzeichen/ ├── plugin.info.txt ← Steckbrief (Name, Autor, Beschreibung) ├── action.php ← rechnet das Sternzeichen aus und baut den Kasten ├── daten.php ← die Texte der 12 Sternzeichen ├── style.css ← Aussehen des Kastens ├── conf/ │ ├── default.php ← Standard-Einstellung (Testbetrieb an/aus) │ └── metadata.php ← sagt DokuWiki, wie die Einstellung im Admin-Bereich aussieht └── lang/ ← alle sichtbaren Texte (Deutsch du/Sie, Englisch)
Warum Daten und Programm trennen? Wer einen Text ändern will (z. B. einen Tipp für den Widder), muss nur daten.php anfassen und kann dabei die Berechnung nicht versehentlich kaputt machen.
📄 2. Die Daten – ein PHP-Array
daten.php enthält kein Programm, sondern gibt nur eine große Liste zurück. In PHP heißt so eine Liste mit Namen assoziatives Array – man greift über einen Namen statt über eine Nummer zu:
<?php return [ // "return" am Dateianfang: die Datei LIEFERT diese Liste 'Widder' => [ // Name des Zeichens = Schlüssel 'emoji' => '♈️', 'datum' => '21. März – 20. April', 'element' => '🔥 Feuer', 'planet' => 'Mars', 'zahl' => '9', 'stein' => 'Diamant', 'bild' => 'data/media/sternzeichen/widder.jpg', // zählt nur der Dateiname 'video' => 'data/media/sternzeichen/widder.mp4', 'mythologie' => 'Der Widder steht in der griechischen Sage für das goldene Vlies …', 'tipps' => [ // eine Liste in der Liste 'Geduld üben – nicht jede Sache erfordert Tempo', 'Zuhören – auch fremde Meinungen zulassen', // … insgesamt 10 Tipps ], ], 'Stier' => [ /* … */ ], // … alle 12 Zeichen ];
Geladen wird die Datei mit einer einzigen Zeile:
$daten = include __DIR__ . '/daten.php'; // __DIR__ = Ordner, in dem action.php liegt echo $daten['Widder']['planet']; // → Mars
🧮 3. Die Berechnung – ein kleiner Zahlentrick
Welches Sternzeichen hat heute, am 11. November? Die Grenzen liegen mitten im Monat (Skorpion: 24.10.–22.11.). Statt mit Monaten und Tagen getrennt zu hantieren, macht das Plugin aus jedem Datum eine einzige Zahl:
Monat × 100 + Tag
- 21. März → 3 × 100 + 21 = 321
- 11. November → 11 × 100 + 11 = 1111
- 24. Oktober → 10 × 100 + 24 = 1024
Solche Zahlen kann man einfach mit „größer“ und „kleiner“ vergleichen – genau wie Kalendertage.
Das ganze Jahr als Zahlenstrahl von 101 bis 1231. Jede farbige Strecke ist ein Sternzeichen, die Zahl darunter sein Beginn. Heute (1111) liegt im Skorpion.
// action.php – Beginn jedes Zeichens als "Monat*100+Tag", aufsteigend sortiert const BEGINN = [ 121 => 'Wassermann', 220 => 'Fische', 321 => 'Widder', 421 => 'Stier', 521 => 'Zwillinge', 622 => 'Krebs', 723 => 'Löwe', 824 => 'Jungfrau', 924 => 'Waage', 1024 => 'Skorpion', 1123 => 'Schütze', 1222 => 'Steinbock', ]; public function sternzeichenAm($monat, $tag) { $wert = $monat * 100 + $tag; // z. B. 1111 für den 11. November $name = 'Steinbock'; // Vorgabe: 1.–20. Januar gehört noch zum Steinbock foreach (self::BEGINN as $beginn => $zeichen) { if ($wert >= $beginn) { // ist dieses Zeichen schon "angefangen"? $name = $zeichen; // dann merken – ein späteres überschreibt es } } return $name; // das LETZTE angefangene Zeichen gewinnt }
Probe am 11. November (1111):
| Beginn | Zeichen | 1111 ≥ Beginn? | gemerkt |
|---|---|---|---|
| 121 | Wassermann | ja | Wassermann |
| … | … | ja | … |
| 924 | Waage | ja | Waage |
| 1024 | Skorpion | ja | Skorpion |
| 1123 | Schütze | nein | Skorpion bleibt |
| 1222 | Steinbock | nein | Skorpion bleibt |
Der Sonderfall Jahreswechsel: Der Steinbock geht vom 22. Dezember bis 20. Januar – also über Silvester. Vom 1. bis 20. Januar ist noch kein Zeichen „angefangen“ (alle Beginn-Zahlen sind größer). Deshalb steht $name am Anfang auf „Steinbock“.
Geprüft: Für alle 366 Tage eines Schaltjahres liefert das neue Plugin genau dasselbe Ergebnis wie der alte Code in main.php.
🪝 4. Wie kommt der Kasten auf die Seite?
Genau wie bei der „Weisheit des Tages“: Das Plugin meldet sich bei DokuWiki für das Ereignis „kurz bevor der Seiteninhalt angezeigt wird“ an.
public function register(EventHandler $controller) { // letzter Wert 100 = Reihenfolge: Das Sternzeichen ist NACH der Weisheit (Standard 0) dran $controller->register_hook('TPL_CONTENT_DISPLAY', 'BEFORE', $this, 'widgetEinfuegen', null, 100); } public function widgetEinfuegen(Event $event) { global $ACT; if ($ACT !== 'show') return; // nur beim Anzeigen if ($this->getConf('nur_admins') && !auth_isadmin()) return; // Testbetrieb? $daten = include __DIR__ . '/daten.php'; $name = $this->sternzeichenAm((int)date('n'), (int)date('j')); // n = Monat, j = Tag (ohne führende Null) if (!isset($daten[$name])) return; // Daten fehlen → still nichts tun $event->data = $this->html($name, $daten[$name]) . $event->data; // Kasten VOR den Inhalt setzen }
Warum steht das Sternzeichen ganz oben, obwohl es später dran ist? Beide Plugins setzen ihren Kasten vor den bisherigen Inhalt. Wer zuletzt kommt, landet also ganz vorne – wie bei einem Stapel Zettel, auf den man immer oben drauflegt.
🎨 5. Die Ausgabe – Aufklappen ganz ohne JavaScript
Der Kasten nutzt die HTML-Elemente <details> und <summary>. Der Browser macht daraus von selbst einen aufklappbaren Bereich – kein einziges Zeichen JavaScript nötig:
<details class="sz-widget"> <!-- zugeklappt, bis man draufklickt --> <summary>♏️ Sternzeichen des Tages: Skorpion 11.11.2026</summary> <!-- immer sichtbar --> <div class="sz-wunsch">Wir wünschen allen „Skorpion“-Geborenen viel Glück und Erfolg! 🍀</div> <div class="sz-mythos">📜 …</div> <video controls preload="metadata" poster="…skorpion.jpg"> <!-- Video erst beim Klick laden --> <source src="…skorpion.mp4" type="video/mp4"> </video> <details class="sz-infos"> … Weitere Infos … </details> <!-- Aufklapper im Aufklapper --> </details>
Drei Kleinigkeiten mit großer Wirkung:
preload=„metadata“– der Browser lädt vom Video nur die Eckdaten (Länge, Größe). Die Videodatei selbst (3–4 MB) kommt erst, wenn jemand auf ▶ drückt. Spart Datenvolumen – besonders am Handy.poster=„…“– bis dahin zeigt der Videoplayer das Bild des Sternzeichens.- Nur vorhandene Felder – fehlt bei einem Zeichen z. B. der Glücksstein, erscheint die Zeile gar nicht erst (statt „Glücksstein: “ ohne Inhalt).
„Weitere Infos“ aufgeklappt: Jede Zeile entsteht aus der Funktion zeile() unten – Bezeichnung fett, Werte mit „|“ getrennt, die zehn Tipps als nummerierte Liste.
// Baut eine Zeile "Bezeichnung: Wert | Bezeichnung: Wert" – nur aus Feldern, die es gibt protected function zeile(array $z, array $felder) { $teile = []; foreach ($felder as $feld => $beschriftung) { if (!empty($z[$feld])) { // Feld vorhanden und nicht leer? // hsc() entschärft den Text: < und > werden nie als HTML ausgeführt $teile[] = '<strong>' . hsc($beschriftung) . ':</strong> ' . hsc($z[$feld]); } } return $teile ? '<div class="sz-zeile">' . implode(' | ', $teile) . '</div>' : ''; }
🖼️ 6. Bilder und Videos – über DokuWikis Medienverwaltung
Bilder und Videos liegen im ganz normalen Medien-Manager von DokuWiki im Ordner sternzeichen. Vorteile: Man kann sie bequem im Wiki austauschen, und DokuWiki liefert Bilder auf Wunsch gleich verkleinert aus.
protected function medienUrl($pfad) { if (empty($pfad)) return ''; $id = 'sternzeichen:' . basename($pfad); // "data/media/sternzeichen/widder.jpg" → "sternzeichen:widder.jpg" return file_exists(mediaFN($id)) ? ml($id) : ''; // Datei da? → Adresse, sonst leer (kein kaputtes Bild) }
mediaFN()– DokuWiki-Funktion: wo liegt die Datei auf der Festplatte?ml()(„media link“) – DokuWiki-Funktion: unter welcher Adresse ist sie im Browser erreichbar?
🌍 7. Texte in Sprachdateien – und eine Einstellung im Admin-Bereich
Kein sichtbarer Text steht fest im Programm. Alles kommt aus lang/de-informal/lang.php (bzw. de für „Sie“, en für Englisch). DokuWiki wählt automatisch die passende Datei zur Wiki-Sprache:
$lang['titel'] = 'Sternzeichen des Tages'; $lang['wunsch'] = 'Wir wünschen allen „%s“-Geborenen viel Glück und Erfolg! 🍀'; // %s = Platzhalter // im Programm: sprintf($this->getLang('wunsch'), 'Skorpion')
Die Einstellung „Testbetrieb: nur Administratoren“ erscheint dank zweier kleiner Dateien automatisch unter Admin → Konfiguration:
// conf/default.php – der Standardwert $conf['nur_admins'] = 0; // 0 = für alle sichtbar, 1 = nur Admins (zum Ausprobieren) // conf/metadata.php – wie DokuWiki die Einstellung anzeigt $meta['nur_admins'] = ['onoff']; // ein Ein/Aus-Häkchen
Der Testbetrieb hat sich bewährt: Neues Widget erst „nur für Admins“ einschalten, in Ruhe prüfen – und erst dann für alle freigeben. Besucher sehen so nie eine halbfertige Version.
🎓 8. Kernaussagen und Ideen für eigene Projekte
- Kleines Plugin, klare Teile: Daten (
daten.php), Berechnung (sternzeichenAm()), Ausgabe (html()), Texte (lang/), Einstellungen (conf/). - Zahlentrick: Aus zwei Werten (Monat, Tag) eine vergleichbare Zahl machen vereinfacht die Logik enorm.
- Randfälle bedenken: Jahreswechsel! Und dann alle Fälle testen (hier: 366 Tage).
- HTML kann mehr, als man denkt:
<details>statt JavaScript,preloadstatt Datenverschwendung. - Nie fremdem Text trauen: auch eigene Daten immer mit
hsc()ausgeben.
Ideen zum Nachbauen (für Schüler):
- Chinesisches Tierkreiszeichen des Jahres (Tipp:
(Jahr − 4) % 12ergibt die Nummer des Tiers) - Namenstag des Tages aus einer Liste mit 366 Einträgen
- Mondphase heute (Tipp: ein Mondzyklus dauert etwa 29,53 Tage)
- „Heute vor … Jahren“ – ein historisches Ereignis zum heutigen Datum
- Countdown bis zu den nächsten Ferien
— Roland Otto 11.11.2026
— Roland Otto 10.05.2026 13:04












Verbesserung? Lass es uns wissen. Deine Meinung zählt: