276 lines
13 KiB
Markdown
276 lines
13 KiB
Markdown
# Perspektivwerk Terminbuchung – lokales Entwicklungssystem
|
||
|
||
Dieser Ordner enthält eine lokale Contao-4.13.58-Entwicklungsumgebung mit Apache, MariaDB und Bootstrap 5. Die Buchungskomponente verwendet browsernative DOM-APIs und keine jQuery-Abhängigkeit.
|
||
|
||
## Contao-Version und Sicherheitsstatus
|
||
|
||
Die Abhängigkeiten sind auf `contao/manager-bundle` und `contao/core-bundle` in Version `4.13.58` festgelegt. `composer.lock` ist Teil des Repositorys und muss bei Änderungen mit eingecheckt werden.
|
||
|
||
Contao 4.13.58 ist ausschließlich als lokale Kompatibilitätsumgebung vorgesehen. In `composer.json` ist die Advisory `PKSA-f8tt-pn3h-s2tw` gezielt dokumentiert und für Composer freigegeben, weil keine neuere 4.13-Version verfügbar ist. Ein `composer audit` meldet darüber hinaus historische Abhängigkeitswarnungen; diese Umgebung darf deshalb nicht unverändert produktiv eingesetzt werden.
|
||
|
||
## Start
|
||
|
||
Voraussetzungen: Docker Desktop (Compose v2) und optional Node.js 20+ zum Ausführen der JavaScript-Regressionstests.
|
||
|
||
```powershell
|
||
docker compose up --build
|
||
```
|
||
|
||
Beim ersten Start eines frischen Clones erzeugt Contao die benötigten Laufzeitverzeichnisse und Asset-Symlinks. Danach einmalig ausführen:
|
||
|
||
```powershell
|
||
docker compose exec app vendor/bin/contao-setup --no-interaction --env=dev
|
||
docker compose exec app chown -R www-data:www-data var public system files
|
||
docker compose exec app vendor/bin/contao-console contao:migrate --no-interaction
|
||
|
||
Neuen Admin-Account anlegen:
|
||
|
||
```powershell
|
||
docker compose exec app vendor/bin/contao-console contao:user:create --admin
|
||
```
|
||
|
||
Danach den versionierten Seitenzustand importieren:
|
||
|
||
```powershell
|
||
.\scripts\Import-ContaoBaseline.ps1
|
||
```
|
||
|
||
- Website/CMS: <http://localhost:8080>
|
||
- Contao-Login: <http://localhost:8080/contao/login>
|
||
- Datenbankinspektor: <http://localhost:8081> (Server `db`, Benutzer `perspektivwerk`, Passwort `perspektivwerk`, Datenbank `perspektivwerk`)
|
||
|
||
Für die Frontend-Regressionstests:
|
||
|
||
```powershell
|
||
npm install
|
||
npm test
|
||
```
|
||
|
||
**Seite öffnen:**
|
||
|
||
Frontend: http://localhost:8080/default
|
||
Backend: http://localhost:8080/contao
|
||
|
||
|
||
## HTML Nachbau der Perpektivwerkseite
|
||
|
||
http://localhost:8080/perspektivwerk/
|
||
|
||
Die genaue Contao-, PHP- und Template-Version der öffentlichen Seite ist nicht verlässlich aus dem ausgelieferten HTML ableitbar. Das Setup bildet daher die wahrscheinlichste Komponentenfamilie nach; es ist kein Dump der Produktivinstallation. IONOS/Hosting, Matomo-Konfiguration, Inhalte, Templates und Erweiterungen müssen bei Bedarf separat ergänzt werden.
|
||
|
||
Der vollständige, statisch auslieferbare Perspektivwerk-DocumentRoot liegt in
|
||
`public/perspektivwerk/`. Er enthält die Buchungs-API unter `booking/` und die
|
||
Verwaltung unter `booking-admin/`. Die Vorschau verwendet relative API-URLs und
|
||
funktioniert dadurch lokal unter `/perspektivwerk/` sowie auf dem Server direkt
|
||
im DocumentRoot.
|
||
|
||
Für einen einfachen Upload der vollständigen statischen Mockup-Seite auf den
|
||
Testserver steht unter Windows dieses Skript bereit:
|
||
|
||
```bat
|
||
scripts\Deploy-Perspektivwerk.bat
|
||
```
|
||
|
||
Es überträgt den vollständigen Ordner `public\perspektivwerk` und dessen
|
||
benachbarte Konfigurationsdatei `public\booking-config.php` nach
|
||
`boogie:html/perspektivwerk/`. Damit entsteht der Server-DocumentRoot unter
|
||
`html/perspektivwerk/perspektivwerk/`; die Konfigurationsdatei liegt eine Ebene
|
||
darüber und damit außerhalb des DocumentRoots. Anschließend setzt es per SSH
|
||
lesbare Dateien und durchsuchbare Verzeichnisse.
|
||
|
||
Vor dem ersten Upload `public\booking-config.php.example` nach
|
||
`public\booking-config.php` kopieren und dort die Produktiv-`database_url`
|
||
eintragen. Die echte Konfigurationsdatei ist von Git ausgeschlossen; sie wird
|
||
bei fehlender oder leerer PHP-Umgebungsvariable `DATABASE_URL` verwendet. Die
|
||
mit übertragene `.htaccess` verweigert den HTTP-Zugriff auf diese Datei.
|
||
Voraussetzung ist ein konfigurierter OpenSSH-Zugang mit dem Host-Alias
|
||
`boogie`.
|
||
|
||
## Datenbankgestützte Buchungsdemo
|
||
|
||
Die Buchungsdemo lädt ihre Termine aus MariaDB und speichert eine Anmeldung
|
||
über `public/perspektivwerk/booking/api.php`. Die API wird unabhängig von
|
||
Contao direkt durch Apache ausgeliefert. In der statischen Vorschau liegt sie
|
||
relativ unter `/perspektivwerk/booking/api.php`.
|
||
|
||
Vor dem ersten Aufruf müssen die externen Migrationen eingespielt werden:
|
||
|
||
```powershell
|
||
.\scripts\Invoke-BookingMigrations.ps1
|
||
```
|
||
|
||
`database/migrations/001_booking_schema.sql` ist produktionsgeeignet
|
||
versioniert und idempotent. Sie enthält Tabellen für Termine und Anmeldungen
|
||
sowie klar erkennbare Demo-Termine. Die API prüft Kapazitäten innerhalb einer
|
||
Transaktion und sperrt den Termin dabei mit `SELECT … FOR UPDATE`; parallele
|
||
Anmeldungen können dadurch die Kapazität nicht überschreiten.
|
||
|
||
Die Demo speichert eine Anfrage zunächst als `pending_confirmation` und
|
||
übergibt die Bestätigungsnachricht direkt an das Server-Sendmail. Der Link ist
|
||
30 Minuten gültig; sein Aufruf bestätigt die Teilnahme atomar und löst die
|
||
zweite Nachricht mit ICS-Anhang aus. Ohne Cronjob werden keine Erinnerungen
|
||
verschickt. Lokal ist der Mailtransport bewusst mit
|
||
`BOOKING_MAIL_TRANSPORT=disabled` deaktiviert.
|
||
|
||
Der Browser erhält ein CSRF-Token per gleicher Sitzung; die API validiert alle
|
||
Pflichtdaten serverseitig und begrenzt Anmeldungen pro Sitzung. Dies ist eine
|
||
Demonstration der Schutzrichtung, ersetzt aber keine gesonderte
|
||
Sicherheitsanalyse für den Produktivbetrieb.
|
||
|
||
### API verwenden
|
||
|
||
Die API ist unter `/booking/api.php` erreichbar und akzeptiert ausschließlich
|
||
gleichartige Same-Origin-Anfragen. Sie liefert JSON und verwendet keine
|
||
Zugangsdaten im Browser.
|
||
|
||
Termine eines Monats laden (`to` ist der erste Tag des Folgemonats):
|
||
|
||
```text
|
||
GET /booking/api.php?project=perspektivwerk&from=2026-09-01&to=2026-10-01
|
||
```
|
||
|
||
`project` ist entweder `perspektivwerk` oder `produktionsschule`. Der Zeitraum
|
||
darf höchstens 93 Tage umfassen. Die Antwort enthält ein `csrfToken` und eine
|
||
Liste `events`. Ein Event enthält unter anderem `id`, `title`, `startsAt`,
|
||
`endsAt`, `capacity`, `availableCapacity`, `status` (`available` oder `full`),
|
||
`format` (`vor_ort` oder `telefonisch`) und `introductionRequired`.
|
||
|
||
Eine Anmeldung wird per `POST` angelegt. Dazu muss dieselbe Browser-Sitzung wie
|
||
beim `GET` verwendet und das erhaltene Token im Header
|
||
`X-SUFW-Booking-CSRF` übertragen werden:
|
||
|
||
```http
|
||
POST /booking/api.php
|
||
Content-Type: application/json
|
||
X-SUFW-Booking-CSRF: <csrfToken-aus-dem-GET>
|
||
|
||
{
|
||
"project": "perspektivwerk",
|
||
"eventId": "6c7f216b-2704-4c81-b17a-000000000001",
|
||
"name": "Max Mustermann",
|
||
"email": "max@example.org",
|
||
"phone": "+49 351 123456",
|
||
"privacyAccepted": true,
|
||
"introductionAttended": false,
|
||
"introductionDate": ""
|
||
}
|
||
```
|
||
|
||
Für ein PS-`Probearbeiten` mit `introductionRequired: true` müssen
|
||
`introductionAttended` den Wert `true` und `introductionDate` ein gültiges
|
||
Datum im Format `YYYY-MM-DD` enthalten. Bei Erfolg antwortet die Demo mit
|
||
`202 Accepted`; bei einem inzwischen ausgebuchten oder doppelt gebuchten Termin
|
||
mit `409 Conflict`; fehlerhafte Pflichtangaben erhalten `422`.
|
||
|
||
Der Link ruft `GET /booking/confirm.php?token=<Token>` auf. Erst dort wird die
|
||
Teilnahme in den Status `confirmed` überführt. Nicht bestätigte Anfragen laufen
|
||
nach 30 Minuten ab.
|
||
|
||
## Eigenständiges PHP-Backend
|
||
|
||
Die Testverwaltung läuft unabhängig von Contao unter
|
||
<http://localhost:8080/perspektivwerk/booking-admin/>. Sie bietet für PW und PS:
|
||
|
||
- Termin anlegen, bearbeiten sowie aktivieren/deaktivieren,
|
||
- Übersicht und Entfernen von Teilnahmen,
|
||
- Anzeige bestätigter Teilnahmen nach frei wählbarem Zeitraum,
|
||
- abonnierbaren ICS-Feed je Projekt.
|
||
|
||
Der Verwaltungsbereich hat absichtlich keine Benutzerverwaltung
|
||
und darf nicht unverändert außerhalb der Testumgebung bereitgestellt werden.
|
||
Er ist eine eigenständige PHP-Seite, keine Contao-Komponente.
|
||
|
||
Die zeitgesteuerten Aufgaben können zusätzlich ohne Browser ausgeführt werden:
|
||
|
||
```powershell
|
||
docker compose exec app php scripts/Run-BookingJobs.php
|
||
```
|
||
|
||
Für einen späteren Betrieb muss dieser Aufruf als geplanter Job erfolgen. Die
|
||
ICS-Adresse zeigt die Testverwaltung an; sie enthält nur Terminmetadaten und
|
||
keine Daten der Teilnehmenden.
|
||
|
||
## Zwei Einbindungswege
|
||
|
||
Die Terminbuchungskomponente hat eine Vorschau auf einem Webseiten-Nachbau der Orginalseite:
|
||
|
||
**Apache-Vorschau:** auf der statischen Perspektivwerk-Seite unter <http://localhost:8080/perspektivwerk/>. Die Seite liegt in `public/perspektivwerk/`; ihre Referenzen auf die Komponente verwenden die relativen Pfade `js/booking.css` und `js/booking.js`.
|
||
2. **Contao:** als eigenständiges Inhaltselement vom Typ **HTML** auf einer Contao-Seite. Es enthält das kombinierte Snippet mit CSS, Komponentenplatzhalter und JavaScript.
|
||
|
||
`public/` ist der Apache-Webroot der lokalen Entwicklungsumgebung. Daher ist kein Kopieren der Komponentenassets nach `files/terminbuchung/` erforderlich.
|
||
|
||
## Statische Buchungsvorschau in Contao einbinden
|
||
|
||
Die Komponente lädt Termin- und Kapazitätsdaten über die beschriebene API. Ein
|
||
Klick auf einen freien Termin öffnet eine modale Eingabemaske für Name,
|
||
E-Mail-Adresse, Telefonnummer und die DSGVO-Kenntnisnahme; für Probearbeiten
|
||
kommen Einführungsveranstaltung und Datum hinzu.
|
||
|
||
Die Komponente benötigt keinen TypeScript-Compiler und keinen Build-Schritt. Regressionstests ausführen:
|
||
|
||
```powershell
|
||
npm run test
|
||
```
|
||
### Einbinden in Contao
|
||
|
||
Ein **neues** Inhaltselement vom Typ **HTML** anlegen. Im Feld **HTML-Code** den vollständigen Inhalt aus [integration/contao-html-element.html](integration/contao-html-element.html) einfügen und speichern. Nicht das Inhaltselement **Text** und keinen TinyMCE-Editor verwenden: Diese können `link`- und `script`-Tags als Text maskieren. Für die Produktionsschule wird `data-project="produktionsschule"` gesetzt.
|
||
|
||
In der Seitenvorschau muss der HTML-Quelltext die Tags als `<link ...>` und `<script ...>` enthalten. Erscheinen stattdessen `<link` oder `<script`, wurde nicht das unmaskierte Feld **HTML-Code** verwendet und die Komponente kann nicht starten.
|
||
|
||
### Erforderliche Contao-Sicherheitseinstellung
|
||
|
||
Contao maskiert nicht freigegebene HTML-Tags auch im Inhaltselement **HTML**. Vor dem Einfügen der Komponente unter **System → Einstellungen → Sicherheit** die folgenden Einträge ergänzen:
|
||
|
||
- **Erlaubte HTML-Tags:** `script`, `link`
|
||
- **Erlaubte HTML-Attribute:** für `script` die Attribute `src`, `type`; für `link` die Attribute `href`, `rel`; für den Komponentenplatzhalter auf `div` entweder `data-*` oder mindestens `data-sufw-booking`, `data-project`.
|
||
|
||
Danach den Contao-Cache über **Systemwartung** leeren und das Inhaltselement erneut speichern. Keine globale Freigabe mit `*` verwenden. Wer ein HTML-Inhaltselement bearbeiten darf, kann mit dem freigegebenen `script`-Tag JavaScript auf der Website ausführen; diese Bearbeitungsrechte müssen daher auf vertrauenswürdige Backend-Nutzer beschränkt bleiben.
|
||
|
||
Die Termin- und Kapazitätsdaten kommen in der statischen Vorschau aus
|
||
`/perspektivwerk/booking/api.php`; Datenbankzugänge oder SQL gehören niemals in
|
||
das HTML oder das Browser-JavaScript.
|
||
`public/perspektivwerk/js/booking.js` und die zugehörige CSS-Datei sind die für
|
||
den statischen Upload bestimmte Kopie. Ein Test stellt sicher, dass beide
|
||
Asset-Paare gleich bleiben. Nach Änderungen können sie lokal synchronisiert
|
||
werden mit:
|
||
|
||
```powershell
|
||
Copy-Item public\perspektivwerk\booking\booking.js public\perspektivwerk\js\booking.js -Force
|
||
Copy-Item public\perspektivwerk\booking\booking.css public\perspektivwerk\js\booking.css -Force
|
||
```
|
||
|
||
Der vorhandene SCP-Upload überträgt den vollständigen statischen
|
||
Perspektivwerk-DocumentRoot einschließlich Buchungsdemo. Vor dem externen
|
||
Einsatz müssen die Migrationen eingespielt und `booking-config.php` mit einer
|
||
sicheren Datenbank-URL befüllt sein.
|
||
|
||
## Statische Perspektivwerk-Seite übertragen
|
||
|
||
Das Skript [scripts/Deploy-Perspektivwerk.bat](scripts/Deploy-Perspektivwerk.bat) überträgt den Ordner `public/perspektivwerk/` als vollständigen DocumentRoot per SCP und korrigiert anschließend die Lesbarkeit auf dem Server per SSH. Es lädt weder Contao noch Datenbank-Migrationen hoch.
|
||
|
||
```bat
|
||
scripts\Deploy-Perspektivwerk.bat
|
||
```
|
||
|
||
Der Hostname `boogie` wird aus der lokalen SSH-Konfiguration verwendet. Der
|
||
Ordner landet unter `/home/boogie/html/perspektivwerk/perspektivwerk/`; die
|
||
Konfiguration liegt daneben unter `/home/boogie/html/perspektivwerk/booking-config.php`.
|
||
Anschließend führt ein separater SSH-Aufruf `chmod -R a+rX
|
||
/home/boogie/html/perspektivwerk` aus. Die Passphrase des SSH-Schlüssels wird
|
||
daher in der Regel zweimal abgefragt.
|
||
|
||
Vor dem Upload muss die ignorierte `booking-config.php` befüllt sein. Das große
|
||
`X` in `a+rX` setzt das Ausführungsrecht nur auf Verzeichnissen und auf bereits
|
||
ausführbaren Dateien; normale HTML-, CSS- und JavaScript-Dateien erhalten kein
|
||
unnötiges Ausführungsbit.
|
||
|
||
## Versionierter Contao-Seitenzustand
|
||
|
||
Die Contao-Seiten, Artikel und Layouts liegen in MariaDB und werden deshalb zusätzlich als bereinigter SQL-Seed versioniert. Details zu Export, Import und den bewusst ausgeschlossenen Daten stehen in [database/README.md](database/README.md).
|
||
|
||
Die Seed-Integritätsprüfungen ausführen:
|
||
|
||
```powershell
|
||
Invoke-Pester .\tests\ContaoBaseline.Tests.ps1
|
||
```
|