- Require contao/core-bundle and calendar-bundle ^6.0 - Render the setup check and the notification help as input_field callbacks instead of a custom backend widget; the fields no longer create a virtual jsonData column - Load the backend style sheet through contao.backend.custom_css and remove the menu listener (tooltip workaround for Contao 5 included) - Tell editors when the Notification Center is not installed instead of linking to a module that does not exist - Generate category aliases with the slug settings of the calendar page so that umlauts are transliterated for German sites - Always return a RecordLabel for the category list, export stored values as they are, remove the decodeEntities options and the legacy "events" URL parameter, use array notation for the price columns Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
W&K-Sync Bundle
Verbindet Contao 6 mit der Seminarverwaltung W&K (Windows-Software ohne Schnittstelle) über CSV-Dateien:
- Import: Die Redaktion exportiert in W&K die Seminarliste als CSV und lädt sie im Contao-Backend hoch. Vor dem Schreiben zeigt eine Vorschau, was sich ändert (neu, geändert, storniert, unverändert, übersprungen, Warnungen). Erst nach Bestätigung werden die Veranstaltungen im Kalender angelegt bzw. aktualisiert.
- Online-Anmeldung: Besucher melden sich über ein Formular des Formulargenerators zu einer Veranstaltung an. Das Bundle blendet je nach Veranstaltung Felder ein und aus, prüft Plätze und Anmeldeschluss, speichert die Anmeldung, verschickt die passende Benachrichtigung (Notification Center 2) und leitet auf die Bestätigungsseite weiter.
- Posteingang: Anmeldungen und Kontaktanfragen werden in Ordnern bearbeitet (Neu, Download-Liste, Bereits bearbeitet, Ignoriert/Spam, Exportiert).
- Export: Die Download-Liste wird als CSV für den W&K-Import erzeugt (ISO-8859-1, Semikolon, feste Spalten), die Datensätze werden als exportiert markiert und die freien Plätze der Veranstaltungen reduziert.
- Contao 6, PHP 8.5, Doctrine DBAL 4, MySQL 8 oder MariaDB
- Keine Abhängigkeiten außer dem Contao-Core und dem Kalender-Bundle
- Notification Center 2 wird für Benachrichtigungen empfohlen (Typ „W&K-Anmeldung“), ist aber nicht erforderlich; ohne Notification Center werden Anmeldungen gespeichert und protokolliert, aber keine Mails verschickt (Stand September 2026 unterstützt das Notification Center Contao 6 noch nicht)
- Newsletter-Anmeldung (Double-Opt-in) mit dem Newsletter-Bundle, optional
Installation
composer require mummert/wk-sync-bundle
Das Paket liegt nicht auf Packagist; das Repository in der composer.json des Projekts eintragen:
"repositories": [
{ "type": "vcs", "url": "ssh://git@git.mummert.media:3245/mummert/wk-sync-bundle.git" }
]
Danach die Datenbank aktualisieren:
vendor/bin/contao-console contao:migrate
Einrichtung
-
Kategorien anlegen – Backend „W&K-Tools → Kategorien“ oder die Standardkategorien des TPI Moritzburg per Befehl:
vendor/bin/contao-console wk-sync:categories:seedJede Kategorie hat eine Art (Schule, Evangelische Schulen, Elementarbereich, Gemeinde, Gottesdienst mit Kindern, Tagungen, Ausbildung, Mentoring, Sonstiges) und die W&K-Bezeichnungen (Werte des Felds
kbsgebiet, eine pro Zeile). Beim Import zählen genaue Treffer zuerst (alle Kategorien mit dieser Bezeichnung, z. B. „Alle Schularten“ für Grundschule und weiterführende Schule), sonst der erste Teilstring-Treffer in Sortierreihenfolge. Die erste zugeordnete Kategorie wird Hauptkategorie und steuert die Formularregeln und die Benachrichtigung. -
Anmeldeformular anlegen – im Formulargenerator, am einfachsten per Befehl (legt alle Felder mit passenden Namen, Optionen und CSS-Klassen an und trägt es im Kalender ein):
vendor/bin/contao-console wk-sync:form:create --calendar=1Die Feldnamen entsprechen den Spalten von
tl_wk_booking(gender,firstname,lastname,street,postal,city,email,phone,position,birthday,teacherPosition,schoolName,schoolCity,vegetarian,overnightStay,addDifferentInvoiceAddress,invoice…,addNotes,notes,newsletter,privacyAccepted,agbAccepted). Spamschutz über das ALTCHA-Feld des Contao-Kerns. Das Formular braucht keine Weiterleitungsseite und kein „Daten in Tabelle speichern“ – beides übernimmt das Bundle.Der Befehl legt bei jedem Aufruf ein neues Formular an (Alias
wk-anmeldung, bei Bedarf nummeriert); bei der Installation passiert nichts automatisch. Ein von Hand gebautes Formular funktioniert genauso, solange die Feldnamen stimmen – welches Formular gilt, entscheidet allein die Auswahl „Anmeldeformular“ in den Einstellungen. -
Einstellungen anlegen – Backend „W&K-Tools → Einstellungen → Neu“, ein Eintrag je Kalender:
- Kalender, in den importiert wird und dessen Veranstaltungen buchbar sind
- Anmeldeformular, Seite mit dem Anmeldeformular, Bestätigungsseite, Seite mit dem Anfrageformular
- Anmeldeschluss (Tage vor Beginn, Standard 1) und angezeigter Anmeldeschluss (Standard 31)
- Eigene Veranstaltungsorte (Standard „TPI Moritzburg“; Orte, die so beginnen, gelten als „im eigenen Haus“)
- Autor importierter Veranstaltungen, Mitgliedergruppen für Kursleitung und Referenten (leer = keine Mitglieder anlegen, die Namen aus W&K bleiben dann unberücksichtigt)
- Newsletter-Verteiler und Aktivierungsseite (optional)
- Benachrichtigungen: Standard sowie je Bedingung 1–12 (siehe unten); die Auswahl zeigt den Titel mit ID. Das Formular erklärt die Bedingungen mit einer Matrix (Art × ein-/mehrtägig); solange keine Benachrichtigung vom Typ „W&K-Anmeldung“ existiert, steht dort ein Hinweis mit Link zum Notification Center.
wk-sync:form:create --calendar=1legt den Eintrag bei Bedarf mit an. Am Kalender selbst wird nichts konfiguriert.Beim Bearbeiten zeigt die Legende Prüfung der Einrichtung oben im Formular, ob die Verkettung stimmt: Feldnamen des Anmeldeformulars (fehlen Vorname, Nachname oder E-Mail, ist das ein Fehler), Modul „W&K-Anmeldeformular“ auf der Anmeldeseite, Bestätigungsseite, Anfrageformular mit Zieltabelle
tl_wk_inquiry, Modul „W&K-Anmeldestatus“ auf der Weiterleitungsseite des Kalenders und mindestens eine zugeordnete Benachrichtigung. Grün, wenn alles passt, rot mit konkreten Hinweisen sonst. -
Seiten und Module
- Seite Kurs (
kurs): Modul Event-Reader des Kerns plus Modul W&K-Anmeldestatus (freie Plätze, Anmeldeschluss, Link „Jetzt anmelden“, Hinweis bei externer Anmeldung, geschlossenem Teilnehmerkreis usw.) - Seite Anmeldung (
kurs/anmeldung): Modul W&K-Anmeldeformular – wird wie der Event-Reader mit dem Alias der Veranstaltung aufgerufen (/kurs/anmeldung/<alias>) - Seite Anmeldung eingegangen: Text; der Link enthält
?booking=<Token>, damit Folgeformulare (z. B. Workshop-Auswahl) die Anmeldung kennen (siehe unten) - Seite Anfrage: Formular des Formulargenerators mit „Daten in Tabelle speichern“ →
tl_wk_inquiry(Feldnamengender,academicTitle,firstname,lastname,street,postal,city,email,phone,subject,notes,newsletter,privacy). Der Link vom Anmeldestatus hängt?event=<ID>an; das Bundle trägt die Veranstaltung automatisch ein. - Kalender-Module des Kerns (Eventliste, Kalender, Eventmenü) erhalten das Feld W&K-Kategorien, um nur Veranstaltungen bestimmter Kategorien zu zeigen.
- Seite Kurs (
-
Benachrichtigungen (Notification Center 2): Benachrichtigungen vom Typ W&K-Anmeldung anlegen (System → Benachrichtigungen) und unter W&K-Tools → Einstellungen den Bedingungen zuordnen. Ohne zugeordnete Benachrichtigung wird die Anmeldung gespeichert, aber keine Mail verschickt (Warnung im Systemprotokoll). Tokens:
##recipient_email##,##subscription_*##(alle Felder der Anmeldung, Auswahlfelder zusätzlich als##subscription_gender_label##usw., Ja/Nein-Felder als##subscription_vegetarian_label##),##event_*##(alle Felder der Veranstaltung, Datumsfelder formatiert),##event_datetime##,##event_link##,##event_categories##,##event_speakers##,##subscription_event_wk_id##,##subscription_confirmation_page_url##,##calendar_*##,##admin_email##. Die bisherigen Token-Namenevent_organizerCityundevent_internalEventNumberbleiben erhalten. -
Benutzergruppen: Zugriff auf die Module des Bereichs „W&K-Tools“ (Import, Posteingang, Anmeldungen, Kontaktanfragen, Exporte, Kategorien, Einstellungen, Protokoll) freigeben. Anmeldungen werden ausschließlich dort bearbeitet, nicht am Event im Kalender.
Import aus W&K
Backend → W&K-Tools → Import → Datei hochladen. Erwartet wird der Seminarexport aus W&K (Semikolon, Anführungszeichen, Windows-1252 oder UTF-8, mehrzeilige Felder). Wichtig: in W&K das größtmögliche Zeitintervall exportieren – Seminare, die nicht in der Datei stehen, werden storniert und ausgeblendet (nur Veranstaltungen des Kalenders, die eine W&K-Nummer tragen; manuell angelegte Veranstaltungen ohne W&K-Nummer bleiben unberührt).
Regeln beim Import:
| W&K | Contao | Bemerkung |
|---|---|---|
kbnr |
wkId |
Muss mit einer führenden Null beginnen, sonst wird der Datensatz übersprungen |
kbvsnr |
wkInternalEventNumber |
Leer = nicht veröffentlichen (übersprungen); „keine“ wird zu leer |
kbtitel, kbutitel, kbrtitel |
title, wkSubTitle, wkSubTitleAdditional |
Titel ist Pflicht |
kbanreise, kbabreise, kbuanreise, kbuabreise |
startDate, endDate, startTime, endTime, addTime |
Datum TT.MM.JJJJ, Uhrzeit HH.MM; wie im Contao-Kern (ganztägig bis 23:59:59) |
kbstatus1 |
wkState |
1–5; 5 (Storno) blendet aus. Manuelles Ausblenden bleibt erhalten, bis sich der W&K-Status ändert |
kbsgebiet |
wkCategoryText, wkCategories, wkCategoryPrimary |
Keine Zuordnung möglich → Storno + Hinweis |
kbpmemo, kbzusatz |
teaser, wkTopics |
Text wird HTML-sicher in Absätze bzw. Listen (- ) umgewandelt |
kbwmemo |
wkTags |
Kommagetrennt, dedupliziert |
kbkln1, kbmref |
wkInstructor, wkSpeakers |
Mitglieder nach Nachname; werden nur angelegt, wenn in den Einstellungen Mitgliedergruppen gesetzt sind (die Vorschau kündigt das an). „N.N.“ wird ignoriert |
kbanzmax, kbanztat |
wkMaxPlaces, wkPlacesLeft |
Freie Plätze = max − belegt |
kbkp1–kbkp6, kbktxp1–kbktxp6 |
wkPrice1–6, wkPriceType1–6 |
Dezimalzahlen |
kbkd* |
wkOrganizer* |
Straße und Landeskennzahl richtig zugeordnet (war vertauscht) |
kbkdemail |
wkOrganizerEmail |
E-Mail oder URL (Veranstaltungsart 3: Anmeldung beim Veranstalter) |
kbvort, kbgruppe, kbvsart, kbztermin |
location, wkAudience, wkEventType, wkAdditionalDates |
„geschlossener Teilnehmerkreis“ verhindert die Online-Anmeldung |
Das Euro-Zeichen, das W&K als „Ç“ exportiert, wird im Umfeld von Zahlen korrigiert. Aliase werden nur beim Anlegen erzeugt und bleiben danach stabil. Ein erneuter Import derselben Datei meldet alle Seminare als unverändert. Jeder Import wird protokolliert (W&K-Tools → Protokoll); die Datei liegt unter var/wk-sync/imports/. Alte Import- und Exportdateien räumt vendor/bin/contao-console wk-sync:purge --days=90 auf (z. B. per Cronjob); die Protokolleinträge bleiben erhalten.
Auf der Konsole:
vendor/bin/contao-console wk-sync:import files/Import/stva.csv 1 # Vorschau
vendor/bin/contao-console wk-sync:import files/Import/stva.csv 1 --apply # anwenden
Formularregeln
Welche Felder das Anmeldeformular zeigt, hängt von der Hauptkategorie (Art), dem Veranstaltungsort und der Veranstaltungsnummer ab:
- Lehrertätigkeit (
teacherPosition,schoolName,schoolCity): nur bei den Arten Schule und Evangelische Schulen. - Abweichende Rechnungsanschrift: bei Schulkursen nur, wenn „Ich unterrichte an einer Schule in sonstiger Trägerschaft“ gewählt ist; sonst immer.
- Übernachtung: nur bei mehrtägigen Veranstaltungen. Bei EXTP-Kursen und Elementarkursen im eigenen Haus erscheint stattdessen der Hinweis, dass Übernachtung und Verpflegung direkt im Gästehaus gebucht werden (Erklärungsfeld mit CSS-Klasse
wk-slot-no-overnight-stay). Ausnahme: Lehrerkurse außer Haus zeigen das Feld. - Vegetarisch: nicht bei Online-Kursen und nicht bei mehrtägigen EXTP-Kursen oder Elementarkursen (außer Tagungen); Lehrerkurse außer Haus zeigen das Feld.
- Abhängige Felder (Schule, Rechnungsanschrift, Nachricht) werden serverseitig nur validiert und gespeichert, wenn ihr steuerndes Feld sichtbar ist und den passenden Wert hat; ein kleines Skript (
bundles/contaowksync/wk-sync.js) blendet sie im Browser ein und aus.
Zusätzlich prüft das Bundle vor dem Anzeigen und noch einmal beim Absenden: veröffentlicht, nicht storniert, kein geschlossener Teilnehmerkreis, keine externe Anmeldung (Veranstaltungsart 3 mit E-Mail/URL), Anmeldeschluss, freie Plätze (W&K-Stand minus offene Anmeldungen, zusätzlich maximale Plätze).
Benachrichtigungs-Bedingungen
Vorrang: Benachrichtigung an der Veranstaltung (Legende „Anmeldung“) → erste erfüllte Bedingung in den Einstellungen → Standard-Benachrichtigung der Einstellungen. Eine zutreffende Bedingung ohne Zuordnung wird übersprungen.
| Nr. | Bedingung |
|---|---|
| 1 | Online-Kurs (Veranstaltungsort „Online“) |
| 2 / 5 | Tages- / Mehrtagesveranstaltung, Art Schule, Nummer beginnt mit EXTP |
| 3 / 7 | Tages- / Mehrtagesveranstaltung, Art Gemeinde, Gottesdienst mit Kindern oder Tagungen |
| 4 / 6 | Tages- / Mehrtagesveranstaltung, Art Elementarbereich |
| 8 / 9 | Tages- / Mehrtagesveranstaltung, Art Evangelische Schulen |
| 10 / 11 | Tages- / Mehrtagesveranstaltung, Art Mentoring |
| 12 | Mehrtägige Ausbildung |
| – | Alles andere (Schule ohne EXTP-Nummer, eintägige Ausbildung, Sonstiges wie AusZEIT) → Standard-Benachrichtigung |
Die Bedingungen sind bewusst fest im Code hinterlegt (NotificationRouter), weil sie kundenspezifisch sind; die Einstellungen zeigen sie als Matrix mit Erklärungen an jedem Auswahlfeld. Ohne Notification Center werden Anmeldungen gespeichert, aber keine Mails verschickt (Warnung im Systemprotokoll).
Folgeformulare (z. B. Workshop-Auswahl)
Die Bestätigungsseite wird mit ?booking=<Token> aufgerufen. Formulare des Formulargenerators auf dieser Seite erhalten beim Absenden automatisch die Felder subscription (ID der Anmeldung), subscription_email, subscription_firstname, subscription_lastname, event, event_title und event_wk_id – in Benachrichtigungen als ##form_subscription_email## usw. verwendbar. Eine eigene Bestätigungsseite lässt sich je Veranstaltung hinterlegen.
Posteingang und Export
W&K-Tools → Posteingang. Neue Anmeldungen und Kontaktanfragen werden gesichtet und einzeln oder gesammelt verschoben. Doppelte Anmeldungen (gleiche E-Mail-Adresse, gleiche Veranstaltung) werden markiert. „Bearbeiten“ öffnet den Datensatz unter W&K-Tools → Anmeldungen bzw. W&K-Tools → Kontaktanfragen; dort lassen sich alle Angaben korrigieren (Liste mit Filter nach Veranstaltung und Ordner, Suche nach Name/E-Mail; der Geburtstag wird wie im Formular als TT.MM.JJJJ gespeichert, so wie W&K es erwartet). Beim Löschen einer Veranstaltung werden ihre Anmeldungen mitgelöscht.
Export: In der Download-Liste erzeugt „Download-Liste für W&K exportieren“ die Datei JJJJ-MM-TT_website_kontaktanfragen_wk.csv mit den Spalten KBNR;KNKDAN;KNKDTITEL;KNKDN2;KNKDN1;KNKDSTR;KNKDPLZ;KNKDORT;KNGEBDAT;KDEMAIL;KDTEL1;KDFREI01;KDFREI02;KDFREI03;KDFREI04;KNUKAT;KNVEG;schoolName;schoolCity. Kontaktanfragen stehen ohne Kursnummer in der Datei. Die Datensätze werden als „Exportiert“ markiert, die freien Plätze der Veranstaltungen um die Zahl der Anmeldungen reduziert (bis zum nächsten W&K-Import), und die Datei bleibt unter W&K-Tools → Exporte zum erneuten Download (mehrere Exporte am selben Tag tragen den gleichen Dateinamen, liegen auf dem Server aber mit Zeitstempel getrennt unter var/wk-sync/exports/).
Berechtigungen
Alle Unterpunkte von „W&K-Tools“ sind normale Contao-Backend-Module und werden je Benutzergruppe unter Benutzergruppen → Erlaubte Module freigeschaltet oder ausgeblendet (Gruppe „W&K-Tools“: Import, Posteingang, Anmeldungen, Kontaktanfragen, Exporte, Kategorien, Einstellungen, Protokoll). Was nicht freigeschaltet ist, erscheint nicht in der linken Spalte und ist auch per URL nicht aufrufbar. Wie bei den Tabellen des Contao-Kerns gilt darüber hinaus: Unter Tabellenrechte (Spalten „Anlegen“, „Bearbeiten“, „Löschen“) legt die Gruppe fest, ob sie Datensätze der Tabellen tl_wk_booking, tl_wk_inquiry, tl_wk_category und tl_wk_config anlegen, bearbeiten und löschen darf, und unter Erlaubte Felder sind alle Felder dieser Tabellen sowie die W&K-Felder der Veranstaltungen einzeln freigebbar. Zum Bearbeiten von Anmeldungen braucht eine Gruppe also das Modul „Anmeldungen“, unter Tabellenrechte „Bearbeiten“ für tl_wk_booking und die gewünschten Felder; ein Zugriff auf den Kalender ist nicht nötig (die Anmeldungen hängen bewusst nicht als Untertabelle an den Veranstaltungen). Eine typische Redaktionsgruppe erhält Posteingang, Anmeldungen, Kontaktanfragen und Exporte plus die Felder von tl_wk_booking und tl_wk_inquiry; Import, Kategorien und Einstellungen bleiben der Leitung bzw. Administratoren vorbehalten.
Eigene Templates
- Frontend-Module:
frontend_module/wk_booking_form.html.twigundfrontend_module/wk_booking_status.html.twig(Blöckebooking_header,booking_status,booking_formbzw.status,inquiry). - Twig-Funktionen für eigene Event-Templates:
wk_booking_info(event.id)(Verfügbarkeit, freie Plätze, Anmeldeschluss, Links),wk_event_categories(event)(Kategorien),wk_event_people(event)(Kursleitung, Referenten).
Sicherheit und Robustheit
- Alle schreibenden Backend-Aktionen laufen über POST mit Request-Token; Ausgaben sind escaped.
- Upload nur
.csv/.txt, Dateien liegen außerhalb des Web-Roots (var/wk-sync/). - Import in einer Transaktion; fehlerhafte Zeilen brechen den Import nicht ab, sondern werden gemeldet.
- Verfügbarkeit wird beim Absenden erneut geprüft (Wettlauf um den letzten Platz).
- Anmeldungen und Anfragen werden nur mit bekannten Spalten gespeichert.
Contao 6
Das Bundle setzt Contao 6 voraus und nutzt dessen Konventionen:
- Output-Encoding: Contao speichert Eingaben unkodiert. Import, Anmeldungen und Export arbeiten durchgehend mit Klartext, die W&K-Datei enthält also nie HTML-Entities. Die Backend-Oberfläche ist Twig (automatisches Escaping); die Kategorienliste liefert ein kodiertes
RecordLabel. - Anzeigefelder: Die Prüfung der Einrichtung, die Erklärung der Benachrichtigungslogik und der Hinweis bei fehlenden Benachrichtigungen sind
input_field_callback-Felder ohne Spalte. Sie legen kein virtuelles Feld (jsonData) an. - Backend-Stylesheet:
public/wk-sync-backend.csswird übercontao.backend.custom_csseingebunden (Icon der Navigationsgruppe, Posteingang, Einstellungen). - Veranstaltungsfelder: Die 43
wk…-Felder intl_calendar_eventssind echte Spalten, keine virtuellen Felder, weil Import und Vorschau sie per SQL vergleichen und die Platzprüfung darauf rechnet. - Ohne Ersatz im Core: Das Backend-Modul (Import, Posteingang, Exporte) ist ein
BE_MOD-Callback-Modul, das Anmeldeformular wird überController::getForm()gerendert und die Veranstaltung wird überInput::get('auto_item')ermittelt. Alle drei sind in Contao 6 nicht als veraltet markiert.
Entwicklung
composer install
vendor/bin/phpunit
Die Tests decken CSV-Parsing, Zeilen-Zuordnung, Kategorie-Auflösung, Formularregeln, Benachrichtigungs-Routing, Textaufbereitung und den W&K-Export ab.
Backend-Bausteine, die sich auch anderswo nutzen lassen:
- Anzeigefelder in DCA-Formularen (Prüfung der Einrichtung, Erklärtexte) sind
input_field_callback-Felder: Feld ohneinputTypeundsqlmiteval.doNotShow, gerendert von einem Listener mit#[AsCallback(target: 'fields.<feld>.input_field')]. BackendMenuListenerhängt das Icon der Navigationsgruppe ein und nimmt die doppelte Escapierung zurück, mit der der Contao-Kern Modulbeschreibungen mit&in Tooltips ausgibt.