From 6afad3095623b2539f19c62ada3bd2f229923bf3 Mon Sep 17 00:00:00 2001 From: Umut Kantarci Date: Sat, 12 Sep 2026 14:45:09 +0200 Subject: [PATCH] Seiten verschieben, Papierkorb, Fassungen, Zeitplan (Arbeitspaket F) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Migration 20260912000900 ergänzt page.publish_at, page.deleted_at und revision.seq. Verschieben nimmt den ganzen Teilbaum mit und hinterlässt für jede alte Adresse ein 301. Kollisionen, Tiefengrenze und das Verschieben unter eigene Unterseiten werden vor der ersten Änderung abgewiesen; alles in einer Transaktion, weil ein halb verschobener Teilbaum Seiten unerreichbar machen würde. Der Papierkorb ersetzt das sofortige Löschen für die Editorrolle: alle Fassungen bleiben, öffentlich 404, verschwunden aus Navigation, Archiven, Feed, Sitemap, llms.txt und Editorliste. Unterseiten gehen mit, beim Wiederherstellen muss das Elternteil zuerst zurück. Eine Fassung wiederherzustellen überschreibt nichts, sondern legt eine neue an. revision.seq behebt dabei einen echten Fehler: die Reihenfolge des Verlaufs war nicht eindeutig, weil created_at nur Sekunden kennt und mehrere Speichervorgänge in dieselbe Sekunde fallen - "vorherige Fassung wiederherstellen" hätte die falsche treffen können. Geplante Veröffentlichung über cms:publish-due. Ohne Cron bleibt die Seite ein Entwurf, was die sichere Richtung ist. Eine frei wählbare Startseite fehlt bewusst: der Pfad home gilt an elf Stellen als Wurzel, das ist ein eigener Schritt (ADR 022). Geprüft: 160 PHP-Tests / 622 Assertions unter PHP 8.2.12 und 8.4.22, 15 JavaScript-Modelltests, vollständiger Editortest mit axe, Migration zuerst gegen eine Datenbankkopie samt Kontrolle des Backfills. Co-Authored-By: Claude Opus 5 --- docs/ARCHITECTURE.md | 16 ++ docs/AUSBAUPLAN.md | 4 +- docs/QUALITY.md | 11 ++ docs/STATUS.md | 13 +- migrations/Version20260912000900.php | 77 ++++++++++ src/Command/PublishDueCommand.php | 33 ++++ src/Content/MenuRepository.php | 2 +- src/Content/PageRepository.php | 217 ++++++++++++++++++++++++++- src/Content/PostRepository.php | 2 +- src/Controller/EditorController.php | 109 +++++++++++++- src/Controller/SeoController.php | 6 +- templates/admin/pages.html.twig | 71 ++++++++- templates/admin/revisions.html.twig | 45 ++++++ tests/CmsTest.php | 155 +++++++++++++++++++ tests/EditorSecurityTest.php | 3 + tests/browser-fixture.php | 3 + 16 files changed, 750 insertions(+), 17 deletions(-) create mode 100644 migrations/Version20260912000900.php create mode 100644 src/Command/PublishDueCommand.php create mode 100644 templates/admin/revisions.html.twig diff --git a/docs/ARCHITECTURE.md b/docs/ARCHITECTURE.md index 9880908..2b610f4 100644 --- a/docs/ARCHITECTURE.md +++ b/docs/ARCHITECTURE.md @@ -268,3 +268,19 @@ Die Verwaltung erlaubt zusätzlich die Schrifthosts, damit die Vorschau dieselbe Zwei weitere Fehler derselben Ursache wurden dabei gefunden: die Einrückung der Unterseiten in der Seitenliste lief über ein `style`-Attribut und wurde ebenfalls verworfen – die Hierarchie sah immer flach aus. Sie nutzt jetzt Klassen. Und das Theme setzte **alle** Links auf die Akzentfarbe und überschrieb damit die kontextabhängigen Farben; auf dunklem Grund ergab das 3,08:1. Die Regel gilt jetzt nur für Links in Textblöcken, und die Voreinstellung für Linkfarbe ist ein dunklerer Verwandter des Akzents, der auf allen hellen Tönen über 4,5:1 liegt. Bewegungseffekte bleiben als eigener Abschnitt im Panel für jeden Block erhalten: Schatten-, Hover- und Einblendungsvorlagen sind Keyframes und lassen sich mit den freien Deklarationen nicht ausdrücken. Die alten Tokenwerte erscheinen dagegen nur noch dort, wo eine Seite sie schon trägt. + +## ADR 022 – Seiten verwalten: Verschieben, Papierkorb, Fassungen, Zeitplan (12.09.2026) + +Migration `20260912000900` ergänzt `page.publish_at`, `page.deleted_at` und `revision.seq`. + +**Verschieben nimmt den ganzen Teilbaum mit und hinterlässt Weiterleitungen.** Der Vorgang läuft in einer Transaktion: ein halb verschobener Teilbaum hätte Seiten unerreichbar gemacht. Vor der ersten Änderung werden alle betroffenen Adressen eingesammelt, auf Kollisionen und auf die Tiefengrenze geprüft; erst danach wird geschrieben und für jede alte Adresse ein 301 vermerkt. Eine Seite unter eine ihrer eigenen Unterseiten zu verschieben wird abgewiesen, weil das den Teilbaum abtrennen würde. + +**Der Papierkorb ersetzt das sofortige Löschen für die Editorrolle.** Eine Seite im Papierkorb behält alle Fassungen, antwortet öffentlich mit 404 und verschwindet aus Navigation, Archiven, Feed, Sitemap, `llms.txt` und der Editorliste. Unterseiten gehen mit, weil sie ohne ihr Elternteil in der Adresse nicht erreichbar wären; beim Wiederherstellen muss deshalb zuerst das Elternteil zurück. Das endgültige Löschen mit allen Fassungen bleibt der Administratorrolle. + +**Eine Fassung wiederherzustellen überschreibt nichts.** Das alte Dokument wird erneut gespeichert und damit eine neue Fassung, sodass der bisherige Stand in der Liste bleibt. + +`revision.seq` behebt einen echten Fehler, den erst ein Test gezeigt hat: `created_at` hat Sekundengenauigkeit, und mehrere Speichervorgänge fallen in dieselbe Sekunde. Die Reihenfolge im Verlauf war dadurch **nicht eindeutig** – sie richtete sich nach der zufälligen Revisions-ID. Jetzt trägt jede Fassung einen je Seite streng aufsteigenden Zähler; die Migration füllt ihn aus den vorhandenen Zeitstempeln. + +**Geplante Veröffentlichung** setzt `publish_at`. Ein Cron-Aufruf von `cms:publish-due` veröffentlicht, was fällig ist, und löscht den Zeitpunkt. Ohne Cron bleibt die Seite ein Entwurf – die sichere Richtung. Zeitpunkte sind UTC; das steht im Formular, weil ein `datetime-local`-Feld sonst die Zeitzone des Browsers unterstellt. Eine Seite im Papierkorb wird nie durch den Zeitplan veröffentlicht. + +**Eine frei wählbare Startseite ist nicht umgesetzt.** Der Pfad `home` gilt an elf Stellen als Wurzel der Website – öffentliche Routen, Canonical, Sitemap, `llms.txt`, Menüauflösung. Das zu einer Einstellung zu machen ist ein eigener Schritt; bis dahin behält die Startseite ihre Adresse, statt dass die Hälfte des Systems der Änderung folgt und die andere nicht. diff --git a/docs/AUSBAUPLAN.md b/docs/AUSBAUPLAN.md index 50240fb..b0c3c0c 100644 --- a/docs/AUSBAUPLAN.md +++ b/docs/AUSBAUPLAN.md @@ -104,10 +104,12 @@ SEO-Felder je Seite: Titel, Beschreibung, Indexierungssteuerung, Open-Graph-Bild Abnahme: Sitemap und robots.txt stimmen mit dem veröffentlichten Bestand überein. Eine Adressänderung erzeugt eine Weiterleitung. Strukturierte Daten decken sich mit sichtbarem Inhalt und bestehen die Prüfung mit dem Testwerkzeug. -### F – Seiten verwalten +### F – Seiten verwalten — **erledigt am 12.09.2026, ohne freie Startseite** *Braucht: A und E für Menüs und Weiterleitungen.* +Umgesetzt: Umbenennen und Verschieben mit Aktualisierung aller Kindpfade und automatischer Weiterleitung, geplante Veröffentlichung, Fassungsverlauf mit Wiederherstellen und ein Papierkorb statt sofortigem Löschen. **Offen geblieben:** eine frei wählbare Startseite – der Pfad `home` gilt an elf Stellen als Wurzel, das ist ein eigener Schritt (ADR 022). Ebenso kein automatisches Leeren des Papierkorbs und kein Vergleich zweier Fassungen. + Seiten umbenennen und verschieben, mit Aktualisierung aller Kindpfade in einer Transaktion und automatischer Weiterleitung von der alten Adresse. Startseite frei wählbar. Geplantes Veröffentlichen. Revisionsliste mit Vergleich und Wiederherstellung in der Oberfläche. Papierkorb statt sofortigem Löschen. Abnahme: Eine Seite mit Unterseiten wird verschoben; alle alten Adressen leiten weiter, Menüs bleiben intakt. diff --git a/docs/QUALITY.md b/docs/QUALITY.md index 53b6f54..ebd5b99 100644 --- a/docs/QUALITY.md +++ b/docs/QUALITY.md @@ -1,5 +1,16 @@ # Qualitätsbericht – erster ausführbarer Prototyp +## Ergänzung: Seiten verwalten, 12.09.2026 + +- **160 PHP-Tests / 622 Assertions** unter PHP 8.2.12 und PHP 8.4.22, **15 JavaScript-Modelltests** und der vollständige Playwright-Editortest mit axe (null Verstöße). +- Verschieben geprüft: eine Seite mit Kind und Enkel wird umbenannt, alle drei Adressen ändern sich, jede neue antwortet mit 200, jede alte mit 301 auf genau ihr neues Ziel, und Slug sowie Elternbeziehung der Nachkommen bleiben unverändert. +- Vier abgewiesene Verschiebungen bewegen nichts und erzeugen keine Weiterleitung: unter die eigene Unterseite, in sich selbst, auf eine vergebene Adresse und mit einem unbrauchbaren Slug. Die Startseite wird ebenfalls abgewiesen. +- Papierkorb geprüft: der Teilbaum geht mit, die Seiten antworten öffentlich mit 404 und verschwinden aus Navigation, Sitemap, Beitragsarchiv, Feed und Editorliste. Ein Kind lässt sich nicht vor seinem Elternteil wiederherstellen, und das Wiederherstellen einer Seite holt keine andere zurück. +- Fassungen geprüft: Reihenfolge neueste zuerst, Wiederherstellen erzeugt eine zusätzliche Fassung statt zu überschreiben, eine Fassung einer anderen Seite wird abgewiesen. +- Zeitplan geprüft: ein künftiger Zeitpunkt veröffentlicht nichts, ein vergangener genau einmal, danach ist der Zeitpunkt verbraucht, ein ungültiges Format wird abgewiesen, und eine Seite im Papierkorb wird nie durch den Zeitplan veröffentlicht. +- **Ein echter Fehler kam dabei heraus:** die Reihenfolge des Fassungsverlaufs war nicht eindeutig. `created_at` hat Sekundengenauigkeit, mehrere Speichervorgänge fallen in dieselbe Sekunde, und die Liste richtete sich dann nach der zufälligen Revisions-ID – „vorherige Fassung wiederherstellen“ hätte die falsche treffen können. Jede Fassung trägt jetzt einen je Seite streng aufsteigenden Zähler; die Migration füllt ihn aus den vorhandenen Zeitstempeln und wurde vorher gegen eine Kopie der Datenbank geprüft. +- Migration `20260912000900` erst gegen eine Kopie ausgeführt und der Backfill kontrolliert (drei Fassungen von `/grundlagen` erhielten seq 1 bis 3 in der richtigen Reihenfolge), danach nach Backup angewendet. `cms:publish-due` ausgeführt. + ## Ergänzung: Browsertests wiederhergestellt, 12.09.2026 - **155 PHP-Tests / 579 Assertions** unter PHP 8.2.12 und PHP 8.4.22, **15 JavaScript-Modelltests** und erstmals seit dem Editorumbau wieder der **vollständige Playwright-Editortest**. diff --git a/docs/STATUS.md b/docs/STATUS.md index 18994ce..b406fb9 100644 --- a/docs/STATUS.md +++ b/docs/STATUS.md @@ -1,6 +1,6 @@ # Projektstatus -Stand: 12.09.2026. Arbeitstitel Kant CMS. **Browsertests wiederhergestellt und erweitert; dabei drei echte Fehler gefunden und behoben, darunter die in der Vorschau wirkungslose Gestaltung. Direktbearbeitung in der Vorschau, Kontrastwarnung, Gestaltungsvorlagen und Bildauswahl für Hintergrundbilder. Der Editor gestaltet frei statt über feste Auswahllisten: eigene Farbpalette, Schriften, und jede Eigenschaft je Block, je Gerät und für den Hover-Zustand frei einstellbar (Dokumentschema v10). SEO-Grundlage mit Sitemap, robots.txt, Weiterleitungen und strukturierten Daten (Arbeitspaket E). Formulare mit serverseitiger Validierung, Einwilligung, E-Mail-Versand und Spamschutz (Arbeitspaket D). Tabs als fortschreitende Verbesserung mit JavaScript (Dokumentschema v7). Zehn neue Blöcke für die Abschnitte der Pilotstartseite (Arbeitspaket C). Beiträge mit Datum, Autor, Auszug, Beitragsbild, Kategorien, Schlagwörtern, Archiven, einbettbarer Beitragsliste und RSS (Arbeitspaket B). Menüs mit Seiten-, Anker- und externen Zielen ersetzen die fest verdrahtete Navigation (Arbeitspaket A). Seiten und Beiträge lassen sich im Browser anlegen und löschen, mit Adresshierarchie bis vier Ebenen.** Die vollständige Phase-1-Abnahme bleibt wegen des fehlenden UX-Builder-Referenzabgleichs offen. +Stand: 12.09.2026. Arbeitstitel Kant CMS. **Seiten verschieben mit automatischen Weiterleitungen, Papierkorb, Fassungsverlauf und geplante Veröffentlichung (Arbeitspaket F). Browsertests wiederhergestellt und erweitert; dabei drei echte Fehler gefunden und behoben, darunter die in der Vorschau wirkungslose Gestaltung. Direktbearbeitung in der Vorschau, Kontrastwarnung, Gestaltungsvorlagen und Bildauswahl für Hintergrundbilder. Der Editor gestaltet frei statt über feste Auswahllisten: eigene Farbpalette, Schriften, und jede Eigenschaft je Block, je Gerät und für den Hover-Zustand frei einstellbar (Dokumentschema v10). SEO-Grundlage mit Sitemap, robots.txt, Weiterleitungen und strukturierten Daten (Arbeitspaket E). Formulare mit serverseitiger Validierung, Einwilligung, E-Mail-Versand und Spamschutz (Arbeitspaket D). Tabs als fortschreitende Verbesserung mit JavaScript (Dokumentschema v7). Zehn neue Blöcke für die Abschnitte der Pilotstartseite (Arbeitspaket C). Beiträge mit Datum, Autor, Auszug, Beitragsbild, Kategorien, Schlagwörtern, Archiven, einbettbarer Beitragsliste und RSS (Arbeitspaket B). Menüs mit Seiten-, Anker- und externen Zielen ersetzen die fest verdrahtete Navigation (Arbeitspaket A). Seiten und Beiträge lassen sich im Browser anlegen und löschen, mit Adresshierarchie bis vier Ebenen.** Die vollständige Phase-1-Abnahme bleibt wegen des fehlenden UX-Builder-Referenzabgleichs offen. Erster Commit angelegt (`82b0782`); das Projekt hat damit einen Rückfallpunkt. Noch kein Remote und kein Push. @@ -25,6 +25,11 @@ Erster Commit angelegt (`82b0782`); das Projekt hat damit einen Rückfallpunkt. - Auf ausdrücklichen Nutzerwunsch lokalen Administrator `umut` mit zufälligem Passwort angelegt und im In-App-Browser erfolgreich angemeldet. Das Passwort steht in keiner Projektdatei; nur der Hash in der lokalen Datenbank. Keine Browser-Passwortspeicherung ausgelöst. - Seitenerstellung und -löschung im Browser: `POST /admin/pages` (ab `ROLE_EDITOR`) legt Seite oder Beitrag mit gültigem Startdokument als privaten Entwurf an; `POST /admin/pages/{id}/delete` (nur `ROLE_ADMIN`) entfernt eine Seite mit allen Revisionen. Beide mit CSRF-Token, Fehlermeldungen als Flash und ohne JavaScript bedienbar. - Adresshierarchie: neue Spalten `path`, `parent_id`, `type`, `created_at` über Migration `20260912000400`; websiteweit eindeutiger Pfad statt eindeutigem Slug, Tiefe bis vier Ebenen, Beiträge ohne Unterseiten, reservierte Adressen und Adressableitung aus dem Titel. Öffentliche Route bedient verschachtelte Pfade wie `/leistungen/webdesign`; ein Lookahead hält `/admin`, `/media` und `/assets` aus dem Auffangmuster. +- **Seiten umbenennen und verschieben** zieht den ganzen Teilbaum mit und hinterlässt für jede alte Adresse eine dauerhafte Weiterleitung. Kollisionen, Tiefengrenze und das Verschieben unter eigene Unterseiten werden vorher abgewiesen; alles in einer Transaktion. +- **Papierkorb statt sofortigem Löschen** für die Editorrolle: alle Fassungen bleiben, öffentlich 404, verschwunden aus Navigation, Archiven, Feed, Sitemap, `llms.txt` und Editorliste. Unterseiten gehen mit; beim Wiederherstellen muss das Elternteil zuerst zurück. Endgültiges Löschen bleibt der Administratorrolle. +- **Fassungsverlauf je Seite** unter `/admin/pages/{id}/revisions` mit Wiederherstellen. Das überschreibt nichts: die alte Fassung wird als neuer Entwurf gespeichert. +- **`revision.seq` behebt einen echten Fehler:** die Reihenfolge des Verlaufs war nicht eindeutig, weil `created_at` nur Sekunden kennt und mehrere Speichervorgänge in dieselbe Sekunde fallen. Jede Fassung hat jetzt einen je Seite aufsteigenden Zähler. +- **Geplante Veröffentlichung** über `publish_at` und den Cron-Befehl `cms:publish-due`. Ohne Cron bleibt die Seite ein Entwurf. Zeiten in UTC; Seiten im Papierkorb werden nie durch den Zeitplan veröffentlicht. - **Die erzeugten Stile wirkten in der Editorvorschau nicht.** Ein `srcdoc`-Iframe erbt die Richtlinie der Verwaltung, die Inline-Styles verbietet; das Stylesheet wurde stillschweigend verworfen. Die Vorschau liefert es jetzt als inertes `