Backend group W&K-Tools with settings module instead of calendar fields

All W&K configuration moves from tl_calendar into the new table
tl_wk_config (one entry per calendar), edited under W&K-Tools → Einstellungen.
The backend group now has separate entries for import, inbox, bookings,
inquiries, exports, categories, settings and log. Notification selects show
the notification ID.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
admin.juergenandClaude Fable 5.1 committed 2026-09-24 18:08:20 +02:00
1 parent efce662b4f
commit c976f47e9c
26 files changed
+492 -360

No files matched your search

+13 -10
View File
@@ -38,7 +38,7 @@ vendor/bin/contao-console contao:migrate
## Einrichtung
1. **Kategorien anlegen** – Backend „W&K-Sync → Kategorien“ oder die Standardkategorien des TPI Moritzburg per Befehl:
1. **Kategorien anlegen** – Backend „W&K-Tools → Kategorien“ oder die Standardkategorien des TPI Moritzburg per Befehl:
```bash
vendor/bin/contao-console wk-sync:categories:seed
```
@@ -50,13 +50,16 @@ vendor/bin/contao-console contao:migrate
```
Die 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.
3. **Kalender konfigurieren** – im Kalender „W&K-Sync aktivieren“ und einstellen:
3. **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)
- Newsletter-Verteiler und Aktivierungsseite (optional)
- Benachrichtigungen: Standard sowie je Bedingung 1–12 (siehe unten)
- Benachrichtigungen: Standard sowie je Bedingung 1–12 (siehe unten); die Auswahl zeigt den Titel mit ID
`wk-sync:form:create --calendar=1` legt den Eintrag bei Bedarf mit an. Am Kalender selbst wird nichts konfiguriert.
4. **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.)
@@ -65,15 +68,15 @@ vendor/bin/contao-console contao:migrate
- Seite **Anfrage**: Formular des Formulargenerators mit „Daten in Tabelle speichern“ → `tl_wk_inquiry` (Feldnamen `gender`, `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.
5. **Benachrichtigungen** (Notification Center 2): Benachrichtigungen vom Typ **W&K-Anmeldung** anlegen und im Kalender zuordnen. 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-Namen `event_organizerCity` und `event_internalEventNumber` bleiben erhalten.
5. **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-Namen `event_organizerCity` und `event_internalEventNumber` bleiben erhalten.
6. **Benutzergruppen**: Zugriff auf die Module „W&K-Sync“ (Import & Posteingang, Anmeldungen, Kontaktanfragen, Kategorien, Protokoll) freigeben. Anmeldungen werden ausschließlich dort bearbeitet, nicht am Event im Kalender.
6. **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-Sync → Import & Posteingang → 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).
**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:
@@ -94,7 +97,7 @@ Regeln beim Import:
| `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. Jeder Import wird protokolliert (W&K-Sync → Protokoll); die Datei liegt unter `var/wk-sync/imports/`.
Das Euro-Zeichen, das W&K als „Ç“ exportiert, wird im Umfeld von Zahlen korrigiert. Aliase werden nur beim Anlegen erzeugt und bleiben danach stabil. Jeder Import wird protokolliert (W&K-Tools → Protokoll); die Datei liegt unter `var/wk-sync/imports/`.
Auf der Konsole:
@@ -119,7 +122,7 @@ Zusätzlich prüft das Bundle vor dem Anzeigen und noch einmal beim Absenden: ve
## Benachrichtigungs-Bedingungen
Vorrang: Benachrichtigung an der Veranstaltung → erste erfüllte Bedingung des Kalenders → Standard-Benachrichtigung des Kalenders.
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 |
|---|---|
@@ -139,9 +142,9 @@ Die Bestätigungsseite wird mit `?booking=<Token>` aufgerufen. Formulare des For
## Posteingang und Export
**W&K-Sync → Import & Posteingang → 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-Sync → Anmeldungen bzw. W&K-Sync → Kontaktanfragen; dort lassen sich alle Angaben korrigieren (Liste mit Filter nach Veranstaltung und Ordner, Suche nach Name/E-Mail). Beim Löschen einer Veranstaltung werden ihre Anmeldungen mitgelöscht.
**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). 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 „Exporte für W&K“ zum erneuten Download.
**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.
## Eigene Templates
+20 -9
View File
@@ -5,23 +5,34 @@ declare(strict_types=1);
use Contao\ArrayUtil;
use Mummert\ContaoWkSyncBundle\Backend\WkSyncModule;
// Backend-Bereich „W&K-Sync“ direkt nach „Inhalte“
// Backend-Bereich „W&K-Tools“ direkt nach „Inhalte“
ArrayUtil::arrayInsert($GLOBALS['BE_MOD'], 1, [
'wk_sync' => [
'wk_sync_tools' => [
'wk_tools' => [
'wk_import' => [
'callback' => WkSyncModule::class,
'stylesheet' => 'bundles/contaowksync/wk-sync-backend.css',
],
'wk_sync_bookings' => [
'wk_inbox' => [
'callback' => WkSyncModule::class,
'stylesheet' => 'bundles/contaowksync/wk-sync-backend.css',
],
'wk_bookings' => [
'tables' => ['tl_wk_booking'],
],
'wk_sync_categories' => [
'tables' => ['tl_wk_category'],
],
'wk_sync_inquiries' => [
'wk_inquiries' => [
'tables' => ['tl_wk_inquiry'],
],
'wk_sync_log' => [
'wk_exports' => [
'callback' => WkSyncModule::class,
'stylesheet' => 'bundles/contaowksync/wk-sync-backend.css',
],
'wk_categories' => [
'tables' => ['tl_wk_category'],
],
'wk_config' => [
'tables' => ['tl_wk_config'],
],
'wk_log' => [
'tables' => ['tl_wk_sync_log'],
],
],
-149
View File
@@ -1,149 +0,0 @@
<?php
declare(strict_types=1);
use Contao\CoreBundle\DataContainer\PaletteManipulator;
use Mummert\ContaoWkSyncBundle\Booking\NotificationRouter;
$dca = &$GLOBALS['TL_DCA']['tl_calendar'];
$dca['palettes']['__selector__'][] = 'wkEnable';
$notificationFields = ['wkNotificationDefault'];
foreach (NotificationRouter::CONDITIONS as $condition) {
$notificationFields[] = 'wkNotificationExplanation'.$condition;
$notificationFields[] = 'wkNotification'.$condition;
}
$dca['subpalettes']['wkEnable'] = implode(',', [
'wkBookingForm', 'wkBookingPage', 'wkConfirmationPage', 'wkInquiryPage',
'wkDeadlineDays', 'wkDeadlineDisplayDays', 'wkOwnVenues',
'wkAuthor', 'wkInstructorGroup', 'wkSpeakerGroup',
'wkNewsletterChannels', 'wkNewsletterPage', 'wkNewsletterText',
...$notificationFields,
]);
PaletteManipulator::create()
->addLegend('wk_legend', 'title_legend', PaletteManipulator::POSITION_AFTER)
->addField('wkEnable', 'wk_legend', PaletteManipulator::POSITION_APPEND)
->applyToPalette('default', 'tl_calendar')
;
$fields = [
'wkEnable' => [
'filter' => true,
'inputType' => 'checkbox',
'eval' => ['submitOnChange' => true, 'tl_class' => 'w50 m12'],
'sql' => ['type' => 'boolean', 'default' => false],
],
'wkBookingForm' => [
'inputType' => 'select',
'foreignKey' => 'tl_form.title',
'eval' => ['includeBlankOption' => true, 'chosen' => true, 'tl_class' => 'w50 clr'],
'sql' => ['type' => 'integer', 'unsigned' => true, 'default' => 0],
'relation' => ['type' => 'hasOne', 'load' => 'lazy'],
],
'wkBookingPage' => [
'inputType' => 'pageTree',
'foreignKey' => 'tl_page.title',
'eval' => ['fieldType' => 'radio', 'tl_class' => 'clr'],
'sql' => ['type' => 'integer', 'unsigned' => true, 'default' => 0],
'relation' => ['type' => 'hasOne', 'load' => 'lazy'],
],
'wkConfirmationPage' => [
'inputType' => 'pageTree',
'foreignKey' => 'tl_page.title',
'eval' => ['fieldType' => 'radio', 'tl_class' => 'clr'],
'sql' => ['type' => 'integer', 'unsigned' => true, 'default' => 0],
'relation' => ['type' => 'hasOne', 'load' => 'lazy'],
],
'wkInquiryPage' => [
'inputType' => 'pageTree',
'foreignKey' => 'tl_page.title',
'eval' => ['fieldType' => 'radio', 'tl_class' => 'clr'],
'sql' => ['type' => 'integer', 'unsigned' => true, 'default' => 0],
'relation' => ['type' => 'hasOne', 'load' => 'lazy'],
],
'wkDeadlineDays' => [
'default' => 1,
'inputType' => 'text',
'eval' => ['rgxp' => 'natural', 'maxlength' => 3, 'tl_class' => 'w50 clr'],
'sql' => ['type' => 'integer', 'unsigned' => true, 'default' => 1],
],
'wkDeadlineDisplayDays' => [
'default' => 31,
'inputType' => 'text',
'eval' => ['rgxp' => 'natural', 'maxlength' => 3, 'tl_class' => 'w50'],
'sql' => ['type' => 'integer', 'unsigned' => true, 'default' => 31],
],
'wkOwnVenues' => [
'default' => 'TPI Moritzburg',
'inputType' => 'textarea',
'eval' => ['style' => 'height:60px', 'decodeEntities' => true, 'tl_class' => 'clr'],
'sql' => ['type' => 'text', 'notnull' => false],
],
'wkAuthor' => [
'inputType' => 'select',
'foreignKey' => 'tl_user.name',
'eval' => ['includeBlankOption' => true, 'chosen' => true, 'tl_class' => 'w50 clr'],
'sql' => ['type' => 'integer', 'unsigned' => true, 'default' => 0],
'relation' => ['type' => 'hasOne', 'load' => 'lazy'],
],
'wkInstructorGroup' => [
'inputType' => 'select',
'foreignKey' => 'tl_member_group.name',
'eval' => ['includeBlankOption' => true, 'chosen' => true, 'tl_class' => 'w50 clr'],
'sql' => ['type' => 'integer', 'unsigned' => true, 'default' => 0],
'relation' => ['type' => 'hasOne', 'load' => 'lazy'],
],
'wkSpeakerGroup' => [
'inputType' => 'select',
'foreignKey' => 'tl_member_group.name',
'eval' => ['includeBlankOption' => true, 'chosen' => true, 'tl_class' => 'w50'],
'sql' => ['type' => 'integer', 'unsigned' => true, 'default' => 0],
'relation' => ['type' => 'hasOne', 'load' => 'lazy'],
],
'wkNewsletterChannels' => [
'inputType' => 'checkbox',
'eval' => ['multiple' => true, 'tl_class' => 'clr'],
'sql' => ['type' => 'blob', 'length' => 65535, 'notnull' => false],
],
'wkNewsletterPage' => [
'inputType' => 'pageTree',
'foreignKey' => 'tl_page.title',
'eval' => ['fieldType' => 'radio', 'tl_class' => 'clr'],
'sql' => ['type' => 'integer', 'unsigned' => true, 'default' => 0],
'relation' => ['type' => 'hasOne', 'load' => 'lazy'],
],
'wkNewsletterText' => [
'inputType' => 'textarea',
'eval' => ['style' => 'height:120px', 'decodeEntities' => true, 'tl_class' => 'clr'],
'sql' => ['type' => 'text', 'notnull' => false],
],
'wkNotificationDefault' => [
'inputType' => 'select',
'eval' => ['includeBlankOption' => true, 'chosen' => true, 'tl_class' => 'w50 clr'],
'sql' => ['type' => 'integer', 'unsigned' => true, 'default' => 0],
],
];
foreach (NotificationRouter::CONDITIONS as $condition) {
$fields['wkNotificationExplanation'.$condition] = [
'inputType' => 'explanation',
'eval' => [
'class' => 'tl_info',
'text' => &$GLOBALS['TL_LANG']['tl_calendar']['wkNotificationExplanation'.$condition],
'tl_class' => 'long clr',
],
];
$fields['wkNotification'.$condition] = [
'label' => &$GLOBALS['TL_LANG']['tl_calendar']['wkNotificationCondition'],
'inputType' => 'select',
'eval' => ['includeBlankOption' => true, 'chosen' => true, 'tl_class' => 'w50 clr'],
'sql' => ['type' => 'integer', 'unsigned' => true, 'default' => 0],
];
}
$dca['fields'] = array_merge($dca['fields'], $fields);
+183
View File
@@ -0,0 +1,183 @@
<?php
declare(strict_types=1);
use Contao\DataContainer;
use Contao\DC_Table;
use Mummert\ContaoWkSyncBundle\Booking\NotificationRouter;
$notificationFields = ['notificationDefault'];
foreach (NotificationRouter::CONDITIONS as $condition) {
$notificationFields[] = 'notificationExplanation'.$condition;
$notificationFields[] = 'notification'.$condition;
}
$GLOBALS['TL_DCA']['tl_wk_config'] = [
'config' => [
'dataContainer' => DC_Table::class,
'enableVersioning' => true,
'markAsCopy' => 'title',
'sql' => [
'keys' => [
'id' => 'primary',
'calendar' => 'unique',
],
],
],
'list' => [
'sorting' => [
'mode' => DataContainer::MODE_SORTED,
'fields' => ['title'],
'flag' => DataContainer::SORT_INITIAL_LETTER_ASC,
'panelLayout' => 'search,limit',
],
'label' => [
'fields' => ['title', 'calendar', 'bookingForm'],
'showColumns' => true,
],
'operations' => [
'edit',
'copy',
'delete',
'show',
],
],
'palettes' => [
'default' => '{title_legend},title,calendar;'
.'{page_legend},bookingForm,bookingPage,confirmationPage,inquiryPage;'
.'{booking_legend},deadlineDays,deadlineDisplayDays,ownVenues;'
.'{import_legend},author,instructorGroup,speakerGroup;'
.'{newsletter_legend:hide},newsletterChannels,newsletterPage,newsletterText;'
.'{notification_legend},'.implode(',', $notificationFields),
],
'fields' => [
'id' => [
'sql' => ['type' => 'integer', 'unsigned' => true, 'autoincrement' => true],
],
'tstamp' => [
'sql' => ['type' => 'integer', 'unsigned' => true, 'default' => 0],
],
'title' => [
'search' => true,
'inputType' => 'text',
'eval' => ['mandatory' => true, 'basicEntities' => true, 'maxlength' => 255, 'tl_class' => 'w50'],
'sql' => ['type' => 'string', 'length' => 255, 'default' => ''],
],
'calendar' => [
'filter' => true,
'inputType' => 'select',
'foreignKey' => 'tl_calendar.title',
'eval' => ['mandatory' => true, 'unique' => true, 'includeBlankOption' => true, 'chosen' => true, 'tl_class' => 'w50'],
'sql' => ['type' => 'integer', 'unsigned' => true, 'default' => 0],
'relation' => ['type' => 'hasOne', 'load' => 'lazy'],
],
'bookingForm' => [
'inputType' => 'select',
'foreignKey' => 'tl_form.title',
'eval' => ['includeBlankOption' => true, 'chosen' => true, 'tl_class' => 'w50'],
'sql' => ['type' => 'integer', 'unsigned' => true, 'default' => 0],
'relation' => ['type' => 'hasOne', 'load' => 'lazy'],
],
'bookingPage' => [
'inputType' => 'pageTree',
'foreignKey' => 'tl_page.title',
'eval' => ['fieldType' => 'radio', 'tl_class' => 'clr'],
'sql' => ['type' => 'integer', 'unsigned' => true, 'default' => 0],
'relation' => ['type' => 'hasOne', 'load' => 'lazy'],
],
'confirmationPage' => [
'inputType' => 'pageTree',
'foreignKey' => 'tl_page.title',
'eval' => ['fieldType' => 'radio', 'tl_class' => 'clr'],
'sql' => ['type' => 'integer', 'unsigned' => true, 'default' => 0],
'relation' => ['type' => 'hasOne', 'load' => 'lazy'],
],
'inquiryPage' => [
'inputType' => 'pageTree',
'foreignKey' => 'tl_page.title',
'eval' => ['fieldType' => 'radio', 'tl_class' => 'clr'],
'sql' => ['type' => 'integer', 'unsigned' => true, 'default' => 0],
'relation' => ['type' => 'hasOne', 'load' => 'lazy'],
],
'deadlineDays' => [
'default' => 1,
'inputType' => 'text',
'eval' => ['rgxp' => 'natural', 'maxlength' => 3, 'tl_class' => 'w50'],
'sql' => ['type' => 'integer', 'unsigned' => true, 'default' => 1],
],
'deadlineDisplayDays' => [
'default' => 31,
'inputType' => 'text',
'eval' => ['rgxp' => 'natural', 'maxlength' => 3, 'tl_class' => 'w50'],
'sql' => ['type' => 'integer', 'unsigned' => true, 'default' => 31],
],
'ownVenues' => [
'default' => 'TPI Moritzburg',
'inputType' => 'textarea',
'eval' => ['style' => 'height:60px', 'decodeEntities' => true, 'tl_class' => 'clr'],
'sql' => ['type' => 'text', 'notnull' => false],
],
'author' => [
'inputType' => 'select',
'foreignKey' => 'tl_user.name',
'eval' => ['includeBlankOption' => true, 'chosen' => true, 'tl_class' => 'w50'],
'sql' => ['type' => 'integer', 'unsigned' => true, 'default' => 0],
'relation' => ['type' => 'hasOne', 'load' => 'lazy'],
],
'instructorGroup' => [
'inputType' => 'select',
'foreignKey' => 'tl_member_group.name',
'eval' => ['includeBlankOption' => true, 'chosen' => true, 'tl_class' => 'w50 clr'],
'sql' => ['type' => 'integer', 'unsigned' => true, 'default' => 0],
'relation' => ['type' => 'hasOne', 'load' => 'lazy'],
],
'speakerGroup' => [
'inputType' => 'select',
'foreignKey' => 'tl_member_group.name',
'eval' => ['includeBlankOption' => true, 'chosen' => true, 'tl_class' => 'w50'],
'sql' => ['type' => 'integer', 'unsigned' => true, 'default' => 0],
'relation' => ['type' => 'hasOne', 'load' => 'lazy'],
],
'newsletterChannels' => [
'inputType' => 'checkbox',
'eval' => ['multiple' => true, 'tl_class' => 'clr'],
'sql' => ['type' => 'blob', 'length' => 65535, 'notnull' => false],
],
'newsletterPage' => [
'inputType' => 'pageTree',
'foreignKey' => 'tl_page.title',
'eval' => ['fieldType' => 'radio', 'tl_class' => 'clr'],
'sql' => ['type' => 'integer', 'unsigned' => true, 'default' => 0],
'relation' => ['type' => 'hasOne', 'load' => 'lazy'],
],
'newsletterText' => [
'inputType' => 'textarea',
'eval' => ['style' => 'height:120px', 'decodeEntities' => true, 'tl_class' => 'clr'],
'sql' => ['type' => 'text', 'notnull' => false],
],
'notificationDefault' => [
'inputType' => 'select',
'eval' => ['includeBlankOption' => true, 'chosen' => true, 'tl_class' => 'w50 clr'],
'sql' => ['type' => 'integer', 'unsigned' => true, 'default' => 0],
],
],
];
foreach (NotificationRouter::CONDITIONS as $condition) {
$GLOBALS['TL_DCA']['tl_wk_config']['fields']['notificationExplanation'.$condition] = [
'inputType' => 'explanation',
'eval' => [
'class' => 'tl_info',
'text' => &$GLOBALS['TL_LANG']['tl_wk_config']['notificationExplanation'.$condition],
'tl_class' => 'long clr',
],
];
$GLOBALS['TL_DCA']['tl_wk_config']['fields']['notification'.$condition] = [
'label' => &$GLOBALS['TL_LANG']['tl_wk_config']['notificationCondition'],
'inputType' => 'select',
'eval' => ['includeBlankOption' => true, 'chosen' => true, 'tl_class' => 'w50 clr'],
'sql' => ['type' => 'integer', 'unsigned' => true, 'default' => 0],
];
}
+2 -1
View File
@@ -64,9 +64,10 @@ $lang['wk_sync']['import']['file_help'] = 'Die in W&K exportierte Seminarliste (
$lang['wk_sync']['import']['hint'] = 'Wichtig: In W&K das größtmögliche Zeitintervall exportieren. Seminare, die nicht in der Datei stehen, werden als storniert markiert und ausgeblendet. Nach dem Hochladen erscheint zuerst eine Vorschau, erst nach Bestätigung wird etwas geändert.';
$lang['wk_sync']['import']['upload_button'] = 'Datei hochladen und Vorschau anzeigen';
$lang['wk_sync']['import']['recent'] = 'Letzte Importe';
$lang['wk_sync']['import']['open_config'] = 'Einstellungen öffnen';
$lang['wk_sync']['import']['none'] = 'Bisher wurde noch nichts importiert.';
$lang['wk_sync']['import']['no_categories'] = 'Es sind noch keine W&K-Kategorien angelegt. Ohne Kategorien werden alle Seminare storniert. Bitte zuerst unter „W&K-Sync → Kategorien“ anlegen (oder „wk-sync:categories:seed“ ausführen).';
$lang['wk_sync']['import']['no_calendar'] = 'In keinem Kalender ist der W&K-Sync aktiviert. Bitte im gewünschten Kalender „W&K-Sync aktivieren“ einschalten.';
$lang['wk_sync']['import']['no_calendar'] = 'Es sind noch keine W&K-Einstellungen angelegt (Kalender, Formular, Seiten, Benachrichtigungen).';
$lang['wk_sync']['import']['applied'] = 'Der Import wurde angewendet: %s neu, %s geändert, %s storniert, %s unverändert.';
$lang['wk_sync']['import']['not_found'] = 'Der Importvorgang wurde nicht gefunden.';
$lang['wk_sync']['import']['discarded'] = 'Die Vorschau wurde verworfen. Es wurde nichts geändert.';
+10 -7
View File
@@ -2,13 +2,16 @@
declare(strict_types=1);
$GLOBALS['TL_LANG']['MOD']['wk_sync'] = 'W&K-Sync';
$GLOBALS['TL_LANG']['MOD']['wk_sync_tools'] = ['Import & Posteingang', 'Seminare aus W&K importieren, Anmeldungen bearbeiten und für W&K exportieren'];
$GLOBALS['TL_LANG']['MOD']['wk_sync_categories'] = ['Kategorien', 'W&K-Kategorien (Bereiche) und ihre Zuordnung pflegen'];
$GLOBALS['TL_LANG']['MOD']['wk_sync_inquiries'] = ['Kontaktanfragen', 'Anfragen aus dem Anfrageformular'];
$GLOBALS['TL_LANG']['MOD']['wk_sync_log'] = ['Protokoll', 'Protokoll der Importe und Exporte'];
$GLOBALS['TL_LANG']['MOD']['wk_tools'] = 'W&K-Tools';
$GLOBALS['TL_LANG']['MOD']['wk_import'] = ['Import', 'Seminare aus einer W&K-Datei importieren (mit Vorschau)'];
$GLOBALS['TL_LANG']['MOD']['wk_inbox'] = ['Posteingang', 'Anmeldungen und Kontaktanfragen sichten, in Ordner verschieben und für W&K exportieren'];
$GLOBALS['TL_LANG']['MOD']['wk_bookings'] = ['Anmeldungen', 'Anmeldungen zu Veranstaltungen ansehen und bearbeiten'];
$GLOBALS['TL_LANG']['MOD']['wk_inquiries'] = ['Kontaktanfragen', 'Anfragen aus dem Anfrageformular'];
$GLOBALS['TL_LANG']['MOD']['wk_exports'] = ['Exporte', 'Erzeugte Exportdateien für W&K herunterladen'];
$GLOBALS['TL_LANG']['MOD']['wk_categories'] = ['Kategorien', 'W&K-Kategorien (Bereiche) und ihre Zuordnung pflegen'];
$GLOBALS['TL_LANG']['MOD']['wk_config'] = ['Einstellungen', 'Kalender, Formular, Seiten, Anmeldeschluss und Benachrichtigungen'];
$GLOBALS['TL_LANG']['MOD']['wk_log'] = ['Protokoll', 'Protokoll der Importe und Exporte'];
$GLOBALS['TL_LANG']['FMD']['wk_sync'] = 'W&K-Sync';
$GLOBALS['TL_LANG']['FMD']['wk_sync'] = 'W&K-Tools';
$GLOBALS['TL_LANG']['FMD']['wk_booking_form'] = ['W&K-Anmeldeformular', 'Anmeldeformular zur Veranstaltung aus der URL (Alias der Veranstaltung).'];
$GLOBALS['TL_LANG']['FMD']['wk_booking_status'] = ['W&K-Anmeldestatus', 'Freie Plätze, Anmeldeschluss und Link zur Anmeldung für die Veranstaltung aus der URL.'];
$GLOBALS['TL_LANG']['MOD']['wk_sync_bookings'] = ['Anmeldungen', 'Anmeldungen zu Veranstaltungen ansehen und bearbeiten'];
-37
View File
@@ -1,37 +0,0 @@
<?php
declare(strict_types=1);
$lang = &$GLOBALS['TL_LANG']['tl_calendar'];
$lang['wk_legend'] = 'W&K-Sync';
$lang['wkEnable'] = ['W&K-Sync aktivieren', 'Seminare aus W&K in diesen Kalender importieren und Online-Anmeldungen ermöglichen.'];
$lang['wkBookingForm'] = ['Anmeldeformular', 'Formular aus dem Formulargenerator, das für Anmeldungen verwendet wird (Feldnamen wie in tl_wk_booking; anlegen mit „wk-sync:form:create“).'];
$lang['wkBookingPage'] = ['Seite mit dem Anmeldeformular', 'Seite, auf der das Modul „W&K-Anmeldeformular“ liegt. Der Link „Jetzt anmelden“ führt dorthin.'];
$lang['wkConfirmationPage'] = ['Bestätigungsseite', 'Seite nach erfolgreicher Anmeldung, z. B. „Anmeldung eingegangen“. Kann je Veranstaltung übersteuert werden.'];
$lang['wkInquiryPage'] = ['Seite mit dem Anfrageformular', 'Seite mit dem Kontakt-/Anfrageformular; sie wird mit ?event=ID verlinkt.'];
$lang['wkDeadlineDays'] = ['Anmeldeschluss (Tage vor Beginn)', 'Bis so viele Tage vor Beginn sind Anmeldungen technisch möglich. 1 = bis einen Tag vor Beginn.'];
$lang['wkDeadlineDisplayDays'] = ['Angezeigter Anmeldeschluss (Tage vor Beginn)', 'Datum, das als „Anmeldung möglich bis“ angezeigt wird. 0 = wie der technische Anmeldeschluss.'];
$lang['wkOwnVenues'] = ['Eigene Veranstaltungsorte', 'Eine Bezeichnung pro Zeile. Veranstaltungsorte, die so beginnen (z. B. „TPI Moritzburg (barrierefrei)“), gelten als „im eigenen Haus“ – relevant für die Felder Übernachtung und Verpflegung.'];
$lang['wkAuthor'] = ['Autor importierter Veranstaltungen', 'Backend-Benutzer, der als Autor neuer Seminare eingetragen wird (leer = der importierende Benutzer).'];
$lang['wkInstructorGroup'] = ['Mitgliedergruppe Kursleitung', 'Kursleitungen aus W&K werden als Mitglieder dieser Gruppe angelegt (leer = keine Mitglieder anlegen).'];
$lang['wkSpeakerGroup'] = ['Mitgliedergruppe Referenten', 'Referenten aus W&K werden als Mitglieder dieser Gruppe angelegt (leer = keine Mitglieder anlegen).'];
$lang['wkNewsletterChannels'] = ['Newsletter-Verteiler', 'Verteiler, die bei „Newsletter abonnieren“ mit Double-Opt-in abonniert werden.'];
$lang['wkNewsletterPage'] = ['Seite zur Newsletter-Aktivierung', 'Seite mit dem Modul „Abonnieren“, die den Aktivierungslink (?token=) verarbeitet.'];
$lang['wkNewsletterText'] = ['Text der Aktivierungsmail', 'Optional. Token: ##link##, ##channels##, ##domain##. Leer = Standardtext.'];
$lang['wkNotificationDefault'] = ['Standard-Benachrichtigung', 'Wird verschickt, wenn keine der Bedingungen zutrifft und an der Veranstaltung nichts eingestellt ist.'];
$lang['wkNotificationCondition'] = ['Benachrichtigung', 'Benachrichtigung vom Typ „W&K-Anmeldung“ für diese Bedingung.'];
$lang['wkNotificationExplanation1'] = '<strong>Bedingung 1: Online-Kurse</strong> – alle Kategorien, Veranstaltungsort „Online“.';
$lang['wkNotificationExplanation2'] = '<strong>Bedingung 2: Tagesveranstaltung Schulfortbildung</strong> – Beginn = Ende, Kategorie-Art „Schule“, Nummer beginnt mit EXTP.';
$lang['wkNotificationExplanation3'] = '<strong>Bedingung 3: Tagesveranstaltung Gemeinde / Gottesdienst mit Kindern / Tagungen</strong> – Beginn = Ende, Kategorie-Art „Gemeinde“, „Gottesdienst mit Kindern“ oder „Tagungen“.';
$lang['wkNotificationExplanation4'] = '<strong>Bedingung 4: Tagesveranstaltung Elementarbereich</strong> – Beginn = Ende, Kategorie-Art „Elementarbereich“.';
$lang['wkNotificationExplanation5'] = '<strong>Bedingung 5: Mehrtägige Schulfortbildung</strong> – Beginn ≠ Ende, Kategorie-Art „Schule“, Nummer beginnt mit EXTP.';
$lang['wkNotificationExplanation6'] = '<strong>Bedingung 6: Mehrtägige Fortbildung Elementarbereich</strong> – Beginn ≠ Ende, Kategorie-Art „Elementarbereich“.';
$lang['wkNotificationExplanation7'] = '<strong>Bedingung 7: Mehrtägige Veranstaltung Gemeinde / Gottesdienst mit Kindern / Tagungen</strong> – Beginn ≠ Ende.';
$lang['wkNotificationExplanation8'] = '<strong>Bedingung 8: Tagesveranstaltung Evangelische Schulen</strong> – Beginn = Ende, Kategorie-Art „Evangelische Schulen“.';
$lang['wkNotificationExplanation9'] = '<strong>Bedingung 9: Mehrtägige Veranstaltung Evangelische Schulen</strong> – Beginn ≠ Ende, Kategorie-Art „Evangelische Schulen“.';
$lang['wkNotificationExplanation10'] = '<strong>Bedingung 10: Tagesveranstaltung Mentoring</strong> – Beginn = Ende, Kategorie-Art „Mentoring“.';
$lang['wkNotificationExplanation11'] = '<strong>Bedingung 11: Mehrtägige Veranstaltung Mentoring</strong> – Beginn ≠ Ende, Kategorie-Art „Mentoring“.';
$lang['wkNotificationExplanation12'] = '<strong>Bedingung 12: Mehrtägige Ausbildung</strong> – Beginn ≠ Ende, Kategorie-Art „Ausbildung“.';
+49
View File
@@ -0,0 +1,49 @@
<?php
declare(strict_types=1);
$lang = &$GLOBALS['TL_LANG']['tl_wk_config'];
$lang['title_legend'] = 'Kalender';
$lang['page_legend'] = 'Formular und Seiten';
$lang['booking_legend'] = 'Anmeldung';
$lang['import_legend'] = 'Import';
$lang['newsletter_legend'] = 'Newsletter';
$lang['notification_legend'] = 'Benachrichtigungen';
$lang['title'] = ['Bezeichnung', 'Interner Name dieser Einstellungen, z. B. „Programm“.'];
$lang['calendar'] = ['Kalender', 'Kalender, in den die W&K-Seminare importiert werden und dessen Veranstaltungen online buchbar sind. Je Kalender ein Eintrag.'];
$lang['bookingForm'] = ['Anmeldeformular', 'Formular aus dem Formulargenerator, das für Anmeldungen verwendet wird (Feldnamen wie in tl_wk_booking; anlegen mit „wk-sync:form:create“).'];
$lang['bookingPage'] = ['Seite mit dem Anmeldeformular', 'Seite, auf der das Modul „W&K-Anmeldeformular“ liegt. Der Link „Jetzt anmelden“ führt dorthin.'];
$lang['confirmationPage'] = ['Bestätigungsseite', 'Seite nach erfolgreicher Anmeldung, z. B. „Anmeldung eingegangen“. Kann je Veranstaltung übersteuert werden.'];
$lang['inquiryPage'] = ['Seite mit dem Anfrageformular', 'Seite mit dem Kontakt-/Anfrageformular; sie wird mit ?event=ID verlinkt.'];
$lang['deadlineDays'] = ['Anmeldeschluss (Tage vor Beginn)', 'Bis so viele Tage vor Beginn sind Anmeldungen technisch möglich. 1 = bis einen Tag vor Beginn.'];
$lang['deadlineDisplayDays'] = ['Angezeigter Anmeldeschluss (Tage vor Beginn)', 'Datum, das als „Anmeldung möglich bis“ angezeigt wird. 0 = wie der technische Anmeldeschluss.'];
$lang['ownVenues'] = ['Eigene Veranstaltungsorte', 'Eine Bezeichnung pro Zeile. Veranstaltungsorte, die so beginnen (z. B. „TPI Moritzburg (barrierefrei)“), gelten als „im eigenen Haus“ – relevant für die Felder Übernachtung und Verpflegung.'];
$lang['author'] = ['Autor importierter Veranstaltungen', 'Backend-Benutzer, der als Autor neuer Seminare eingetragen wird (leer = der importierende Benutzer).'];
$lang['instructorGroup'] = ['Mitgliedergruppe Kursleitung', 'Kursleitungen aus W&K werden als Mitglieder dieser Gruppe angelegt (leer = keine Mitglieder anlegen).'];
$lang['speakerGroup'] = ['Mitgliedergruppe Referenten', 'Referenten aus W&K werden als Mitglieder dieser Gruppe angelegt (leer = keine Mitglieder anlegen).'];
$lang['newsletterChannels'] = ['Newsletter-Verteiler', 'Verteiler, die bei „Newsletter abonnieren“ mit Double-Opt-in abonniert werden.'];
$lang['newsletterPage'] = ['Seite zur Newsletter-Aktivierung', 'Seite mit dem Modul „Abonnieren“, die den Aktivierungslink (?token=) verarbeitet.'];
$lang['newsletterText'] = ['Text der Aktivierungsmail', 'Optional. Token: ##link##, ##channels##, ##domain##. Leer = Standardtext.'];
$lang['notificationDefault'] = ['Standard-Benachrichtigung', 'Wird verschickt, wenn keine der Bedingungen zutrifft und an der Veranstaltung nichts eingestellt ist. Nur Benachrichtigungen vom Typ „W&K-Anmeldung“.'];
$lang['notificationCondition'] = ['Benachrichtigung für diese Bedingung', 'Leer = Bedingung wird übersprungen, die nächste zutreffende Bedingung zählt.'];
$lang['notificationExplanation1'] = '<strong>Bedingung 1: Online-Kurse</strong> – alle Kategorien, Veranstaltungsort „Online“.';
$lang['notificationExplanation2'] = '<strong>Bedingung 2: Tagesveranstaltung Schulfortbildung</strong> – Beginn = Ende, Kategorie-Art „Schule“, Nummer beginnt mit EXTP.';
$lang['notificationExplanation3'] = '<strong>Bedingung 3: Tagesveranstaltung Gemeinde / Gottesdienst mit Kindern / Tagungen</strong> – Beginn = Ende, Kategorie-Art „Gemeinde“, „Gottesdienst mit Kindern“ oder „Tagungen“.';
$lang['notificationExplanation4'] = '<strong>Bedingung 4: Tagesveranstaltung Elementarbereich</strong> – Beginn = Ende, Kategorie-Art „Elementarbereich“.';
$lang['notificationExplanation5'] = '<strong>Bedingung 5: Mehrtägige Schulfortbildung</strong> – Beginn ≠ Ende, Kategorie-Art „Schule“, Nummer beginnt mit EXTP.';
$lang['notificationExplanation6'] = '<strong>Bedingung 6: Mehrtägige Fortbildung Elementarbereich</strong> – Beginn ≠ Ende, Kategorie-Art „Elementarbereich“.';
$lang['notificationExplanation7'] = '<strong>Bedingung 7: Mehrtägige Veranstaltung Gemeinde / Gottesdienst mit Kindern / Tagungen</strong> – Beginn ≠ Ende.';
$lang['notificationExplanation8'] = '<strong>Bedingung 8: Tagesveranstaltung Evangelische Schulen</strong> – Beginn = Ende, Kategorie-Art „Evangelische Schulen“.';
$lang['notificationExplanation9'] = '<strong>Bedingung 9: Mehrtägige Veranstaltung Evangelische Schulen</strong> – Beginn ≠ Ende, Kategorie-Art „Evangelische Schulen“.';
$lang['notificationExplanation10'] = '<strong>Bedingung 10: Tagesveranstaltung Mentoring</strong> – Beginn = Ende, Kategorie-Art „Mentoring“.';
$lang['notificationExplanation11'] = '<strong>Bedingung 11: Mehrtägige Veranstaltung Mentoring</strong> – Beginn ≠ Ende, Kategorie-Art „Mentoring“.';
$lang['notificationExplanation12'] = '<strong>Bedingung 12: Mehrtägige Ausbildung</strong> – Beginn ≠ Ende, Kategorie-Art „Ausbildung“.';
$lang['new'] = ['Neue Einstellungen', 'Einstellungen für einen Kalender anlegen'];
$lang['edit'] = ['Einstellungen bearbeiten', 'Einstellungen ID %s bearbeiten'];
$lang['copy'] = ['Einstellungen duplizieren', 'Einstellungen ID %s duplizieren'];
$lang['delete'] = ['Einstellungen löschen', 'Einstellungen ID %s löschen'];
$lang['show'] = ['Details', 'Details der Einstellungen ID %s anzeigen'];
+2 -1
View File
@@ -60,9 +60,10 @@ $lang['wk_sync']['import']['file_help'] = 'The seminar list exported from W&K (s
$lang['wk_sync']['import']['hint'] = 'Important: export the largest possible time range in W&K. Seminars missing from the file will be marked as cancelled and hidden. A preview is shown first; nothing changes until you confirm.';
$lang['wk_sync']['import']['upload_button'] = 'Upload file and show preview';
$lang['wk_sync']['import']['recent'] = 'Recent imports';
$lang['wk_sync']['import']['open_config'] = 'Open settings';
$lang['wk_sync']['import']['none'] = 'Nothing has been imported yet.';
$lang['wk_sync']['import']['no_categories'] = 'No W&K categories exist yet. Without categories all seminars would be cancelled. Please create them under "W&K sync → Categories" first (or run "wk-sync:categories:seed").';
$lang['wk_sync']['import']['no_calendar'] = 'W&K sync is not enabled in any calendar. Please enable "W&K sync" in the calendar settings.';
$lang['wk_sync']['import']['no_calendar'] = 'No W&K settings exist yet (calendar, form, pages, notifications).';
$lang['wk_sync']['import']['applied'] = 'The import has been applied: %s new, %s changed, %s cancelled, %s unchanged.';
$lang['wk_sync']['import']['not_found'] = 'The import was not found.';
$lang['wk_sync']['import']['discarded'] = 'The preview has been discarded. Nothing was changed.';
+10 -7
View File
@@ -2,13 +2,16 @@
declare(strict_types=1);
$GLOBALS['TL_LANG']['MOD']['wk_sync'] = 'W&K sync';
$GLOBALS['TL_LANG']['MOD']['wk_sync_tools'] = ['Import & inbox', 'Import seminars from W&K, process bookings and export them for W&K'];
$GLOBALS['TL_LANG']['MOD']['wk_sync_categories'] = ['Categories', 'Manage W&K categories and their mapping'];
$GLOBALS['TL_LANG']['MOD']['wk_sync_inquiries'] = ['Inquiries', 'Inquiries from the contact form'];
$GLOBALS['TL_LANG']['MOD']['wk_sync_log'] = ['Log', 'Log of imports and exports'];
$GLOBALS['TL_LANG']['MOD']['wk_tools'] = 'W&K tools';
$GLOBALS['TL_LANG']['MOD']['wk_import'] = ['Import', 'Import seminars from a W&K file (with preview)'];
$GLOBALS['TL_LANG']['MOD']['wk_inbox'] = ['Inbox', 'Review bookings and inquiries, move them between folders and export them for W&K'];
$GLOBALS['TL_LANG']['MOD']['wk_bookings'] = ['Bookings', 'View and edit bookings'];
$GLOBALS['TL_LANG']['MOD']['wk_inquiries'] = ['Inquiries', 'Inquiries from the contact form'];
$GLOBALS['TL_LANG']['MOD']['wk_exports'] = ['Exports', 'Download the export files created for W&K'];
$GLOBALS['TL_LANG']['MOD']['wk_categories'] = ['Categories', 'Manage W&K categories and their mapping'];
$GLOBALS['TL_LANG']['MOD']['wk_config'] = ['Settings', 'Calendar, form, pages, deadline and notifications'];
$GLOBALS['TL_LANG']['MOD']['wk_log'] = ['Log', 'Log of imports and exports'];
$GLOBALS['TL_LANG']['FMD']['wk_sync'] = 'W&K sync';
$GLOBALS['TL_LANG']['FMD']['wk_sync'] = 'W&K tools';
$GLOBALS['TL_LANG']['FMD']['wk_booking_form'] = ['W&K booking form', 'Booking form for the event given in the URL.'];
$GLOBALS['TL_LANG']['FMD']['wk_booking_status'] = ['W&K booking status', 'Free places, deadline and booking link for the event given in the URL.'];
$GLOBALS['TL_LANG']['MOD']['wk_sync_bookings'] = ['Bookings', 'View and edit bookings'];
-37
View File
@@ -1,37 +0,0 @@
<?php
declare(strict_types=1);
$lang = &$GLOBALS['TL_LANG']['tl_calendar'];
$lang['wk_legend'] = 'W&K-Sync';
$lang['wkEnable'] = ['W&K-Sync aktivieren', 'Seminare aus W&K in diesen Kalender importieren und Online-Anmeldungen ermöglichen.'];
$lang['wkBookingForm'] = ['Anmeldeformular', 'Formular aus dem Formulargenerator, das für Anmeldungen verwendet wird (Feldnamen wie in tl_wk_booking; anlegen mit „wk-sync:form:create“).'];
$lang['wkBookingPage'] = ['Seite mit dem Anmeldeformular', 'Seite, auf der das Modul „W&K-Anmeldeformular“ liegt. Der Link „Jetzt anmelden“ führt dorthin.'];
$lang['wkConfirmationPage'] = ['Bestätigungsseite', 'Seite nach erfolgreicher Anmeldung, z. B. „Anmeldung eingegangen“. Kann je Veranstaltung übersteuert werden.'];
$lang['wkInquiryPage'] = ['Seite mit dem Anfrageformular', 'Seite mit dem Kontakt-/Anfrageformular; sie wird mit ?event=ID verlinkt.'];
$lang['wkDeadlineDays'] = ['Anmeldeschluss (Tage vor Beginn)', 'Bis so viele Tage vor Beginn sind Anmeldungen technisch möglich. 1 = bis einen Tag vor Beginn.'];
$lang['wkDeadlineDisplayDays'] = ['Angezeigter Anmeldeschluss (Tage vor Beginn)', 'Datum, das als „Anmeldung möglich bis“ angezeigt wird. 0 = wie der technische Anmeldeschluss.'];
$lang['wkOwnVenues'] = ['Eigene Veranstaltungsorte', 'Eine Bezeichnung pro Zeile. Veranstaltungsorte, die so beginnen (z. B. „TPI Moritzburg (barrierefrei)“), gelten als „im eigenen Haus“ – relevant für die Felder Übernachtung und Verpflegung.'];
$lang['wkAuthor'] = ['Autor importierter Veranstaltungen', 'Backend-Benutzer, der als Autor neuer Seminare eingetragen wird (leer = der importierende Benutzer).'];
$lang['wkInstructorGroup'] = ['Mitgliedergruppe Kursleitung', 'Kursleitungen aus W&K werden als Mitglieder dieser Gruppe angelegt (leer = keine Mitglieder anlegen).'];
$lang['wkSpeakerGroup'] = ['Mitgliedergruppe Referenten', 'Referenten aus W&K werden als Mitglieder dieser Gruppe angelegt (leer = keine Mitglieder anlegen).'];
$lang['wkNewsletterChannels'] = ['Newsletter-Verteiler', 'Verteiler, die bei „Newsletter abonnieren“ mit Double-Opt-in abonniert werden.'];
$lang['wkNewsletterPage'] = ['Seite zur Newsletter-Aktivierung', 'Seite mit dem Modul „Abonnieren“, die den Aktivierungslink (?token=) verarbeitet.'];
$lang['wkNewsletterText'] = ['Text der Aktivierungsmail', 'Optional. Token: ##link##, ##channels##, ##domain##. Leer = Standardtext.'];
$lang['wkNotificationDefault'] = ['Standard-Benachrichtigung', 'Wird verschickt, wenn keine der Bedingungen zutrifft und an der Veranstaltung nichts eingestellt ist.'];
$lang['wkNotificationCondition'] = ['Benachrichtigung', 'Benachrichtigung vom Typ „W&K-Anmeldung“ für diese Bedingung.'];
$lang['wkNotificationExplanation1'] = '<strong>Bedingung 1: Online-Kurse</strong> – alle Kategorien, Veranstaltungsort „Online“.';
$lang['wkNotificationExplanation2'] = '<strong>Bedingung 2: Tagesveranstaltung Schulfortbildung</strong> – Beginn = Ende, Kategorie-Art „Schule“, Nummer beginnt mit EXTP.';
$lang['wkNotificationExplanation3'] = '<strong>Bedingung 3: Tagesveranstaltung Gemeinde / Gottesdienst mit Kindern / Tagungen</strong> – Beginn = Ende, Kategorie-Art „Gemeinde“, „Gottesdienst mit Kindern“ oder „Tagungen“.';
$lang['wkNotificationExplanation4'] = '<strong>Bedingung 4: Tagesveranstaltung Elementarbereich</strong> – Beginn = Ende, Kategorie-Art „Elementarbereich“.';
$lang['wkNotificationExplanation5'] = '<strong>Bedingung 5: Mehrtägige Schulfortbildung</strong> – Beginn ≠ Ende, Kategorie-Art „Schule“, Nummer beginnt mit EXTP.';
$lang['wkNotificationExplanation6'] = '<strong>Bedingung 6: Mehrtägige Fortbildung Elementarbereich</strong> – Beginn ≠ Ende, Kategorie-Art „Elementarbereich“.';
$lang['wkNotificationExplanation7'] = '<strong>Bedingung 7: Mehrtägige Veranstaltung Gemeinde / Gottesdienst mit Kindern / Tagungen</strong> – Beginn ≠ Ende.';
$lang['wkNotificationExplanation8'] = '<strong>Bedingung 8: Tagesveranstaltung Evangelische Schulen</strong> – Beginn = Ende, Kategorie-Art „Evangelische Schulen“.';
$lang['wkNotificationExplanation9'] = '<strong>Bedingung 9: Mehrtägige Veranstaltung Evangelische Schulen</strong> – Beginn ≠ Ende, Kategorie-Art „Evangelische Schulen“.';
$lang['wkNotificationExplanation10'] = '<strong>Bedingung 10: Tagesveranstaltung Mentoring</strong> – Beginn = Ende, Kategorie-Art „Mentoring“.';
$lang['wkNotificationExplanation11'] = '<strong>Bedingung 11: Mehrtägige Veranstaltung Mentoring</strong> – Beginn ≠ Ende, Kategorie-Art „Mentoring“.';
$lang['wkNotificationExplanation12'] = '<strong>Bedingung 12: Mehrtägige Ausbildung</strong> – Beginn ≠ Ende, Kategorie-Art „Ausbildung“.';
+49
View File
@@ -0,0 +1,49 @@
<?php
declare(strict_types=1);
$lang = &$GLOBALS['TL_LANG']['tl_wk_config'];
$lang['title_legend'] = 'Kalender';
$lang['page_legend'] = 'Formular und Seiten';
$lang['booking_legend'] = 'Anmeldung';
$lang['import_legend'] = 'Import';
$lang['newsletter_legend'] = 'Newsletter';
$lang['notification_legend'] = 'Benachrichtigungen';
$lang['title'] = ['Bezeichnung', 'Interner Name dieser Einstellungen, z. B. „Programm“.'];
$lang['calendar'] = ['Kalender', 'Kalender, in den die W&K-Seminare importiert werden und dessen Veranstaltungen online buchbar sind. Je Kalender ein Eintrag.'];
$lang['bookingForm'] = ['Anmeldeformular', 'Formular aus dem Formulargenerator, das für Anmeldungen verwendet wird (Feldnamen wie in tl_wk_booking; anlegen mit „wk-sync:form:create“).'];
$lang['bookingPage'] = ['Seite mit dem Anmeldeformular', 'Seite, auf der das Modul „W&K-Anmeldeformular“ liegt. Der Link „Jetzt anmelden“ führt dorthin.'];
$lang['confirmationPage'] = ['Bestätigungsseite', 'Seite nach erfolgreicher Anmeldung, z. B. „Anmeldung eingegangen“. Kann je Veranstaltung übersteuert werden.'];
$lang['inquiryPage'] = ['Seite mit dem Anfrageformular', 'Seite mit dem Kontakt-/Anfrageformular; sie wird mit ?event=ID verlinkt.'];
$lang['deadlineDays'] = ['Anmeldeschluss (Tage vor Beginn)', 'Bis so viele Tage vor Beginn sind Anmeldungen technisch möglich. 1 = bis einen Tag vor Beginn.'];
$lang['deadlineDisplayDays'] = ['Angezeigter Anmeldeschluss (Tage vor Beginn)', 'Datum, das als „Anmeldung möglich bis“ angezeigt wird. 0 = wie der technische Anmeldeschluss.'];
$lang['ownVenues'] = ['Eigene Veranstaltungsorte', 'Eine Bezeichnung pro Zeile. Veranstaltungsorte, die so beginnen (z. B. „TPI Moritzburg (barrierefrei)“), gelten als „im eigenen Haus“ – relevant für die Felder Übernachtung und Verpflegung.'];
$lang['author'] = ['Autor importierter Veranstaltungen', 'Backend-Benutzer, der als Autor neuer Seminare eingetragen wird (leer = der importierende Benutzer).'];
$lang['instructorGroup'] = ['Mitgliedergruppe Kursleitung', 'Kursleitungen aus W&K werden als Mitglieder dieser Gruppe angelegt (leer = keine Mitglieder anlegen).'];
$lang['speakerGroup'] = ['Mitgliedergruppe Referenten', 'Referenten aus W&K werden als Mitglieder dieser Gruppe angelegt (leer = keine Mitglieder anlegen).'];
$lang['newsletterChannels'] = ['Newsletter-Verteiler', 'Verteiler, die bei „Newsletter abonnieren“ mit Double-Opt-in abonniert werden.'];
$lang['newsletterPage'] = ['Seite zur Newsletter-Aktivierung', 'Seite mit dem Modul „Abonnieren“, die den Aktivierungslink (?token=) verarbeitet.'];
$lang['newsletterText'] = ['Text der Aktivierungsmail', 'Optional. Token: ##link##, ##channels##, ##domain##. Leer = Standardtext.'];
$lang['notificationDefault'] = ['Standard-Benachrichtigung', 'Wird verschickt, wenn keine der Bedingungen zutrifft und an der Veranstaltung nichts eingestellt ist. Nur Benachrichtigungen vom Typ „W&K-Anmeldung“.'];
$lang['notificationCondition'] = ['Benachrichtigung für diese Bedingung', 'Leer = Bedingung wird übersprungen, die nächste zutreffende Bedingung zählt.'];
$lang['notificationExplanation1'] = '<strong>Bedingung 1: Online-Kurse</strong> – alle Kategorien, Veranstaltungsort „Online“.';
$lang['notificationExplanation2'] = '<strong>Bedingung 2: Tagesveranstaltung Schulfortbildung</strong> – Beginn = Ende, Kategorie-Art „Schule“, Nummer beginnt mit EXTP.';
$lang['notificationExplanation3'] = '<strong>Bedingung 3: Tagesveranstaltung Gemeinde / Gottesdienst mit Kindern / Tagungen</strong> – Beginn = Ende, Kategorie-Art „Gemeinde“, „Gottesdienst mit Kindern“ oder „Tagungen“.';
$lang['notificationExplanation4'] = '<strong>Bedingung 4: Tagesveranstaltung Elementarbereich</strong> – Beginn = Ende, Kategorie-Art „Elementarbereich“.';
$lang['notificationExplanation5'] = '<strong>Bedingung 5: Mehrtägige Schulfortbildung</strong> – Beginn ≠ Ende, Kategorie-Art „Schule“, Nummer beginnt mit EXTP.';
$lang['notificationExplanation6'] = '<strong>Bedingung 6: Mehrtägige Fortbildung Elementarbereich</strong> – Beginn ≠ Ende, Kategorie-Art „Elementarbereich“.';
$lang['notificationExplanation7'] = '<strong>Bedingung 7: Mehrtägige Veranstaltung Gemeinde / Gottesdienst mit Kindern / Tagungen</strong> – Beginn ≠ Ende.';
$lang['notificationExplanation8'] = '<strong>Bedingung 8: Tagesveranstaltung Evangelische Schulen</strong> – Beginn = Ende, Kategorie-Art „Evangelische Schulen“.';
$lang['notificationExplanation9'] = '<strong>Bedingung 9: Mehrtägige Veranstaltung Evangelische Schulen</strong> – Beginn ≠ Ende, Kategorie-Art „Evangelische Schulen“.';
$lang['notificationExplanation10'] = '<strong>Bedingung 10: Tagesveranstaltung Mentoring</strong> – Beginn = Ende, Kategorie-Art „Mentoring“.';
$lang['notificationExplanation11'] = '<strong>Bedingung 11: Mehrtägige Veranstaltung Mentoring</strong> – Beginn ≠ Ende, Kategorie-Art „Mentoring“.';
$lang['notificationExplanation12'] = '<strong>Bedingung 12: Mehrtägige Ausbildung</strong> – Beginn ≠ Ende, Kategorie-Art „Ausbildung“.';
$lang['new'] = ['Neue Einstellungen', 'Einstellungen für einen Kalender anlegen'];
$lang['edit'] = ['Einstellungen bearbeiten', 'Einstellungen ID %s bearbeiten'];
$lang['copy'] = ['Einstellungen duplizieren', 'Einstellungen ID %s duplizieren'];
$lang['delete'] = ['Einstellungen löschen', 'Einstellungen ID %s löschen'];
$lang['show'] = ['Details', 'Details der Einstellungen ID %s anzeigen'];
@@ -1,7 +0,0 @@
{% trans_default_domain 'contao_default' %}
{{ messages|raw }}
<nav class="wk-sync-nav">
<a href="{{ urls.dashboard }}" class="tl_submit{{ current_key in ['', 'preview'] ? ' active' }}">{{ 'MSC.wk_sync.nav.import'|trans }}</a>
<a href="{{ urls.inbox }}" class="tl_submit{{ current_key == 'inbox' ? ' active' }}">{{ 'MSC.wk_sync.nav.inbox'|trans }}{% if counts is defined and counts[''] is defined and counts[''] > 0 %} <span class="wk-sync-badge">{{ counts[''] }}</span>{% endif %}</a>
<a href="{{ urls.exports }}" class="tl_submit{{ current_key == 'exports' ? ' active' }}">{{ 'MSC.wk_sync.nav.exports'|trans }}</a>
</nav>
@@ -1,6 +1,6 @@
{% trans_default_domain 'contao_default' %}
<div class="wk-sync">
{{ include('@Contao/backend/wk_sync/_nav.html.twig') }}
{{ messages|raw }}
<h2>{{ 'MSC.wk_sync.exports.headline'|trans }}</h2>
<p class="tl_help">{{ 'MSC.wk_sync.exports.help'|trans }}</p>
@@ -1,6 +1,6 @@
{% trans_default_domain 'contao_default' %}
<div class="wk-sync">
{{ include('@Contao/backend/wk_sync/_nav.html.twig') }}
{{ messages|raw }}
<h2>{{ 'MSC.wk_sync.import.headline'|trans }}</h2>
@@ -9,7 +9,7 @@
{% endif %}
{% if calendars is empty %}
<p class="tl_error">{{ 'MSC.wk_sync.import.no_calendar'|trans }}</p>
<p class="tl_error">{{ 'MSC.wk_sync.import.no_calendar'|trans }} <a href="{{ path('contao_backend', {do: 'wk_config'}) }}">{{ 'MSC.wk_sync.import.open_config'|trans }}</a></p>
{% else %}
<form method="post" action="{{ urls.upload }}" enctype="multipart/form-data" class="tl_form wk-sync-upload">
<input type="hidden" name="REQUEST_TOKEN" value="{{ request_token }}">
@@ -19,7 +19,7 @@
<label for="wk_calendar"><strong>{{ 'MSC.wk_sync.import.calendar'|trans }}</strong></label>
<select name="calendar" id="wk_calendar" class="tl_select">
{% for calendar in calendars %}
<option value="{{ calendar.id }}">{{ calendar.title }}</option>
<option value="{{ calendar.id }}">{{ calendar.configTitle }} → {{ calendar.title }}</option>
{% endfor %}
</select>
</div>
@@ -1,6 +1,6 @@
{% trans_default_domain 'contao_default' %}
<div class="wk-sync">
{{ include('@Contao/backend/wk_sync/_nav.html.twig') }}
{{ messages|raw }}
<h2>{{ 'MSC.wk_sync.inbox.headline'|trans }}</h2>
@@ -1,6 +1,6 @@
{% trans_default_domain 'contao_default' %}
<div class="wk-sync">
{{ include('@Contao/backend/wk_sync/_nav.html.twig') }}
{{ messages|raw }}
<h2>{{ 'MSC.wk_sync.preview.headline'|trans([log.filename, calendar.title]) }}</h2>
+55 -39
View File
@@ -44,7 +44,11 @@ use Twig\Environment;
*/
class WkSyncBackend
{
public const string MODULE = 'wk_sync_tools';
public const string MODULE_IMPORT = 'wk_import';
public const string MODULE_INBOX = 'wk_inbox';
public const string MODULE_EXPORTS = 'wk_exports';
public const string MODULE_BOOKINGS = 'wk_bookings';
public const string MODULE_INQUIRIES = 'wk_inquiries';
private const int MAX_UPLOAD_BYTES = 20 * 1024 * 1024;
@@ -75,32 +79,42 @@ class WkSyncBackend
}
$this->framework->initialize();
$module = (string) $request->query->get('do', '');
$key = (string) $request->query->get('key', '');
try {
return match ($key) {
'' => $this->dashboard($request),
'upload' => $this->upload($request),
'preview' => $this->preview($request),
'apply' => $this->apply($request),
'discard' => $this->discard($request),
'inbox' => $this->inboxView($request),
'move' => $this->move($request),
'export' => $this->export($request),
'exports' => $this->exports($request),
'download' => $this->download($request),
default => throw new PageNotFoundException('Unknown action "'.$key.'"'),
return match ($module) {
self::MODULE_IMPORT => match ($key) {
'' => $this->importView($request),
'upload' => $this->upload($request),
'preview' => $this->preview($request),
'apply' => $this->apply($request),
'discard' => $this->discard($request),
default => throw new PageNotFoundException('Unknown action "'.$key.'"'),
},
self::MODULE_INBOX => match ($key) {
'' => $this->inboxView($request),
'move' => $this->move($request),
'export' => $this->export($request),
default => throw new PageNotFoundException('Unknown action "'.$key.'"'),
},
self::MODULE_EXPORTS => match ($key) {
'' => $this->exportsView($request),
'download' => $this->download($request),
default => throw new PageNotFoundException('Unknown action "'.$key.'"'),
},
default => throw new PageNotFoundException('Unknown module "'.$module.'"'),
};
} catch (ImportException $e) {
$this->message()->addError($e->getMessage());
throw new RedirectResponseException($this->url([]));
throw new RedirectResponseException($this->url('' !== $module ? $module : self::MODULE_IMPORT));
}
}
private function dashboard(Request $request): string
private function importView(Request $request): string
{
return $this->render('dashboard', [
return $this->render('import', [
'calendars' => $this->calendars->findEnabled(),
'imports' => array_map($this->decorateLog(...), $this->log->findLatest(SyncLog::TYPE_IMPORT, 10)),
'counts' => $this->inbox->countByState(),
@@ -133,7 +147,7 @@ class WkSyncBackend
$prepared = $this->importService->prepare($path, $filename, $calendarId, $this->userId());
throw new RedirectResponseException($this->url(['key' => 'preview', 'id' => $prepared['logId']]));
throw new RedirectResponseException($this->url(self::MODULE_IMPORT, ['key' => 'preview', 'id' => $prepared['logId']]));
}
private function preview(Request $request): string
@@ -186,7 +200,7 @@ class WkSyncBackend
$this->message()->addConfirmation($this->trans('import.applied', [$result['created'], $result['updated'], $result['cancelled'], $result['unchanged']]));
throw new RedirectResponseException($this->url(['key' => 'preview', 'id' => $id]));
throw new RedirectResponseException($this->url(self::MODULE_IMPORT, ['key' => 'preview', 'id' => $id]));
}
private function discard(Request $request): string
@@ -200,7 +214,7 @@ class WkSyncBackend
$this->message()->addInfo($this->trans('import.discarded'));
}
throw new RedirectResponseException($this->url([]));
throw new RedirectResponseException($this->url(self::MODULE_IMPORT));
}
private function inboxView(Request $request): string
@@ -257,7 +271,7 @@ class WkSyncBackend
$this->message()->addConfirmation($this->trans('inbox.moved', [$moved, $this->trans('state.'.$target->translationKey())]));
throw new RedirectResponseException($this->url(['key' => 'inbox', 'list' => $list]));
throw new RedirectResponseException($this->url(self::MODULE_INBOX, ['list' => $list]));
}
private function export(Request $request): string
@@ -269,15 +283,15 @@ class WkSyncBackend
if (null === $result) {
$this->message()->addInfo($this->trans('export.empty'));
throw new RedirectResponseException($this->url(['key' => 'inbox', 'list' => InboxState::DownloadList->value]));
throw new RedirectResponseException($this->url(self::MODULE_INBOX, ['list' => InboxState::DownloadList->value]));
}
$this->message()->addConfirmation($this->trans('export.done', [$result['bookings'], $result['inquiries']]));
throw new RedirectResponseException($this->url(['key' => 'exports', 'highlight' => $result['logId']]));
throw new RedirectResponseException($this->url(self::MODULE_EXPORTS, ['highlight' => $result['logId']]));
}
private function exports(Request $request): string
private function exportsView(Request $request): string
{
return $this->render('exports', [
'exports' => array_map($this->decorateLog(...), $this->log->findLatest(SyncLog::TYPE_EXPORT, 50)),
@@ -400,8 +414,8 @@ class WkSyncBackend
{
$log['date'] = $this->formatDate((int) $log['tstamp'], 'datimFormat');
$log['calendarTitle'] = $this->calendars->find((int) $log['calendar'])?->title ?? '';
$log['preview_url'] = $this->url(['key' => 'preview', 'id' => $log['id']]);
$log['download_url'] = $this->url(['key' => 'download', 'id' => $log['id']]);
$log['preview_url'] = $this->url(self::MODULE_IMPORT, ['key' => 'preview', 'id' => $log['id']]);
$log['download_url'] = $this->url(self::MODULE_EXPORTS, ['key' => 'download', 'id' => $log['id']]);
$log['file_exists'] = $this->storage->exists((string) ($log['path'] ?? ''));
return $log;
@@ -410,8 +424,8 @@ class WkSyncBackend
private function editUrl(InboxItem $item, string $referer): string
{
$params = $item->isBooking()
? ['do' => 'wk_sync_bookings', 'table' => 'tl_wk_booking', 'act' => 'edit', 'id' => $item->id]
: ['do' => 'wk_sync_inquiries', 'table' => 'tl_wk_inquiry', 'act' => 'edit', 'id' => $item->id];
? ['do' => self::MODULE_BOOKINGS, 'table' => 'tl_wk_booking', 'act' => 'edit', 'id' => $item->id]
: ['do' => self::MODULE_INQUIRIES, 'table' => 'tl_wk_inquiry', 'act' => 'edit', 'id' => $item->id];
if ('' !== $referer) {
$params['ref'] = $referer;
@@ -432,29 +446,31 @@ class WkSyncBackend
'messages' => $this->message()->generate(),
'request_token' => $this->csrfTokenManager->getDefaultTokenValue(),
'urls' => [
'dashboard' => $this->url([]),
'upload' => $this->url(['key' => 'upload']),
'apply' => $this->url(['key' => 'apply']),
'discard' => $this->url(['key' => 'discard']),
'inbox' => $this->url(['key' => 'inbox']),
'move' => $this->url(['key' => 'move']),
'export' => $this->url(['key' => 'export']),
'exports' => $this->url(['key' => 'exports']),
'import' => $this->url(self::MODULE_IMPORT),
'upload' => $this->url(self::MODULE_IMPORT, ['key' => 'upload']),
'apply' => $this->url(self::MODULE_IMPORT, ['key' => 'apply']),
'discard' => $this->url(self::MODULE_IMPORT, ['key' => 'discard']),
'inbox' => $this->url(self::MODULE_INBOX),
'move' => $this->url(self::MODULE_INBOX, ['key' => 'move']),
'export' => $this->url(self::MODULE_INBOX, ['key' => 'export']),
'exports' => $this->url(self::MODULE_EXPORTS),
'bookings' => $this->url(self::MODULE_BOOKINGS),
'inquiries' => $this->url(self::MODULE_INQUIRIES),
],
'inbox_urls' => array_combine(
array_map(static fn (InboxState $s): string => $s->translationKey(), InboxState::cases()),
array_map(fn (InboxState $s): string => $this->url(['key' => 'inbox', 'list' => $s->value]), InboxState::cases()),
array_map(fn (InboxState $s): string => $this->url(self::MODULE_INBOX, ['list' => $s->value]), InboxState::cases()),
),
'current_key' => (string) $request?->query->get('key', ''),
'current_module' => (string) $request?->query->get('do', ''),
]);
}
/**
* @param array<string, mixed> $params
*/
private function url(array $params): string
private function url(string $module, array $params = []): string
{
return $this->router->generate('contao_backend', ['do' => self::MODULE, ...$params]);
return $this->router->generate('contao_backend', ['do' => $module, ...$params]);
}
private function requirePost(Request $request): void
+7 -1
View File
@@ -41,6 +41,12 @@ class NotificationCenterNotifier implements BookingNotifierInterface
public function availableNotifications(): array
{
return $this->notificationCenter->getNotificationsForNotificationType(BookingNotificationType::NAME);
$options = [];
foreach ($this->notificationCenter->getNotificationsForNotificationType(BookingNotificationType::NAME) as $id => $title) {
$options[(int) $id] = \sprintf('%s (ID %d)', $title, $id);
}
return $options;
}
}
+27 -4
View File
@@ -29,7 +29,7 @@ class CreateBookingFormCommand extends Command
{
$this
->addOption('title', null, InputOption::VALUE_REQUIRED, 'Titel des Formulars', 'W&K-Anmeldung')
->addOption('calendar', null, InputOption::VALUE_REQUIRED, 'Formular diesem Kalender (ID) als Anmeldeformular zuweisen')
->addOption('calendar', null, InputOption::VALUE_REQUIRED, 'Formular in den W&K-Einstellungen dieses Kalenders (ID) eintragen; die Einstellungen werden bei Bedarf angelegt')
->addOption('privacy-url', null, InputOption::VALUE_REQUIRED, 'Link zur Datenschutzseite', '/datenschutz')
->addOption('terms-url', null, InputOption::VALUE_REQUIRED, 'Link zu den AGB', '/agb')
;
@@ -69,15 +69,38 @@ class CreateBookingFormCommand extends Command
$calendarId = (int) $input->getOption('calendar');
if ($calendarId > 0) {
$this->connection->update('tl_calendar', ['wkBookingForm' => $formId, 'tstamp' => $now], ['id' => $calendarId]);
$io->writeln(\sprintf('Kalender %d verwendet das Formular jetzt als Anmeldeformular.', $calendarId));
$this->assignToConfig($calendarId, $formId, $now);
$io->writeln(\sprintf('Die W&K-Einstellungen für Kalender %d verwenden das Formular jetzt als Anmeldeformular.', $calendarId));
} else {
$io->note('Das Formular jetzt im Kalender unter „W&K-Sync → Anmeldeformular“ auswählen.');
$io->note('Das Formular jetzt unter „W&K-Tools → Einstellungen“ als Anmeldeformular auswählen.');
}
return Command::SUCCESS;
}
private function assignToConfig(int $calendarId, int $formId, int $now): void
{
$configId = $this->connection->fetchOne('SELECT id FROM tl_wk_config WHERE calendar = ?', [$calendarId]);
if (false !== $configId) {
$this->connection->update('tl_wk_config', ['bookingForm' => $formId, 'tstamp' => $now], ['id' => (int) $configId]);
return;
}
$title = (string) $this->connection->fetchOne('SELECT title FROM tl_calendar WHERE id = ?', [$calendarId]);
$this->insert('tl_wk_config', [
'tstamp' => $now,
'title' => '' !== $title ? $title : 'Kalender '.$calendarId,
'calendar' => $calendarId,
'bookingForm' => $formId,
'deadlineDays' => 1,
'deadlineDisplayDays' => 31,
'ownVenues' => 'TPI Moritzburg',
]);
}
/**
* @return list<array<string, mixed>>
*/
@@ -10,7 +10,7 @@ use Doctrine\DBAL\Connection;
/**
* Newsletter-Verteiler (leer, wenn das Newsletter-Bundle fehlt).
*/
#[AsCallback(table: 'tl_calendar', target: 'fields.wkNewsletterChannels.options')]
#[AsCallback(table: 'tl_wk_config', target: 'fields.newsletterChannels.options')]
class NewsletterChannelOptionsListener
{
public function __construct(private readonly Connection $connection)
@@ -10,19 +10,19 @@ use Mummert\ContaoWkSyncBundle\Booking\BookingNotifierInterface;
/**
* Auswahl der Benachrichtigungen vom Typ „W&K-Anmeldung“ (leer ohne Notification Center).
*/
#[AsCallback(table: 'tl_calendar', target: 'fields.wkNotificationDefault.options')]
#[AsCallback(table: 'tl_calendar', target: 'fields.wkNotification1.options')]
#[AsCallback(table: 'tl_calendar', target: 'fields.wkNotification2.options')]
#[AsCallback(table: 'tl_calendar', target: 'fields.wkNotification3.options')]
#[AsCallback(table: 'tl_calendar', target: 'fields.wkNotification4.options')]
#[AsCallback(table: 'tl_calendar', target: 'fields.wkNotification5.options')]
#[AsCallback(table: 'tl_calendar', target: 'fields.wkNotification6.options')]
#[AsCallback(table: 'tl_calendar', target: 'fields.wkNotification7.options')]
#[AsCallback(table: 'tl_calendar', target: 'fields.wkNotification8.options')]
#[AsCallback(table: 'tl_calendar', target: 'fields.wkNotification9.options')]
#[AsCallback(table: 'tl_calendar', target: 'fields.wkNotification10.options')]
#[AsCallback(table: 'tl_calendar', target: 'fields.wkNotification11.options')]
#[AsCallback(table: 'tl_calendar', target: 'fields.wkNotification12.options')]
#[AsCallback(table: 'tl_wk_config', target: 'fields.notificationDefault.options')]
#[AsCallback(table: 'tl_wk_config', target: 'fields.notification1.options')]
#[AsCallback(table: 'tl_wk_config', target: 'fields.notification2.options')]
#[AsCallback(table: 'tl_wk_config', target: 'fields.notification3.options')]
#[AsCallback(table: 'tl_wk_config', target: 'fields.notification4.options')]
#[AsCallback(table: 'tl_wk_config', target: 'fields.notification5.options')]
#[AsCallback(table: 'tl_wk_config', target: 'fields.notification6.options')]
#[AsCallback(table: 'tl_wk_config', target: 'fields.notification7.options')]
#[AsCallback(table: 'tl_wk_config', target: 'fields.notification8.options')]
#[AsCallback(table: 'tl_wk_config', target: 'fields.notification9.options')]
#[AsCallback(table: 'tl_wk_config', target: 'fields.notification10.options')]
#[AsCallback(table: 'tl_wk_config', target: 'fields.notification11.options')]
#[AsCallback(table: 'tl_wk_config', target: 'fields.notification12.options')]
#[AsCallback(table: 'tl_calendar_events', target: 'fields.wkNotification.options')]
class NotificationOptionsListener
{
+26 -20
View File
@@ -7,7 +7,9 @@ namespace Mummert\ContaoWkSyncBundle\Sync;
use Contao\StringUtil;
/**
* W&K-Einstellungen eines Kalenders (tl_calendar).
* W&K-Einstellungen für einen Kalender (tl_wk_config, je Kalender ein Eintrag).
*
* `id` ist die ID des Kalenders (tl_calendar), `configId` die des Einstellungs-Datensatzes.
*/
final readonly class CalendarConfig
{
@@ -18,7 +20,9 @@ final readonly class CalendarConfig
*/
public function __construct(
public int $id,
public int $configId,
public string $title,
public string $configTitle,
public int $jumpTo,
public bool $enabled,
public int $bookingForm,
@@ -40,40 +44,42 @@ final readonly class CalendarConfig
}
/**
* @param array<string, mixed> $row
* @param array<string, mixed> $row Zeile aus tl_wk_config, optional mit calendarTitle und calendarJumpTo
*/
public static function fromRow(array $row): self
{
$venues = preg_split('/\r?\n/', (string) ($row['wkOwnVenues'] ?? '')) ?: [];
$venues = preg_split('/\r?\n/', (string) ($row['ownVenues'] ?? '')) ?: [];
$venues = array_values(array_filter(array_map(trim(...), $venues), static fn (string $v): bool => '' !== $v));
$conditions = [];
foreach ($row as $key => $value) {
if (preg_match('/^wkNotification(\d+)$/', (string) $key, $m)) {
if (preg_match('/^notification(\d+)$/', (string) $key, $m)) {
$conditions[(int) $m[1]] = (int) $value;
}
}
return new self(
(int) $row['id'],
(int) ($row['calendar'] ?? 0),
(int) ($row['id'] ?? 0),
(string) ($row['calendarTitle'] ?? ''),
(string) ($row['title'] ?? ''),
(int) ($row['jumpTo'] ?? 0),
(bool) ($row['wkEnable'] ?? false),
(int) ($row['wkBookingForm'] ?? 0),
(int) ($row['wkBookingPage'] ?? 0),
(int) ($row['wkConfirmationPage'] ?? 0),
(int) ($row['wkInquiryPage'] ?? 0),
(int) ($row['wkDeadlineDays'] ?? 1),
(int) ($row['wkDeadlineDisplayDays'] ?? 0),
(int) ($row['calendarJumpTo'] ?? 0),
(int) ($row['calendar'] ?? 0) > 0,
(int) ($row['bookingForm'] ?? 0),
(int) ($row['bookingPage'] ?? 0),
(int) ($row['confirmationPage'] ?? 0),
(int) ($row['inquiryPage'] ?? 0),
(int) ($row['deadlineDays'] ?? 1),
(int) ($row['deadlineDisplayDays'] ?? 0),
$venues,
(int) ($row['wkAuthor'] ?? 0),
(int) ($row['wkInstructorGroup'] ?? 0),
(int) ($row['wkSpeakerGroup'] ?? 0),
array_map(intval(...), StringUtil::deserialize($row['wkNewsletterChannels'] ?? null, true)),
(int) ($row['wkNewsletterPage'] ?? 0),
(string) ($row['wkNewsletterText'] ?? ''),
(int) ($row['wkNotificationDefault'] ?? 0),
(int) ($row['author'] ?? 0),
(int) ($row['instructorGroup'] ?? 0),
(int) ($row['speakerGroup'] ?? 0),
array_map(intval(...), StringUtil::deserialize($row['newsletterChannels'] ?? null, true)),
(int) ($row['newsletterPage'] ?? 0),
(string) ($row['newsletterText'] ?? ''),
(int) ($row['notificationDefault'] ?? 0),
$conditions,
);
}
+15 -7
View File
@@ -6,8 +6,13 @@ namespace Mummert\ContaoWkSyncBundle\Sync;
use Doctrine\DBAL\Connection;
/**
* Einstellungen je Kalender (tl_wk_config).
*/
class CalendarConfigRepository
{
private const string SELECT = 'SELECT c.*, k.title AS calendarTitle, k.jumpTo AS calendarJumpTo FROM tl_wk_config c INNER JOIN tl_calendar k ON k.id = c.calendar';
/** @var array<int, CalendarConfig|null> */
private array $cache = [];
@@ -15,14 +20,17 @@ class CalendarConfigRepository
{
}
public function find(int $id): CalendarConfig|null
/**
* Einstellungen zum Kalender mit dieser ID.
*/
public function find(int $calendarId): CalendarConfig|null
{
if (!\array_key_exists($id, $this->cache)) {
$row = $this->connection->fetchAssociative('SELECT * FROM tl_calendar WHERE id = ?', [$id]);
$this->cache[$id] = false === $row ? null : CalendarConfig::fromRow($row);
if (!\array_key_exists($calendarId, $this->cache)) {
$row = $this->connection->fetchAssociative(self::SELECT.' WHERE c.calendar = ?', [$calendarId]);
$this->cache[$calendarId] = false === $row ? null : CalendarConfig::fromRow($row);
}
return $this->cache[$id];
return $this->cache[$calendarId];
}
/**
@@ -30,7 +38,7 @@ class CalendarConfigRepository
*/
public function findEnabled(): array
{
$rows = $this->connection->fetchAllAssociative('SELECT * FROM tl_calendar WHERE wkEnable = 1 ORDER BY title');
$rows = $this->connection->fetchAllAssociative(self::SELECT.' ORDER BY c.title');
return array_map(CalendarConfig::fromRow(...), $rows);
}
@@ -51,7 +59,7 @@ class CalendarConfigRepository
return [];
}
$rows = $this->connection->fetchAllAssociative('SELECT * FROM tl_calendar WHERE wkEnable = 1 AND wkBookingForm = ?', [$formId]);
$rows = $this->connection->fetchAllAssociative(self::SELECT.' WHERE c.bookingForm = ?', [$formId]);
return array_map(CalendarConfig::fromRow(...), $rows);
}
+1 -1
View File
@@ -15,7 +15,7 @@ class BookingRulesTest extends TestCase
{
private function calendar(): CalendarConfig
{
return CalendarConfig::fromRow(['id' => 1, 'wkOwnVenues' => "TPI Moritzburg\nMoritzburg"]);
return CalendarConfig::fromRow(['id' => 1, 'calendar' => 1, 'ownVenues' => "TPI Moritzburg\nMoritzburg"]);
}
/**
+3 -3
View File
@@ -14,10 +14,10 @@ class NotificationRouterTest extends TestCase
{
private function calendar(): CalendarConfig
{
$row = ['id' => 1, 'wkNotificationDefault' => 99];
$row = ['id' => 1, 'calendar' => 1, 'notificationDefault' => 99];
foreach (NotificationRouter::CONDITIONS as $c) {
$row['wkNotification'.$c] = 100 + $c;
$row['notification'.$c] = 100 + $c;
}
return CalendarConfig::fromRow($row);
@@ -83,7 +83,7 @@ class NotificationRouterTest extends TestCase
public function testFallsThroughToNextConditionWhenNotificationMissing(): void
{
$calendar = CalendarConfig::fromRow(['id' => 1, 'wkNotification1' => 0, 'wkNotification4' => 104]);
$calendar = CalendarConfig::fromRow(['id' => 1, 'calendar' => 1, 'notification1' => 0, 'notification4' => 104]);
$result = (new NotificationRouter())->resolve($this->event(CategoryKind::Elementary, true, ['location' => 'Online']), $calendar);
self::assertSame([1, 4], $result['conditions']);