Files
admin.juergen 4fe142422a Warn on leaving the editor with unsaved changes
Ergänzt einen beforeunload-Handler im Frontend-Editor: Beim Verlassen der
Seite (Link, Zurück, Tab schließen) wird gewarnt, sobald mindestens ein
Editor-Formular ungespeicherte Änderungen hat. Der Handler koppelt an die
bestehende Dirty-Erkennung (data-survey-dirty-form) und wird nur
registriert, wenn solche Formulare existieren – also nur im Editor, nicht
in Liste/Reader/Ergebnissen.

Ein bewusstes Absenden (Speichern oder Aktion) setzt über einen globalen
submit-Listener ein Flag, sodass der normale Speicher-Flow nicht warnt.

Bewusst kein Auto-Save (getrennte Formulare mit eigenen CSRF-Tokens,
Struktur-Sperre nach Teilnahmen/Veröffentlichung); Dirty-Hinweis plus
beforeunload sind der risikoarme Kompromiss.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-05 20:24:09 +02:00

534 lines
23 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Survey Bundle AI Handover
Diese Datei beschreibt den aktuell gewünschten und umgesetzten Stand des Bundles. Ziel ist, dass ein KI-Modell oder ein neuer Bearbeiter die Logik, die Zustände und die wichtigsten Einstiegspunkte schnell versteht und darauf weiterarbeiten kann.
## Zweck des Bundles
Das Bundle stellt ein Contao-Umfragesystem bereit mit:
- Frontend-Liste für Redakteure
- Frontend-Frageneditor
- Öffentlicher Umfrageansicht über Alias-Link
- Ergebnisseite im Frontend
- Excel-Export der Ergebnisse
- PDF-Export der Ergebnisse über Gotenberg
- Backend-Verwaltung in Contao-DCA-Listen und Editmasken
## Zentrale Zustandsfelder
Die Umfrage arbeitet mit drei relevanten Zuständen in `tl_survey`:
- `published`
- Fachlich: Die Umfrage wurde veröffentlicht.
- Frontend: Ab Veröffentlichung darf der Redakteur im Frontend weder Fragen noch Metadaten bearbeiten.
- Backend: Reversibel erlaubt. Der Admin darf den Status im Backend ändern.
- `isActive`
- Fachlich: Öffentliche Sichtbarkeit an/aus.
- Gilt unabhängig vom Veröffentlichungsstatus als technischer Sichtbarkeitszustand.
- Der öffentliche Reader liefert nur Umfragen aus, die `published = 1` und `isActive = 1` haben.
- `isLocked`
- Fachlich: Struktur wurde bereits benutzt oder veröffentlicht.
- Wird automatisch gesetzt, wenn die erste Antwort gespeichert wird.
- Wird beim Veröffentlichen ebenfalls gesetzt.
- Im Backend gibt es bewusst keine harte Sperre mehr. Das Feld dient vor allem der internen Logik und Historie.
## Gewollte Produktentscheidung
- Frontend ist streng:
- Keine Fragenbearbeitung nach Veröffentlichung
- Keine Metadatenbearbeitung nach Veröffentlichung
- Keine Fragenbearbeitung nach ersten Teilnahmen
- Die Frontend-Sperre für veröffentlichte Umfragen soll bewusst nicht tief im System verankert sein.
- Für Redakteure genügt das Ausblenden bzw. Deaktivieren der relevanten Buttons und Formulare im Frontend.
- Es ist ausdrücklich keine zusätzliche harte Systembarriere gewünscht, solange die Redakteursoberfläche den Bearbeitungspfad sauber schließt.
- Backend ist bewusst toleranter:
- Ein einzelner Admin darf im Notfall weiter arbeiten
- Statt harter Sperre gibt es nur einen Warnhinweis in der Liste bei veröffentlichten Umfragen
- `published` ist im Backend editierbar
- `isActive` ist im Backend per Contao-Toggle schaltbar
- Bewusster Trade-off zwischen Frontend und Backend:
- Ein veröffentlichter Survey soll für Frontend-Redakteure praktisch nicht mehr bearbeitbar sein.
- Im Backend darf der Admin trotz Veröffentlichung weiterhin Änderungen vornehmen, zum Beispiel für Rechtschreibfehler oder kleine Korrekturen.
- Dadurch bleibt bewusst das Risiko bestehen, dass der Admin auch Fragen oder Strukturen ändert, obwohl bereits Antworten vorliegen.
- Mögliche Folgen sind verwässerte oder fachlich falsche Ergebnisseiten sowie Inkonsistenzen, wenn bestehende Antworten nicht mehr sauber zur später geänderten Struktur passen.
- Dieses Risiko wird bewusst akzeptiert und organisatorisch über Briefing/Disziplin des Admins bzw. der zuständigen Redakteure abgefangen, nicht über eine harte technische Sperre im Backend.
Diese Entscheidung ist absichtlich so getroffen und kein Bug.
## Datenmodell und wichtige Tabellen
- `tl_survey`
- Umfrage-Stammdaten und Statusfelder
- Enthält zusätzlich die backend-only gepflegte `internalNote`
- `tl_survey_content`
- Fragen der Umfrage
- `tl_survey_condition`
- Generische Sprungregeln
- `tl_survey_editor`
- Zugewiesene Frontend-Redakteure
- `tl_survey_submission`
- Umfrage-Durchläufe
- `tl_survey_answer`
- Gespeicherte Antworten inklusive Snapshot-Feldern zur Ergebnisstabilität
Wichtige Snapshot-Felder in `tl_survey_answer`:
- `questionType`
- `questionLabel`
- `questionDescription`
- `questionSorting`
Diese Felder sorgen dafür, dass Ergebnisse auch dann stabil bleiben, wenn Fragen später im Backend verändert oder gelöscht wurden.
## Frontend-Module und Controller
### 1. Redakteursliste
Controller:
- `src/Controller/FrontendModule/MemberSurveyListController.php`
Template:
- `contao/templates/frontend/member_survey_list.html.twig`
Zweck:
- Zeigt alle dem eingeloggten Frontend-Mitglied zugewiesenen Umfragen
- Sortierung ist bewusst fest auf `Titel ASC`
- Hier liegen die Redakteursaktionen zur Veröffentlichung und Aktivierung
Aktuelle Button- und Sichtbarkeitslogik pro Umfrage:
- `Bearbeiten`
- Nur klickbar, solange `published = 0`
- Nach Veröffentlichung disabled
- `Öffentliche Ansicht`
- Nur sichtbar, wenn `published = 1`
- `Link kopieren`
- Nur sichtbar, wenn `published = 1`
- Kopiert die **absolute** öffentliche Reader-URL (`copyUrl`, erzeugt mit `UrlGeneratorInterface::ABSOLUTE_URL`) über den `data-copy-text`-Handler in `_survey_assets.html.twig`.
- `Entwurfs-Ansicht`
- Nur sichtbar, wenn `published = 0`
- `Ergebnisse anzeigen`
- Immer verfügbar, sofern Results-Seite konfiguriert ist
- Tab-Verhalten (bewusste Produktentscheidung nach Kundenrückmeldung):
- `Bearbeiten` öffnet **im selben Tab** (eigentlicher Arbeitsschritt).
- `Öffentliche Ansicht`, `Entwurfs-Ansicht` und `Ergebnisse anzeigen` öffnen **im neuen Fenster** (`target="_blank"`), damit die Editor-/Listenansicht erhalten bleibt.
- `Umfrage veröffentlichen`
- Eigene Aktion in der Liste
- Nach Veröffentlichung disabled und textlich als veröffentlicht markiert
- `Duplizieren`
- Legt eine Kopie der Umfrage für den Redakteur an
- Titelvergabe ist fortlaufend: `Titel - Kopie`, danach `Titel - Kopie 2`, `Titel - Kopie 3` usw.
- Bestehende `- Kopie`-Suffixe werden fachlich normalisiert, damit keine Titel wie `... - Kopie - Kopie` mehr neu entstehen
- Wichtig: Die Titel-Kollisionsprüfung (`SurveyEditorService::buildDuplicateTitle`) prüft **ohne** Ausschluss der Quell-ID gegen `titleExists`. Sonst würde beim Duplizieren einer bereits `... - Kopie`-Umfrage erneut derselbe Titel vergeben statt `... - Kopie 2` (früherer Bug).
- Aktiv-Umschalter (grauer/grüner Punkt **mit Textlabel** „Aktiv"/„Deaktiviert")
- Schaltet `isActive`
- Ist bewusst kein Auge mehr
- Das Textlabel wurde ergänzt, weil der reine Punkt ohne Beschriftung für den Kunden unklar war (`survey-visibility-toggle` in `_survey_assets.html.twig`).
- Bei `isActive = 0` ist der öffentliche Reader-Link deaktiviert: `SurveyRepository::findByPublicAlias` verlangt `published = 1` UND `isActive = 1`, sonst liefert der Reader die Leer-/Hinweisseite.
Wichtiger technischer Punkt (CSRF):
- Jedes Frontend-Aktionsformular (Liste **und** Editor) sendet bewusst **zwei** Tokens:
- `REQUEST_TOKEN` (`{{ contao.request_token }}`) nötig für Contaos globalen Frontend-POST-Gate; fehlt er, lehnt Contao den POST schon vor dem Controller mit HTTP 400 ab.
- `_token` (`{{ csrf_token('<aktion>-' ~ id) }}`) wird im Controller mit `isCsrfTokenValid('<aktion>-'.$id, $token)` geprüft (session-basierter Symfony-Token).
- Beide sind erforderlich. Frühere Stände hatten nur einen davon das führte zu „Ungültiger CSRF-Token" (500) bzw. Bad Request (400). Der Listen-Controller spiegelt jetzt exakt die Token-Mechanik des Editors.
- Der Contao-`REQUEST_TOKEN` allein reicht **nicht** als Controller-Prüfung: Er ist cookie-gebunden und bei anonymen/fragmentierten Frontend-Requests nicht stabil (Contao setzt das CSRF-Cookie dort teils gar nicht). Deshalb die zusätzliche session-basierte `_token`-Prüfung.
### 2. Frontend-Editor
Controller:
- `src/Controller/FrontendModule/MemberSurveyEditController.php`
Template:
- `contao/templates/frontend/member_survey_edit.html.twig`
Zweck:
- Anlegen neuer Umfragen
- Bearbeiten der Metadaten vor Veröffentlichung
- Bearbeiten der Fragenstruktur
Aktueller Aufbau:
- Bei neuer Umfrage:
- Titel und Beschreibung
- Bei bestehender, nicht veröffentlichter Umfrage:
- Metadatenformular oberhalb des Frageneditors
- Frage-für-Frage-Editor im Akkordeon
- Neue Frage anlegen
- Sprungregelübersicht rechts/unten je nach Breakpoint
- Bei veröffentlichter Umfrage oder bei `isLocked = 1`:
- Fragen im Frontend nicht mehr änderbar
- Metadaten im Frontend ebenfalls nicht mehr bearbeitbar
Ungespeicherte Änderungen (bewusst kein Auto-Save):
- Jedes Editor-Formular (`data-survey-dirty-form`: Metadaten, jede Frage, neue Frage) trackt clientseitig, ob sich der Inhalt gegenüber dem Ladezustand geändert hat, und blendet den roten „Ungespeicherte Änderungen"-Hinweis ein.
- Zusätzlich gibt es einen `beforeunload`-Handler (`_survey_assets.html.twig`), der beim Verlassen der Seite (Link, Zurück, Tab schließen) warnt, sobald mindestens ein Formular ungespeicherte Änderungen hat.
- Der Handler wird nur registriert, wenn dirty-getrackte Formulare existieren (also nur im Editor, nicht in Liste/Reader/Ergebnissen).
- Ein bewusstes Absenden (Speichern oder eine Aktion) setzt ein `surveyIsSubmitting`-Flag über einen globalen `submit`-Listener, damit der normale Speicher-Flow **nicht** warnt.
- Auto-Save wurde bewusst nicht umgesetzt (getrennte Formulare mit eigenen CSRF-Tokens, Struktur-Sperre nach Teilnahmen/Veröffentlichung). Der Dirty-Hinweis plus die `beforeunload`-Warnung sind der gewählte, risikoarme Kompromiss.
Wichtige Entscheidung:
- Die Header-Badges im Fragen-Akkordeon wurden bewusst entfernt.
- Typ, Pflicht usw. sollen nicht schon im Header gezeigt werden.
- Die Details sieht man erst nach dem Aufklappen.
- Die Sprungziel-Dropdowns im Frontend zeigen Positionsnummern der Umfrage (`1. ...`, `2. ...`) statt globaler Datenbank-IDs.
- Die aktuell bearbeitete Frage wird dort nicht als eigenes Sprungziel angeboten.
### 3. Öffentliche Umfrageansicht
Controller:
- `src/Controller/FrontendModule/ShowSurveyController.php`
Services:
- `src/Service/SurveyFlowService.php`
- `src/Service/SurveySubmissionService.php`
Zweck:
- Öffentliche Teilnahme an Umfragen
- Zusätzlich Entwurfsansicht per `?preview=1`
Wichtige Regeln:
- Öffentliche Ansicht findet eine Umfrage nur, wenn:
- `published = 1`
- `isActive = 1`
- Entwurfsansicht arbeitet ohne Speicherung
- Öffentliche Ansicht speichert Antworten und erstellt/führt Submission fort
- Rückwärtsnavigation ist implementiert
- Nach Abschluss zeigt der gleiche Browser-Token für eine kurze Cooldown-Zeit (`RESTART_COOLDOWN_SECONDS = 60`) die Danke-Seite; danach darf dieselbe Session erneut teilnehmen (`SurveySubmissionService::isRestartAllowed`).
Saubere Link-URLs (Reader und Ergebnisseite):
- Die Umfrage wird über den Alias als **Pfad-Segment** adressiert: `/<reader-seite>/<alias>` statt `?auto_item=<alias>`.
- URL-Erzeugung: per Route-Platzhalter `['parameters' => '/'.$alias]` (Frontend-Liste `MemberSurveyListController`, Backend `SurveyDcaListener::buildSurveyPageUrl`), nicht `['auto_item' => $alias]`.
- Auslesen im Controller: `$this->getContaoAdapter(Input::class)->get('auto_item')`. Wichtig: `Input::get` markiert den Route-Parameter als **benutzt** sonst wirft Contaos `LegacyRouteParametersListener` eine 404 wegen „unbenutzter Parameter". Ein reines `$request->query->get('auto_item')` reicht für Pfad-URLs nicht.
### 4. Ergebnisseite
Controller:
- `src/Controller/FrontendModule/ShowSurveyResultsController.php`
Service:
- `src/Service/SurveyResultsViewService.php`
Template:
- `contao/templates/frontend/show_survey_results.html.twig`
Zweck:
- Aggregierte Auswertung pro Frage
- Anzeige von Anzahl Teilnahmen, Rücklauf und Frageergebnissen
Aktuelle Ergebnislogik:
- `yes_no_maybe`
- Optionsstatistik plus Kreisdiagramm
- `choice`
- Optionsstatistik plus Kreisdiagramm
- `range`
- Minimum, Maximum, Durchschnitt plus Verteilung und Linienchart
- **Diagramm** (`distribution`): deckt die **komplette Skala** von `rangeMin` bis `rangeMax` in `rangeStep`-Schritten ab (inkl. Werte ohne Antworten), damit die X-Achse linear und vollständig ist. Antworten außerhalb der konfigurierten Grenzen erweitern die Skala. Siehe `SurveyResultsViewService::buildRangeScale`.
- **Werte-Liste/Tabelle** (`distributionRows`): zeigt bewusst nur die **tatsächlich beantworteten** Werte (aufsteigend). So bläht sich die Ergebnisseite bei großen Skalen (z. B. Alter 1100 mit Schrittweite 1) nicht mit hunderten Nullzeilen auf. Die vollständige Skala bleibt trotzdem im Diagramm sichtbar. Online-Liste, PDF-Tabelle und die SVG-/Chart.js-Diagramme greifen entsprechend auf `distributionRows` (Liste) bzw. `distribution` (Diagramm) zu.
- Bei großen Skalen werden die X-Achsen-Beschriftungen ausgedünnt (Chart.js `autoSkip`/`maxTicksLimit`, SVG jede n-te Beschriftung) und die Punkt-Marker verkleinert bzw. ab >40 Punkten weggelassen. Die PDF-Range-Tabelle hat keine Farb-Swatch-Spalte mehr (nur Wert/Anzahl/Anteil), weil die Kopplung an ein Vollskalen-Diagramm nicht mehr sinnvoll ist.
- `text`
- Kein Pie-Chart
- Gruppierte Freitextantworten: jede unterschiedliche Antwort **einmal** mit exakter Häufigkeit (`responseSummary`, häufigste zuerst, kein Top-5-Limit). Prozentangaben und die frühere Doppel-Auflistung wurden bewusst entfernt.
Pro Frage werden statt einer reinen Prozent-Rücklaufquote absolute Zahlen ausgewiesen:
- `totalAnswers` und `completedSubmissionCount` als „X von Y beantwortet".
- `skippedCount` (= `completedSubmissionCount - totalAnswers`) als „Z übersprungen", nur wenn > 0. So sind per Sprungregel übersprungene Fragen sichtbar, statt überall die volle Teilnehmerzahl zu zeigen.
Reihenfolge:
- Die Frageergebnisse werden **vor** dem Finalisieren nach `_sorting` sortiert. Wichtig: `finalizeQuestionResult` entfernt die internen `_sorting`-Werte würde man erst danach sortieren (früherer Bug), fiele die Reihenfolge auf den alphabetischen Fallback zurück. Online-Ansicht, PDF und Excel folgen dadurch derselben Frage-Sortierung.
Wichtig:
- Die Ergebnisse werden aus den Antwort-Snapshots aufgebaut, nicht nur aus den aktuellen Fragen.
- Dadurch bleiben historische Ergebnisse stabil.
## Sprunglogik
Relevante Services/Repos:
- `src/Service/SurveyFlowService.php`
- `src/Repository/SurveyConditionRepository.php`
- `src/Controller/FrontendModule/MemberSurveyEditController.php`
Reihenfolge der Navigation:
1. Direkter Ja/Nein-Sprung über `jumpOnYes` / `jumpOnNo`
2. Generische Bedingung aus `tl_survey_condition`
3. Nächste Frage nach Sortierung
Wichtiger Detailpunkt:
- In der Sprungregelübersicht im Frontend-Editor werden bewusst die Positionsnummern innerhalb der Umfrage angezeigt, nicht die globalen Datenbank-IDs der Fragen.
- Gleiches gilt für die Sprungziel-Dropdowns in Backend und Frontend.
Wichtige technische Eigenschaft:
- `jumpOnYes`, `jumpOnNo` und generische Bedingungen referenzieren Ziel-Fragen intern immer über die Frage-ID, nicht über die Position.
- Dadurch bleiben Sprünge bei einer reinen Umordnung der Fragen fachlich stabil an dieselbe Frage gebunden.
- Wenn eine Ziel-Frage an eine andere Position verschoben wird, zeigt die Oberfläche danach automatisch die neue Positionsnummer an.
## Excel-Export
Controller:
- `src/Controller/SurveyResultsExportController.php`
Service:
- `src/Service/SurveyResultsExportService.php`
Bibliothek:
- `phpoffice/phpspreadsheet`
Zweck:
- Export aller abgeschlossenen Teilnahmen in eine XLSX-Datei
Aktuelle Spaltenlogik:
- Ja/Nein/Vielleicht:
- pro Antwortoption eigene Spalte
- Choice:
- pro Antwortoption eigene Spalte
- Range:
- eine Wertespalte
- Text:
- eine Textspalte
Wichtige Eigenschaft:
- Die Spalten orientieren sich an den aktuell vorhandenen Fragen im Survey.
- Die Zeilen enthalten nur abgeschlossene Submissions.
- Über den Antwortspalten steht eine eigene **Fragetext-Zeile** (`F1: <Fragetext>`, `F2: ...`). Je Frage werden die Zellen dieser Zeile über alle zugehörigen Antwortspalten zusammengeführt (`mergeCells`). Zuvor fehlte der Fragetext im Excel komplett der Kunde sah nur `F1 ja`, `F1 nein` usw. Reihenfolge: Titel (Zeile 1), Fragetexte (Zeile 2), Spaltenköpfe (Zeile 3), Daten ab Zeile 4; Freeze-Pane auf `A4`.
- Diagramme gibt es im Excel bewusst nicht (dafür ist der PDF-Export da).
## PDF-Export
Controller:
- `src/Controller/SurveyResultsExportController.php`
Service:
- `src/Service/SurveyResultsPdfService.php`
Template:
- `src/Resources/views/pdf/survey_results.html.twig`
Externe Abhängigkeit:
- Gotenberg über `surveyGotenbergUrl` in den Contao-Einstellungen
Zweck:
- Serverseitiger PDF-Export der Ergebnisse
Aktuelle Logik:
- HTML wird serverseitig mit Twig gerendert
- Diagramme werden als SVG/Data-URI vorbereitet
- Gotenberg rendert daraus das PDF
- Ist keine Gotenberg-URL gesetzt, wird im Frontend kein PDF-Download angeboten
## Backend-Aufbau
### tl_survey
Dateien:
- `contao/dca/tl_survey.php`
- `contao/languages/de/tl_survey.php`
- `contao/languages/en/tl_survey.php`
Wichtige Listenoperationen:
- `Fragen bearbeiten`
- `Umfrage bearbeiten`
- `isActive` als Contao-Toggle
- Warnsymbol bei veröffentlichten Umfragen
- `Link kopieren`
- `Ergebnisse anzeigen`
Aktuelle Listen- und Filterlogik:
- Die mehrspaltige Umfragenliste zeigt aktuell:
- `Titel`
- `Zugewiesene Benutzer`
- `Vorlage`
- `Interne Notiz`
- `Kategorien` wurden bewusst aus der mehrspaltigen Liste und aus der rechten Filterspalte entfernt.
- Die Spalte `Zugewiesene Benutzer` wird in der Listenmetadatenlogik gekürzt:
- maximal 3 Namen direkt sichtbar
- danach Zusatz wie `... und X weitere`
- `internalNote`
- backend-only Feld in `tl_survey`
- nur im Backend bearbeitbar
- in der mehrspaltigen Liste sichtbar
- bei Duplikaten wird der Wert mitkopiert
- In der rechten Suchspalte ist `Interne Notiz` suchbar.
- `ID` wurde für die Umfragenliste bewusst aus dem Suchfeld entfernt.
- Das geschieht über einen kleinen backend-spezifischen JS-Workaround, weil Contao `id` im Core für `DC_Table` hart ergänzt.
Aktuelle Backend-Entscheidung:
- Das Warnsymbol genügt als Hinweis bei veröffentlichten Umfragen
- Es gibt bewusst keine harte Sperre für Fragen im Backend
### Child-Tabellen
- `tl_survey_content`
- `tl_survey_condition`
- `tl_survey_editor`
Hinweis:
- `isLocked` wird in den Child-Headerfeldern nicht mehr angezeigt, weil der Backend-Lock bewusst keine führende Benutzerfunktion mehr ist.
- In `tl_survey_content` ist der Fragenlisten-Header zusätzlich reduziert:
- nur noch `Titel` und `Veröffentlicht`
- kein öffentlicher Link-Schlüssel mehr
- die rechte Header-Leiste mit Aktionen ist im Normalzustand ausgeblendet
- im aktiven Verschiebe-/Clipboard-Modus wird sie wieder eingeblendet, damit `Neu oben` bzw. die Einfügepositionen erreichbar bleiben
Fragenliste `tl_survey_content`:
- Direkt sichtbare Primäraktion pro Frage:
- `Bearbeiten`-Stift
- Verschieben:
- erfolgt im Backend über den normalen Contao-Paste-/Move-Flow
- die Aktion `Verschieben` liegt bewusst nur unter den drei Punkten
- dort wird der Contao-Pfeil `mover.svg` verwendet, nicht die blaue Schere
- nach Aktivierung des Clipboard-Zustands erscheinen die üblichen Einfügeziele wie `Nach dem Element ... einfügen`
### Systemeinstellungen
Datei:
- `contao/dca/tl_settings.php`
Wichtige Settings:
- `surveyReaderPage`
- `surveyResultsPage`
- `surveyGotenbergUrl`
## Aktuelle Produktregeln in Kurzform
- Frontend-Liste:
- Titel alphabetisch
- Veröffentlichung und Aktivierung in der Liste
- Frontend-Editor:
- Metadaten nur vor Veröffentlichung
- Fragenbearbeitung nur solange nicht veröffentlicht und keine Teilnahmen vorliegen
- Öffentliche Ansicht:
- Nur `published = 1` und `isActive = 1`
- Backend:
- `published` reversibel erlaubt
- `isActive` per Standard-Contao-Toggle
- nur Warnsymbol statt Backend-Sperre
- Admin darf Fragen auch nach Veröffentlichung oder nach vorhandenen Antworten umsortieren
## Wichtige Dateien nach Themengebiet
Frontend-Liste:
- `src/Controller/FrontendModule/MemberSurveyListController.php`
- `contao/templates/frontend/member_survey_list.html.twig`
Frontend-Editor:
- `src/Controller/FrontendModule/MemberSurveyEditController.php`
- `contao/templates/frontend/member_survey_edit.html.twig`
Öffentliche Umfrage:
- `src/Controller/FrontendModule/ShowSurveyController.php`
- `src/Service/SurveyFlowService.php`
- `src/Service/SurveySubmissionService.php`
Ergebnisse:
- `src/Controller/FrontendModule/ShowSurveyResultsController.php`
- `src/Service/SurveyResultsViewService.php`
- `contao/templates/frontend/show_survey_results.html.twig`
Exporte:
- `src/Controller/SurveyResultsExportController.php`
- `src/Service/SurveyResultsExportService.php`
- `src/Service/SurveyResultsPdfService.php`
Backend:
- `contao/dca/tl_survey.php`
- `src/EventListener/SurveyDcaListener.php`
## Technische Validierung, die in diesem Projekt gut funktioniert
- `php -l` auf die geänderten PHP-Dateien
- `ddev exec php bin/console lint:yaml ...`
- `ddev exec php bin/console cache:clear`
## Offene technische Punkte / Deployment-Hinweise
- Neue DB-Spalte in `tl_survey`:
- `internalNote`
- Typ aktuell: `varchar(255) NOT NULL default ''`
- Für andere Umgebungen muss diese Spalte per Migration/SQL vorhanden sein, sonst fehlen Backend-Feld, Listenanzeige und Suchfunktion fachlich bzw. führen zu Inkonsistenzen.
- Nach Änderungen an DCA, Backend-CSS oder Backend-JS ist in dieser Installation typischerweise nötig:
- `php vendor/bin/contao-console assets:install public --symlink --relative`
- `php vendor/bin/contao-console cache:clear --env=prod`
- Es gibt aktuell zwei bewusst eingebaute backend-spezifische UI-Workarounds über Bundle-Assets:
- In der Umfragenliste wird `ID` aus dem Suchfeld per JS entfernt, weil Contao das Feld im Core von `DC_Table` hart ergänzt.
- In der Fragenliste (`table=tl_survey_content`) wird die rechte Parent-Header-Leiste per CSS/JS im Normalzustand ausgeblendet und nur im aktiven Clipboard-/Verschiebe-Modus wieder eingeblendet.
- Lokale DDEV-Besonderheit, die bei Fehleranalyse relevant sein kann:
- Das Bundle läuft hier als Path-Repository/Symlink.
- Wenn Backend-Übersetzungen oder Assets trotz korrektem Code unerwartet fehlen, zuerst Bundle-Pfad, veröffentlichte Assets und Prod-Cache prüfen.
## Bekannte bewusste Trade-offs
- Backend kann veröffentlichte Umfragen technisch weiterhin verändern.
- Das ist bewusst akzeptiert, weil nur ein Admin arbeitet.
- `isLocked` bleibt als internes Logikfeld bestehen, obwohl der Backend-Lock nicht mehr durchgesetzt wird.
- PDF-Export hängt von Gotenberg ab und ist absichtlich optional.
- Eine reine Umordnung von Fragen ist für Sprungziele logisch unkritisch, weil Sprünge an Frage-IDs hängen.
- Ergebnisse vorhandener, weiterhin existierender Fragen folgen bei der Anzeige jedoch der aktuellen Sortierung der Frage.
- Historische Antwort-Snapshots speichern zwar `questionSorting`, die Ergebnisansicht sortiert vorhandene Fragen aber primär nach der aktuellen Frage-Sortierung.
- Das ist im toleranten Backend-Modell ein bewusst akzeptierter Side-Effect.
## Empfohlene Einstiegspunkte für künftige Änderungen
- Status-/Sichtbarkeitslogik: `SurveyRepository`, `SurveyEditorService`, `MemberSurveyListController`
- Fragerouting: `SurveyFlowService`, `SurveyConditionRepository`
- Ergebnisdarstellung: `SurveyResultsViewService`
- Exportlogik: `SurveyResultsExportService`, `SurveyResultsPdfService`
- Backend-Operationen und DCA-Verhalten: `tl_survey.php`, `SurveyDcaListener`