Digitaler Schulkalender für WordPress — aus IServ direkt auf die Schulwebsite.
Version 4.20.0 · PHP 8.0+ · WordPress 6.0+ · GPL v2
Curricu:lr Terminplan liest den IServ-Kalender eurer Schule als iCal-Feed und stellt ihn als interaktiven Terminplan auf der WordPress-Website bereit. Kein manuelles Einpflegen — Termine aus IServ erscheinen automatisch auf der Schulwebsite.
Das Projekt besteht aus vier Teilen:
| Teil | Was | Wo |
|---|---|---|
| WordPress-Plugin | Zeigt den Terminplan auf der Schulwebsite | plugin/ |
| Konverter-Tool | Wandelt Excel-Terminpläne in iCal-Dateien um | konverter/ |
| Excel-Vorlage + Scripts | Generiert die Stundenplanvorlage für Excel | scripts/ + website/downloads/ |
| Website | Projektwebsite mit Anleitung und Downloads | website/ |
- Quartalansicht — Tabellenansicht auf Desktop, Agenda-Karten auf Mobile
- Jahresansicht — Kalenderraster Sep–Jul, ein Blick auf das gesamte Schuljahr
- Kategorie-Filter — farbige Badges, horizontal scrollbar, immer sichtbar
- Volltextsuche — Echtzeit-Filterung über alle Termintitel und -beschreibungen
- Dark Mode — manuell oder automatisch per System-Einstellung
- PDF-Export — einzelnes Quartal oder gesamtes Schuljahr, mit Lade-Feedback
- Feedback-Funktion — Termine direkt per E-Mail kommentieren, Log im Admin
- Kiosk-Modus — Einbettung in IServ ohne WordPress-Login, per Token-URL
- Entwurf-Kiosk — Vorschau-Link für das Schulleitungsteam, bevor der Plan freigegeben wird
- Mehrere Schuljahre — bis zu 5 Profile parallel, einfacher Schuljahreswechsel
- Entwurf-Modus — neues Schuljahr vorbereiten, erst nach Beschluss veröffentlichen
- Hilfe-Overlay — erklärt alle Bedienelemente, kein CDN nötig
- WordPress 6.0 oder höher
- PHP 8.0 oder höher
- IServ mit iCal-Feed (HTTPS-URL des Schulkalenders)
- Den Ordner
plugin/alsgsh-terminplannachwp-content/plugins/kopieren - Plugin in WordPress aktivieren
- Einstellungen → Schul-Terminplan öffnen
- Neues Schuljahr-Profil anlegen (Tab Schuljahr anlegen)
- iCal-URL des IServ-Kalenders eintragen
- Cache aufbauen klicken — Termine werden geladen
- Shortcode auf einer Seite einfügen:
[gsh_terminplan]
| Parameter | Beispiel | Beschreibung |
|---|---|---|
| (ohne) | [gsh_terminplan] |
Aktives Schuljahr, aktuelles Quartal |
quartal |
quartal="2" |
Bestimmtes Quartal (1–4) |
quartal |
quartal="alle" |
Alle Quartale mit Tabs |
schuljahr |
schuljahr="sj_2026_27" |
Bestimmtes Schuljahr-Profil |
schuljahr |
schuljahr="entwurf" |
Entwurfs-Profil anzeigen (nur Admins) |
URL-Parameter ?sj= überschreibt den Shortcode-Wert (Schuljahr-Umschalter im Frontend).
Das Plugin verwaltet bis zu 5 Schuljahr-Profile. Jedes Profil hat:
- eigene iCal-URL
- eigene Quartalsgrenzen
- Status Beschlossen oder Entwurf
Nur das als aktiv markierte, beschlossene Profil erscheint im Shortcode ohne Parameter.
Entwurf-Profile sind nur für WordPress-Admins sichtbar — bis zur Freigabe über den Entwurf-Kiosk.
Termine werden automatisch per Keyword-Matching kategorisiert. Standardkategorien:
| Kategorie | Typische Keywords |
|---|---|
| Jahrgang 5/6 | Jg. 5, Jg. 6, Klasse 5, Klasse 6 |
| Jahrgang 7/8 | Jg. 7, Jg. 8 |
| Jahrgang 9/10 | Jg. 9, Jg. 10 |
| Oberstufe | EF, Q1, Q2 |
| Inklusion | IFÖ, AL-SuS, Förderplan, AOSF |
| Feiertage | Feiertag, schulfrei, Ferientag |
| Konferenzen | Lehrerkonferenz, Zeugnisausgabe |
Labels, Farben und Keywords sind unter Einstellungen → Kategorien frei konfigurierbar.
Der Kiosk-Modus zeigt den Terminplan ohne WordPress-Navigation — ideal für IServ-Infoboards oder -Navigationseinträge.
Das Plugin registriert die Seitenvorlage automatisch — kein Kopieren in den Theme-Ordner nötig.
- Neue WordPress-Seite anlegen → Vorlage „Terminplan Kiosk" wählen → veröffentlichen
- Kiosk-Token generieren: Admin → Kiosk & System → „Zufälligen Token erzeugen" → speichern
- IServ-Domain eintragen (verhindert Einbettung von fremden Seiten)
- Die angezeigte Kiosk-URL in IServ als Navigationslink oder iFrame hinterlegen
Sicherheit: Token-Vergleich ist timing-sicher (hash_equals). Rate-Limiting: max. 10 Fehlversuche pro IP pro Stunde.
Detaillierte Schritt-für-Schritt-Anleitung: docs/anleitung-kiosk-einrichtung.md
Neu in v4.1.0
Ermöglicht es dem Schulleitungsteam, einen Entwurfs-Terminplan vor der offiziellen Freigabe einzusehen — ohne WordPress-Login, per einfachem Link.
Das Plugin registriert die Seitenvorlage automatisch — kein Kopieren in den Theme-Ordner nötig.
Schritt 1: WordPress-Seite anlegen
WordPress-Backend → Seiten → Neu erstellen:
- Titel: z. B. „Terminplan Entwurf"
- Vorlage: Terminplan Entwurf-Vorschau auswählen
- Seite veröffentlichen (die Seite ist trotzdem nur mit Token erreichbar)
Schritt 2: Entwurf-Token generieren
Admin → Einstellungen → Schul-Terminplan → Kiosk & System:
- Abschnitt Entwurf-Vorschau → „Zufälligen Token erzeugen"
- Einstellungen speichern
- Die generierte URL wird automatisch angezeigt
Schritt 3: URL teilen
Die URL hat das Format:
https://eure-schule.de/terminplan-entwurf/?token=abc123...
Diese URL per E-Mail oder Chat an das Schulleitungsteam senden.
Was sieht das Team?
- Den vollständigen Entwurfs-Terminplan mit allen Funktionen (Filter, Suche, Dark Mode)
- Ein prominentes gelbes Banner: „Entwurf — noch nicht beschlossen"
- Kein WordPress-Login nötig
Wann wird der Entwurf öffentlich?
Erst wenn das Profil im Admin auf Beschlossen gesetzt und als aktives Schuljahr markiert wird. Bis dahin bleibt es für normale Besucher unsichtbar.
Sicherheit: Separater Token vom Live-Kiosk. Timing-sicherer Vergleich (hash_equals). Rate-Limiting: max. 10 Fehlversuche pro IP pro Stunde.
Das Konverter-Tool wandelt eine ausgefüllte Excel-Terminplan-Vorlage direkt im Browser in eine .ics-Datei um — die dann in IServ als Kalender importiert werden kann.
Verwendung:
konverter/Terminplan_Konverter.htmllokal im Browser öffnen (Doppelklick genügt — kein Server nötig)- Fertig ausgefüllte Excel-Vorlage hochladen
- Vorschau prüfen
- ICS herunterladen und in IServ importieren
Das Tool läuft vollständig im Browser, keine Daten verlassen den Rechner.
Die Schulwochen-Vorlage für Excel wird per Python-Script generiert und enthält alle Schulwochen des Schuljahres als vorgefertigte Tabelle.
# Python 3 + openpyxl benötigt
pip install openpyxl
python scripts/build_excel_template.py
# Ergebnis: website/downloads/Terminplan_Schulwochen_Vorlage.xlsxIn der generierten Vorlage den Tab Ferien öffnen:
| Zelle | Inhalt |
|---|---|
| B3–C3 | Herbstferien (erster / letzter Tag) |
| B4–C4 | Weihnachtsferien |
| B5–C5 | Osterferien |
| B6–C6 | Pfingstferien (optional) |
| B7–C7 | Sommerferien |
| B10 | Erster Schultag (SW 00) |
| B11 | Erster Unterrichtstag (SW 01) |
| B12 | Letzter Schultag |
python scripts/recalc.pyPrüft: keine Blattsperren, echte Datumswerte, 42 Schulwochen-Header, Ferien-Skip korrekt.
Kurzfassung — vollständige Anleitung in docs/schuljahreswechsel-anleitung.md.
1. Excel-Vorlage generieren
python scripts/build_excel_template.py
2. Ferien und Termine eintragen (Tab "Ferien", Tab "Terminplan")
3. Vorlage per Konverter in ICS umwandeln
konverter/Terminplan_Konverter.html im Browser öffnen
4. ICS in IServ importieren (als neuen Schulkalender)
5. Im Plugin: neues Profil als Entwurf anlegen, iCal-URL des neuen Kalenders eintragen
6. Entwurf-Kiosk-Link an Schulleitung senden → Feedback einholen
7. Nach Beschluss: Profil auf "Beschlossen" setzen, als aktives Schuljahr markieren
8. Altes Schuljahr-Profil deaktivieren oder löschen
plugin/
gsh-terminplan.php # Plugin-Bootstrap: Admin-UI, iCal-Abruf, Shortcode, Kiosk
curriculr-data-layer.php # REST API curriculr/v1: Dokumente, Stufen, Revisionen, ICS-Feed
curriculr-auth.php # IServ-SSO: OIDC-Code-Flow, App-Token (JWT), Gruppen-Whitelist
curriculr-guard.php # Bearer-Token-Validierung als WP permission_callback
page-terminplan-entwurf.php # Page-Template: Entwurf-Kiosk (ins Theme kopieren)
assets/
css/gsh-terminplan.css # Gesamtes CSS (kein Inline-CSS im PHP)
css/design-tokens.css # Design-Token-System (Curricu:lr Farbpalette)
konverter/
Terminplan_Konverter.html # Standalone Excel→iCal-Konverter (Browser)
scripts/
build_excel_template.py # Hauptscript: generiert fertige Vorlage
patch_rows.py # Grundstruktur (einmalig bei Strukturänderung)
patch_xlsx.py # Datumsformeln setzen
recalc.py # Verifikation der Formeln
ferien_2026_27.json # Feriendaten als JSON
website/
index.html # Startseite (GitHub Pages)
anleitung.html # Installationsanleitung
download.html # Download-Seite
downloads/
Terminplan_Schulwochen_Vorlage.xlsx # Aktuelle Vorlage
Terminplan_Konverter.html # Konverter-Tool (Mirror)
gsh-terminplan.zip # Plugin-ZIP
docs/
schuljahreswechsel-anleitung.md # Detaillierte Jahresanleitung
sop-terminplan-schuljahr.md # Betriebsanweisung
tasks/
lessons.md # Gelernte Lektionen (für KI-Assistenz)
Kein Build-System. Änderungen direkt in den Quelldateien, dann in WordPress hochladen.
php -l plugin/gsh-terminplan.phpErwartete Ausgabe: No syntax errors detected in plugin/gsh-terminplan.php
Alles CSS ausschließlich in plugin/assets/css/gsh-terminplan.css. Kein Inline-CSS in PHP-Strings oder Heredocs.
Bei jedem Release müssen genau diese vier Stellen auf X.Y.Z gesetzt werden:
| # | Datei / Stelle | Beispiel |
|---|---|---|
| 1 | Plugin-Header: Version: in gsh-terminplan.php |
Version: 4.12.0 |
| 2 | PHP-Konstante in gsh-terminplan.php |
define('GSH_TP_VERSION', '4.12.0') |
| 3 | gsh_tp_changelog() — neuer Eintrag oben |
'version' => '4.12.0' |
| 4 | Plugin-Header-Kommentar Changelog X.Y.Z: |
neuer Block oben |
Bump-Regel: Bugfix → Patch, neues Feature → Minor, Breaking Change → Major.
gsh-terminplan.php — Plugin-Bootstrap:
| Sektion | Bereich | Schlüsselfunktionen |
|---|---|---|
| 0 | Profil-Hilfsfunktionen | gsh_tp_get_profiles, gsh_tp_active_profile_id |
| 1 | Admin-UI + Tab-System | gsh_tp_settings_page, gsh_tp_render_profile_tab, gsh_tp_render_system_tab |
| 2 | iCal-Abruf / Cache | gsh_tp_fetch_ical, gsh_tp_fetch_sync, gsh_tp_do_refresh |
| 3 | Schulwochen / Quartale | gsh_tp_quartale, gsh_tp_current_q |
| 5 | Shortcode | gsh_tp_shortcode |
| 9 | Deinstallation | Profil-aware Cleanup |
curriculr-data-layer.php — REST API curriculr/v1: Dokumente (wp_curriculr_docs), Stufen (Entwurf/Genehmigt/Öffentlich), Revisionen (wp_curriculr_doc_revisions), ICS-Feed-Generierung, CORS.
curriculr-auth.php — IServ-OIDC-SSO: Authorize-URL, Code→Token-Austausch, Userinfo, App-Token-Signierung (HS256 JWT), Gruppen-Whitelist.
curriculr-guard.php — Bearer-Token-Validierung als WP permission_callback auf allen curriculr/v1-Routen; füllt Autoren-Claims für Revisions-Attribution.
Nicht anfassen (gsh-terminplan.php): iCal-Parser, Date-Index, Tabellen-Rendering, PDF-Export, Change-Notification-System.
- [FIX] 409-Konfliktantwort enthält jetzt
authorNameundsavedAt(auswp_curriculr_doc_revisions) — Planner zeigt „Gespeichert von X am TT.MM.JJJJ"
- [FIX] IServ-SSO:
iserv:groups-Claim korrekt aus Token gelesen (stattgroups)
- [FEATURE]
curriculr-guard.php: Bearer App-Token-Validierung als WPpermission_callbackauf allencurriculr/v1-Routen - [FEATURE] DB v4:
author_sub/author_namein Revisions-Tabelle;repo_putbefüllt Autorenfelder aus Guard-Claims - [SECURITY] Guard fail-closed (leerer Key → 403); stale Claims global bei Fehler geleert; Lade-Reihenfolge auth→guard→data-layer erzwungen
- [FEATURE]
curriculr-auth.php: IServ-OIDC-Code-Flow, App-Token (HS256-JWT, RAM im Browser), Gruppen-Whitelist - [FEATURE] REST-Routen:
POST /auth/login,GET /auth/callback,POST /auth/token,POST /auth/logout - [FEATURE] Admin-Tab „System": SSO-Konfiguration, Status-Anzeige, Datenschutz-/Vibecoding-Hinweis
- [SECURITY] Nonce Pflicht (fehlt → Hard-Fail); Single-Use State+Nonce+Handoff-Transients; Gruppen-Whitelist fail-closed;
hash_equalsHMAC-Vergleich; Secrets nur inwp-config.php - [SECURITY] Nonce-Bypass-Fix +
wp_kses_postauf Admin-Echo (vor Merge)
- [FEATURE]
wp_curriculr_doc_revisions-Tabelle: Snapshot bei jedem PUT, Prune auf 50 Revisionen - [FEATURE] REST-Routen:
GET /doc/{sj}/revisions(Liste),GET /doc/{sj}/revisions/{id}(Einzelabruf) - [FEATURE] Nächtliches WP-Cron-Backup: JSON + ICS in
uploads/curriculr-backups/
- [FEATURE] Profil-Zuordnung: Planner-
schoolyear.id→ WP-Profil-Key im System-Tab konfigurierbar - [FEATURE] REST-Endpunkt
PUT /doc/{sj}: versioniertes Speichern mit 409-Konfliktschutz und Stage-Verwaltung - [FEATURE] REST-Endpunkt
GET /doc/{sj}: aktuellen Stand mit Versionsnummer abrufen - [FEATURE] ICS-Feed-Endpunkt aus gespeichertem Dokument generieren (kein IServ-Pull nötig)
- [FEATURE]
curriculr/v1REST-API:wp_curriculr_docs-Tabelle, Stage-Übergänge (Entwurf → Genehmigt → Öffentlich), CORS-Header für GitHub-Pages-Origin - [FEATURE] Feed-Reuse: WP-Frontend liest ICS-Feed aus gespeichertem Dokument (kein direkter IServ-Abruf mehr nötig)
- [FIX] Verschiedene Stabilitäts- und Kompatibilitätsfixes für PHP 8.0+
- [FIX] Entwurf-Vorschau + IServ-Kiosk: je eigenes Formular mit direktem POST-Handler — kein
options.phpmehr - [UX] Kiosk & System Tab: zwei klar getrennte Sektionen mit eigenem Speichern-Button
- [FEATURE]
theme_page_templates-Filter: Vorlage „Terminplan Entwurf-Vorschau" erscheint automatisch im WP-Seiten-Editor - [CHORE] PDF-Strings: GSH → Curriculr (Logo-Mark, Footer, Dateinamen)
- [FIX] Entwurf-Token in
gsh_tp_options-Gruppe — gleicher Form-Submit wie IServ-Kiosk
- [FIX] Entwurf-Token-Formular: direkter POST-Handler statt
options.php— kein Redirect auf „Alle Einstellungen" mehr
- [FIX] Entwurf-Vorschau: fehlender
<form>-Wrapper — Token wird jetzt gespeichert - [FIX] Entwurf-Token in eigene Option-Gruppe — IServ-Kiosk-Daten bleiben beim Speichern erhalten
- [FEATURE]
template_include-Filter: Plugin-Template ohne Theme-Copy nutzbar - [FEATURE] Button „Vorschau-Seite automatisch erstellen" im Admin
- [UX] Header: einzeilig, Suche inline, keine redundante Überschrift
- [UX] Jahresansicht-Toggle in sticky Tab-Leiste integriert — Rückkehr zur Quartalansicht immer sichtbar
- [UX] Filter-Bar: horizontal scrollbar (kein Wrap), immer sichtbarer Toggle-Button
- [UX] Tab-Design: Unterstrich-Indikator statt Navy-Pill-Hintergrund
- [UX] Hilfe-Button: Pill mit „Hilfe"-Label statt reines Icon
- [UX] PDF-Buttons: Lade-Feedback mit
disabled-State und reduzierter Deckkraft für 3,5 s - [FIX]
border-left-Antipattern: alle 8+ Instanzen durch Full-Border ersetzt - [FIX] Schriftgrößen auf Event-Cards und Agenda-Karten erhöht (≥ 0,8 rem)
- [FIX] Kategorie-Fallback-Farbe von
#6c757dauf#94a3b8(höherer Kontrast auf Pastel-Hintergrund) - [FIX] Admin-Notices:
border-left→bordermitborder-radius
- [FEATURE] Jahresansicht: Sep–Jul-Kalenderraster (
gsh_tp_yearview(),gtpViewToggle()) - [UX]
gtpApply()erweitert: Jahresansicht wird bei Quartalswechsel korrekt ausgeblendet
- [FEATURE] Entwurf-Kiosk: Token-gesicherte Vorschau-Seite für das Schulleitungsteam
- [SECURITY]
gsh_tp_check_draft_kiosk_access(): timing-sicherer Vergleich + Rate-Limiting (10/h/IP) - [UX] Admin Kiosk & System Tab: neue Entwurf-Vorschau-Sektion mit Token-Generator und URL-Anzeige
- [DESIGN] Curricu:lr-Designsystem: Navy-Primärfarbe, Inter-Font, neue Markenidentität
- [DESIGN] Plugin-Frontend: vollständige CSS-Neuschrift mit Design-Tokens
- [INFRA]
design-tokens.cssals separate Datei mit Cache-Busting
- [UX] Header zweizeilig, Suche in voller Breite, Quartal-Dot-Indikator
- [UX] Filter-Bar mit Mobile-Toggle, Footer in Aktions- und Meta-Bereich
- [UX] Theme-Switcher und Feedback-Button mit SVG-Icons
- [FEATURE] Neue Standardkategorie „Inklusion"
- [FEATURE] Keywords pro Kategorie im Admin editierbar
- [FEATURE] Eigenes Hilfe-Overlay (kein CDN)
- [FEATURE] Kategorien-System mit
GSH_TP_DEFAULT_CATEGORIESund Keyword-Matching
- [FEATURE] Feedback per AJAX +
wp_mail()mit Admin-Log
- [FEATURE] Dark Mode mit Hell/Dunkel/System-Umschalter
- [FEATURE] Kiosk-Modus mit Token-Authentifizierung und Rate-Limiting
GPL v2 or later — freie Nutzung, Weitergabe und Modifikation unter gleicher Lizenz.