Wiederkehrende Aufgaben ohne Cronjob (ADR 073): Sicherung, Statistik kuerzen, geplante Veroeffentlichung, Updatepruefung und Aufraeumen laufen als Plan in der Tabelle scheduled_task - eine Zeile je Aufgabe mit Faelligkeit, Sperre und letztem Ergebnis. Auf geteiltem Hosting gibt es oft keinen Cronjob; der Plan haengt stattdessen an Anfragen und an einem Takt aus dem geoeffneten Backend. Diese Arbeit entstand in einer parallel laufenden Sitzung und lag bis hierher unveroeffentlicht im Arbeitsverzeichnis: die Releases 1.5.0 bis 1.5.5 wurden bewusst aus dem jeweiligen Tag gebaut und enthielten sie deshalb nicht. Vor der Veroeffentlichung geprueft, nicht bloss eingepackt: - phpunit 452/452, npm test 28/28, npm run test:editor vollstaendig. - Die Migration 20260914000100 legt nur die Tabelle scheduled_task an und nimmt sie beim Zurueckrollen wieder weg. Sie fasst keine bestehenden Daten an. - Seit einer Stunde keine Schreibzugriffe der anderen Sitzung mehr. Dabei zwei Fehler behoben: - config/services.yaml verwendet !tagged_iterator. Das ist gueltiges Symfony, aber "lint:yaml config" kennt eigene Tags nur mit --parse-tags und gab 1 zurueck - der CI-Lauf waere daran gescheitert. Flag ergaenzt. - AdminHeadersSubscriber.php (aus 1.5.3) entsprach nicht dem Stil von php-cs-fixer; composer lint gab 8 zurueck. Behoben. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
107 KiB
Projektstatus
Stand: 14.09.2026, Fassung 1.6.0. Das System heißt KantSites (vorher Arbeitstitel „Kant CMS“). Startseite von 18 auf 8 Datenbankabfragen, gemessenes Ladebudget, alle langen Listen blättern. Sicherung und Wiederherstellung, letztere nachgewiesen. Globale Seitenbreite wirkt jetzt auch nach oben, vier Menüplätze mit drei Menüarten, und jede Seite wählt ihren Kopf- und Fußbereich – auch „keiner“. 93 Gestaltungseigenschaften, neun Einblendungen, sieben Hover-Effekte, Symbol, Fortschritt und ein Slider ohne JavaScript. Update über Git mit Vorprüfung, Sicherung und Rückweg. Statistik ohne Wiedererkennung und damit ohne Einwilligung. Einwilligung mit tatsächlich zurückgehaltenen Einbettungen, Datenschutz und Impressum als Pflichtseiten. Benutzerverwaltung in der Oberfläche, Mediathek als eigene Seite mit Blättern. Die Verwaltung hat einen gemeinsamen Rahmen mit einer Navigation, die zeigt, wo man ist. Eigenes CSS je Element für alle, eigenes Skript nur für Administratoren und nur als eigene Datei. Formatierter Text mit Werkzeugleiste und HTML-Ansicht, serverseitig gegen eine Positivliste gefiltert. Kopf- und Fußbereich sind frei gestaltbare Blockdokumente, dazu verknüpfte wiederverwendbare Sektionen (Arbeitspaket G, Teil 1). Frei wählbare Startseite; /sitemap.xml und Geschwister waren lokal nie erreichbar und sind es jetzt. 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.
Für den Livegang vorbereitet (13.09.2026): docs/LIVEGANG.md trennt, was geprüft ist, von dem, was nur auf dem echten Hosting geprüft werden kann, und nennt die Reihenfolge am Umzugstag. 14 Weiterleitungen von den alten WordPress-Adressen auf die neuen sind angelegt und beantwortet (/productsolutions → /projekte, die gekürzten Beitragsadressen, /category/allgemein); ohne sie hätte jede bei Google stehende Adresse nach dem Umzug mit 404 geantwortet. In der Datenschutzerklärung wurde der Abschnitt über Google Fonts an die Wirklichkeit angepasst (die Schriften kommen jetzt vom eigenen Server) und die Abschnitte über Google Maps und reCAPTCHA entfernt, weil beides nicht eingebunden ist – eine Erklärung, die Datenflüsse beschreibt, die es nicht gibt, ist so falsch wie eine, die welche verschweigt. Nicht geprüft und nicht prüfbar von hier: der E-Mail-Versand.
Verwaltung und Updateweg (1.4.3, 13.09.2026): Die Farbfelder im Designbereich haben einen Farbwähler samt Palettenauswahl; das Textfeld bleibt maßgeblich, damit @akzent ein Verweis bleibt und nicht beim ersten Klick zu einem festen Hexwert erstarrt (ADR 066). Die Verwaltung ist auf dem Telefon bedienbar: das Menü ist unter 760 px ein zugeklapptes <details>, das den aktuellen Bereich nennt, mit 44 px hohen Zielen – ohne Skript. Die zwölf Navigationssymbole sind eigene SVG-Pfade statt Unicode-Zeichen, die ein Telefon zu bunten Emoji macht (ADR 067). Und es gibt ein Update ohne Git: ArchiveUpdate lädt das Releasearchiv, vergleicht die Prüfsumme, sichert, ersetzt die Dateien und führt die Migrationen in einer zweiten Anfrage aus – der einzige Updateweg auf geteiltem Hosting (ADR 068, docs/UPDATE.md).
Nachbau nachgezogen (13.09.2026): Der Fußbereich ist jetzt vierspaltig wie im Original – Zeichen und Satz, „Leistungen", „Unternehmen", „Kontakt", darunter die Zeile mit Copyright. Die beiden Linklisten sind Menüs, keine Dokumentinhalte: sie lassen sich in der Verwaltung pflegen. Alle 29 Beiträge haben jetzt Text – die elf fehlenden sind neu geschrieben, denn auch im Original stehen dort nur Bildergalerien ohne Beschreibung. Die Karte „Bereit für den nächsten Schritt" hat 34 px Abstand zu den Kontaktzeilen darunter. Nicht erledigt: das TeamViewer-Logo im Fernwartungsbereich – dazu unten.
Vom Original übernommen (13.09.2026): die Meta-Beschreibung der Startseite im Wortlaut von kant-it.de, das Symbol der Website (dort 192 × 192) und die Bot-Regeln der robots.txt. Titel, Canonical, Open Graph, strukturierte Daten (LocalBusiness statt Yoasts ProfessionalService), Sitemap, robots.txt und llms.txt entstehen in KantSites aus dem Inhalt und stimmen mit dem Original überein. Beim Abgleich aufgefallen: das og:image des echten kant-it.de zeigt auf wp-content/uploads/kant-it-share.png – die Datei antwortet mit 404, beim Teilen erscheint dort also kein Bild.
Im Nachbau von kant-it.de gefunden und behoben: vier tote Verweise im Hero-Slider – die Adressen waren im Aufbauskript von Hand geschrieben, die Beiträge bekamen längere. Sie werden jetzt aus derselben Regel abgeleitet, mit der die Beiträge entstehen, und ein zweiter Lauf des Skripts aktualisiert vorhandene Seiten, statt sie zu überspringen. Eine Prüfung aller internen Verweise auf 404 gibt es im CMS noch nicht; gefunden wurden sie mit einem einmaligen Durchlauf im Browser.
Wiederkehrende Aufgaben ohne Cronjob (14.09.2026, ADR 073): Bis hierher stand in vier Dokumenten „gehört in einen Cronjob“ – auf geteiltem Hosting oft nicht einlösbar, und die Folge war nicht „läuft seltener“, sondern läuft nie. Jetzt gibt es einen Aufgabenplan mit vier Auslösern: jeder Seitenaufruf (nach der ausgelieferten Antwort, in kernel.terminate), das geöffnete Backend (im Minutentakt, nur solange der Reiter sichtbar ist), ein URL-Cronjob unter /cms-cron/<Schlüssel> für Hoster, die nur eine Adresse aufrufen können, und die Konsole (cms:tasks:run, auch als Dauerläufer mit --loop). Fünf Aufgaben sind angeschlossen: geplante Veröffentlichung (5 Minuten), Statistik verdichten, Sicherung, Updateprüfung und Aufräumen (je täglich). Der Bildschirm Aufgaben zeigt Plan, letztes Ergebnis, Dauer, Auslöser und Protokoll, schaltet jede Aufgabe einzeln und ändert ihren Abstand. Die Einschränkung steht auf diesem Bildschirm: ohne Besuch und ohne geöffnetes Backend passiert nichts – eine Aufgabe für 03:00 Uhr läuft auf einer stillen Website erst um 07:15 Uhr.
Erledigt
AGENTS.mdundENTWICKLUNGSPLAN.mdunverändert aus Downloads im Projektstamm abgelegt; SHA-256 jeweils mit dem Original identisch.- Leeres lokales Git-Projekt auf
maininitialisiert; Quellen und Lockfiles für den ersten Commit vorgemerkt. Noch kein Commit und kein Remote/Push. - Nutzerentscheidung „herkömmliches PHP-Webhosting“ übernommen. Symfony 7.4 / Twig / Doctrine gewählt; kein Python-/Django-Anwendungscode.
- Start-/Build-/Prüfkommandos und Hostingvoraussetzungen in README, begründete Architektur und Datenmodell in ARCHITECTURE, Plananalyse/offene Anforderungen in REQUIREMENTS.
- Erstes Datenbankschema, versionierte Blockdokumente, unveränderliche Revisionen über den Repository-Vertrag, getrennte Entwürfe/Veröffentlichungen und optimistische Schreibkonflikterkennung.
- Drei gespeicherte öffentliche HTML-Demoseiten, lokale CSS-Ausgabe ohne JavaScript, Metadaten/Canonical, 404, Sprunglink und sichtbarer Fokus.
/homeleitet auf/um. - Idempotentes Demo-Seeding und lokaler CLI-Import validierter Entwürfe. Keine ungeschützte HTTP-Schreibschnittstelle.
- Symfony-Security-Anmeldung, CSRF, Login-Throttling, geschützte Sessions und Rollen Editor/Publisher/Admin. Neue Migration ergänzt ausschließlich die Benutzertabelle. Passwort-Rehash hält Sessionbenutzer und Datenbank synchron; Rollenänderungen invalidieren bestehende Sitzungen.
- Seitenübersicht und visueller Editor: Elementbibliothek, Strukturbaum, gemeinsame serverseitige Live-Vorschau, Eigenschaften, Drag-and-drop und Schaltflächen zum Verschieben/Verschachteln, Duplizieren/Löschen, lokales Undo/Redo.
- Inhalt-/Designbereiche mit typabhängigen Abständen, Ausrichtung, Schriftgröße/-stärke, Zeilenhöhe, Spaltenaufteilung/-abstand und Ecken. Gerätewerte Desktop → Tablet → Mobil samt Vererbung, Einzel-/Gerätereset und Undo. Echte Vorschau-Layoutbreiten 1240/768/390 px mit visueller Skalierung.
- Drei bearbeitbare Abschnittsvorlagen (Intro, zwei Leistungen, Kontaktaufruf), einklappbarer Werkzeugkasten, Struktursuche mit Vorfahren, einklappbare Teilbäume, Pfadnavigation und dauerhaft erreichbare Blockaktionen.
- Dokumentformat v2, erhaltender idempotenter v1-Konverter, gemeinsame Tokenliste für Validator/Editor/CSS und dokumentierter Migrations-/Rückweg. Bestehende v1-Seiten werden beim Öffnen nicht geändert; keine neue DB-Migration erforderlich.
- Dokumentformat v3 für Galerie, Bildbeschriftungen/-varianten und Effekte, mit erhaltendem Upgrade ohne Downgrade und Undo. v1/v2 bleiben unterstützt. Schatten, Hover-Anheben/Bildzoom und Einblenden beim Laden ohne öffentliches JavaScript; Bewegungsreduktion wird berücksichtigt.
- Medienverwaltung im Editor und unter
/admin/media: JPEG/PNG/WebP-Upload, echte Bildvalidierung, Größen-/Pixel-/Speichergrenzen, WebP-Varianten bis 480/960/1600/2400 px (ohne Hochskalieren, Hintergrundbilder geräteabhängig), Suche, Titel/Alt-Text, Archivieren/Wiederherstellen und Schreibkonflikterkennung. Bildauswahl für einzelne Bilder und Galerien, kontextbezogene Alt-Texte/dekorative Verwendung und Beschriftungen. - Neue Medientabelle durch Migration
20260912000300lokal angewendet. Vorher konsistentes SQLite-Backup untervar/backups/before-media-20260912-051258.db. Bestehende Seiten/Benutzer nicht geändert. Lokal läuft PHP 8.4 mit 8M Upload-, 10M POST- und 256M Speicherlimit; originale Uploads werden nicht gespeichert. - Speichern/Laden von Entwürfen, separates Veröffentlichen mit Bestätigung, sichtbare Speicher-/Fehler-/Konfliktzustände, JSON-Sicherung und bewusster Server-Neuladedialog. Keine Tests schreiben in den echten Seitenbestand.
- Auf ausdrücklichen Nutzerwunsch lokalen Administrator
umutmit 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(abROLE_EDITOR) legt Seite oder Beitrag mit gültigem Startdokument als privaten Entwurf an;POST /admin/pages/{id}/delete(nurROLE_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 Migration20260912000400; 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,/mediaund/assetsaus dem Auffangmuster. - Ladeverhalten gemessen statt vermutet (ADR 039, Arbeitspaket M): ein Zähler liefert die Datenbankabfragen je Anfrage im Kopf
X-Kant-Queries(außerhalb der Produktion). Die erste Messung zeigte 18 Abfragen auf der Startseite, weil Einstellungen und Theme je Aufrufer erneut gelesen wurden; beide merken sich ihren Stand jetzt für die Dauer einer Anfrage – 18 → 8. Die Browserprüfung misst je Seite HTML, CSS, Anfragen, Skripte und Abfragen gegen Obergrenzen: Startseite 7,4 KB HTML, 22,7 KB CSS, 2 Anfragen, 0 Skripte. - Aufgabenplan ohne Cronjob (ADR 073, Migration
20260914000100): eine Zeile je Aufgabe mit Fälligkeit, Sperre und letztem Ergebnis. Wer sie ausführen darf, entscheidet ein bedingtesUPDATE– auf MySQL wie auf SQLite atomar, damit zwei gleichzeitige Besucher eine Aufgabe nicht zweimal starten; die Sperre verfällt nach 15 Minuten von selbst. Der Normalfall kostet keine Datenbankabfrage: invar/scheduler.jsonsteht, wann überhaupt das nächste Mal etwas ansteht. Ein Fehlschlag ist ein sichtbarer Zustand mit wachsendem Abstand bis zum nächsten Versuch (Verdopplung, gedeckelt bei sechs Stunden), und eine scheiternde Aufgabe reißt nie eine Seite mit. Neue Aufgaben sind eine Datei: werApp\Ops\Tasks\Taskimplementiert, steht beim nächsten Abgleich im Plan. Befehle:cms:tasks,cms:tasks:run(--task,--limit,--budget,--loop,--max-time). - Das erste Bild einer Seite lädt sofort, alles darunter weiterhin verzögert. Es ist meistens das größte Ding über der Falz.
- Slider mit Punkten ohne Skript (ADR 044):
::scroll-markerzeichnet je Folie einen Punkt, der aktive ist erkennbar, die Rollleiste entfällt; ältere Browser behalten das wischbare Schiebefeld. Beitragskarten sind echte Karten (Bild 16:10, 18 px Ecken). Überschriften dürfen ein Wort hervorheben (<mark>, in Akzentfarbe). Behoben: die Theme-Schriftskala überschrieb die Größen in Karten und Aufruf-Bändern. - „Weiterlesen" ist jetzt ein Link. In der wechselseitigen Beitragsliste stand dort eine reine Beschriftung: sie sah aus wie ein Knopf und war keiner. Der Titel steckt für Vorlesesoftware im Linktext, damit nicht zwölf Links gleich heißen.
- Behoben in 1.4.5: die Verwaltung zeigte Git-Fehler auf einer Installation ohne Git (ADR 072).
gitinstalliert und „Arbeitskopie" waren derselbe Schalter; auf Umuts Hosting ist Git vorhanden, die Installation kam aber aus dem Releasearchiv. Dort standfatal: Not a git repositoryan der Stelle der Fassungsnummer und eine rote Vorprüfung für Bedingungen, die gar nicht gelten. Ohne Arbeitskopie verschwinden die Git-Karten jetzt. - Behoben in 1.4.5: „Sicherung anlegen" endete mit 500 (ADR 072).
VACUUM INTOgibt es erst seit SQLite 3.27; Umuts Hosting hat eine ältere Fassung. Bei älteren wird unter einer Schreibsperre kopiert, und der Sicherungsknopf fängt jeden Fehler ab, statt mit einer 500-Seite zu enden. - Das Update schreibt mit (1.4.6, ADR 071): Jeder Schritt des Archiv-Updaters geht sofort nach
var/updates.log– während des Laufs, nicht am Ende. Auf Umuts Hosting war die Ergebnisseite tot, und damit war auch verloren, was der Updater berichtet hatte; genau so wurde der nächste Fehlschlag dann doch noch lesbar („binließ sich nicht beiseitelegen – der alte Stand wurde zurückgeschoben"). Die Ergebnisseite versteht zusätzlich die Schreibweise bis 1.4.4: Beim Update schreibt der alte Code die Schritte und der neue zeigt sie an. - Behoben in 1.4.5: der Updateknopf endete mit 500 (ADR 071). Auf Umuts Hosting gefunden, mit einer echten 1.4.3-Installation nachgestellt: Das Update lief vollständig durch – alle neun Schritte, Dateien getauscht,
var/und.envunberührt –, aber die Ergebnisseite starb daran. Der Updater über Git nennt einen Schrittstep, der über das Archiv nannte ihnlabel, beide legen ihn unter demselben Sitzungsschlüssel ab, und die Vorlage lieststep. Damit war auch der zweite Knopf unerreichbar und die Website blieb im Wartungszustand. Zehn Tests prüften das Rückgabe-Array, keiner die Seite, die es anzeigt; der neue Test rendert sie und wurde gegengeprüft, indem der Fehler noch einmal eingebaut wurde. - Migrationen im Browser ausführen (1.4.4, ADR 070): Wer ein Update von Hand über FTP einspielt, hat danach neuen Code und eine alte Datenbank. Der Bildschirm „System" zeigt jetzt eine Karte, sobald etwas aussteht, nennt die Namen und führt sie auf Knopfdruck aus – vorher ging das nur über eine Konsole, also genau das, was auf geteiltem Hosting fehlt. Der Weg von Hand steht Schritt für Schritt in
docs/UPDATE.md. - Ein Fehlschlag beim Laden sagt, woran es lag (1.4.4, ADR 070): „Die Releaseseite antwortet nicht" war drei Ursachen in einem Satz. Beim Gegenprüfen des Releases fiel auf, dass diesem PHP der Zertifikatsspeicher fehlt – auf einem Webhosting der häufigste Grund und der einzige, den man in einer Minute behebt. Die Meldung nennt ihn jetzt, und bei genau diesem Fehler wird der zweite Weg über den Dateizeiger versucht. Abgeschaltet wird die Prüfung nicht.
- Das Hintergrundbild des ersten Abschnitts wird vorab geholt (1.4.3, ADR 069): Auf der Startseite des Piloten ist das größte Element über der Falz kein
<img>, sondern der Hintergrund des Heros – und der steht nur im Stylesheet, das der Browser erst nach dem HTML holt. Drei Vorabverweise mit denselben Gerätegrenzen wie der Compiler holen genau die Variante, die die Regel auch nimmt; einimagesrcsethätte bei manchen Fensterbreiten ein zweites Bild geladen. Gemessen: LCP 1,3 s → 1,1 s, Leistung Desktop 96 → 97, Mobil 98 → 99 (auf dem Entwicklungsserver ohne Kompression). Dabei gefunden: dasselbe Bild wurde je Geräteregel neu abgefragt – die Mediathek merkt sich jetzt, wonach sie in dieser Anfrage schon gefragt wurde, Startseite 19 → 15 Abfragen. - Update aus dem Releasearchiv (1.4.3, ADR 068): der Weg für Installationen ohne Git – bisher stand dort nur „Dieses Verzeichnis ist keine Git-Arbeitskopie". Zwei Knöpfe im Bildschirm „System": der erste lädt, prüft die Prüfsumme, sichert und ersetzt die Dateien, der zweite führt die Migrationen aus – in einer eigenen Anfrage, weil ein Prozess, der sich selbst ausgetauscht hat, mit dem alten Code weiterarbeitet.
var/,.env, hochgeladene Schriften und das erzeugte Stylesheet-Bündel bleiben unberührt,public/wird dazu Datei für Datei zusammengeführt. Der alte Stand liegt danach untervar/update/. Geprüft mit acht Tests gegen eine Releaseseite im Speicher (tests/ArchiveUpdateTest.php), einer davon hält die Ersetzungsliste gegen die Mitnahmeliste des Releaseskripts. - Eigene Symbole in der Verwaltung (1.4.3, ADR 067): zwölf SVG-Pfade statt Unicode-Zeichen, die auf dem Telefon als Emoji in fremdem Stil erscheinen. Ein Test findet Emoji-Zeichen in der Navigationsvorlage und zwei Einträge mit demselben Pfad – beides war vorhanden.
- Verwaltung auf dem Telefon (1.4.3, ADR 067): das Menü klappt unter 760 px zu und nennt in der Zusammenfassung den Bereich, in dem man steht; Ziele 44 px hoch, kein Skript, kein seitliches Scrollen. Geprüft bei 390 px.
- Farbwähler im Designbereich (1.4.3, ADR 066): Farbfeld und Palettenauswahl neben dem Textfeld, das maßgeblich bleibt – ein
<input type="color">allein hätte@akzentin einen festen Wert verwandelt und die Verbindung zur Palette gekappt. Ohne JavaScript bleibt das Textfeld. - Behoben in 1.4.2: die Systemprüfung nennt jetzt die SQLite-Fassung. Auf dem Hosting scheiterte die Installation im dritten Schritt mit „near "(": syntax error" – dort läuft SQLite in einer Fassung vor 3.16, und Doctrine liest das Schema mit Tabellenfunktionen (
pragma_table_info(...)), die es erst seit 3.16 gibt. Die Prüfung nennt die gefundene Fassung vor der Wahl der Datenbank, und der Datenbankschritt bietet SQLite gar nicht erst an, wenn sie zu alt ist – mit dem Hinweis auf MySQL. Eine Voraussetzung, die erst mitten in der Installation auffällt, ist keine Prüfung wert. - Behoben in 1.4.1: dem Releasearchiv fehlten
migrations.phpundcli-config.php. Auf dem Hosting brach die Installation deshalb im dritten Schritt ab – „The configuration file does not exist", genau dort, wo die Tabellen entstehen sollten. Die beiden Dateien standen weder in der Mitnahme- noch in der Ausschlussliste des Releaseskripts, also fiel niemandem auf, dass sie fehlen. Ein Test ordnet jetzt jeden Eintrag im Projektstamm einer der beiden Listen zu; was neu dazukommt und unbedacht bleibt, lässt ihn fehlschlagen. Der Installationsweg wurde anschließend vollständig durchgespielt: 3504 Dateien entpackt, 14 Migrationen ausgeführt, 15 Tabellen angelegt. - Fassung 1.4.0 ist veröffentlicht und der Installationsweg einmal vollständig durchgespielt:
install.phpaus dem Release geladen (anonym, ohne Anmeldung), in ein leeres Verzeichnis gelegt, aufgerufen – es lädtkantsites-1.4.0.zip(4,7 MB, 3502 Dateien mitvendor/), prüft die SHA-256-Summe, entpackt und löscht sich selbst. Die frische Installation antwortet mit der Systemprüfung unter/install. Das Repository ist dafür öffentlich – ein privates liefert die Downloadadresse ohne Anmeldung nicht aus. - Die Verwaltung kann umziehen (ADR 065):
php bin/console cms:admin:path meine-verwaltunglegt sie auf eine andere Adresse – geprüft wird, dass der Name frei ist und keine Seite verdeckt. Der Wert steht in.env.localund gilt für Routen, Firewall, Schutzkopfzeilen und Statistik. In derrobots.txtwird ein geänderter Name nicht genannt; beim Standardnamenadminbleibt die Zeile, weil diese Adresse ohnehin bekannt ist. Die Systemseite zeigt die aktuelle Adresse an. - Dokumentschema v19: Baustein Rechtszeile (Impressum, Datenschutz, „Einwilligung ändern") für den Fußbereich – gestaltbar und an der richtigen Stelle statt als heller Streifen unter einem dunklen Fuß; die eingebaute Leiste erscheint nur noch, wenn die Seite den Baustein nicht hat. Dazu „Beim Klick" je Bild (nichts / vergrößern / Adresse) und „Bilder vergrößerbar" je Galerie.
- Bildbetrachter (ADR 063): ohne JavaScript ein Verweis auf die große Bildfassung, mit JavaScript ein Betrachter über der Seite –
<dialog>mit Blättern, Escape, Pfeiltasten und Zähler. Das Skript lädt nur auf Seiten, die ein vergrößerbares Bild enthalten. - Knopf „nach oben" (ADR 062), abschaltbar und vollständig einstellbar: Seite, Form, Größe, Farben, Schwelle, Beschriftung. Ein Anker ohne Skript; eingeblendet über eine Scroll-Zeitachse, wo der Browser sie kennt.
- Behoben: zwei Stylesheet-Regeln griffen seit drei Veröffentlichungen nicht mehr (ADR 064). Beim Entfernen alter Regeln waren Selektorreste stehen geblieben; CSS nimmt dann die nächste Regel mit. Betroffen waren der Einwilligungsdialog und die Rechtszeile – beide standen ungestaltet unter dem Fuß. Ein Test prüft jetzt jede CSS-Datei auf abgebrochene Selektoren.
- Menüs kosten nicht mehr je eine Abfrage. Jedes Menü holte die Seitenliste selbst; mit vier Menüs im Fuß waren das vier gleiche Abfragen je Seitenaufruf. Jetzt einmal je Anfrage gemerkt – Startseite des Nachbaus 21 → 18 Abfragen.
- Behoben: die Vorschau zeigte beim Aufbau bis zu 1440 ms lang eine Seite ohne Gestaltung (ADR 061). Die Stile wurden erst angewandt, wenn das letzte Bild geladen war. Jetzt sitzen sie, sobald das Dokument geparst ist, und der Rahmen bleibt bis dahin verdeckt. Die Farben selbst stimmten immer – Vorschau und veröffentlichte Seite geben nachweislich dieselben berechneten Werte.
- Cloudflare Turnstile für Formulare, optional (ADR 059): aus, bis in den Einstellungen beide Schlüssel stehen; dann erscheint die Prüfung in jedem Formular, und die Sicherheitsrichtlinie öffnet sich nur auf Seiten mit Formular für
challenges.cloudflare.com. Antwortet Cloudflare nicht, wird die Anfrage trotzdem angenommen. Die eingebauten drei Prüfungen ohne Dritte bleiben unabhängig davon bestehen. Der Preis steht in der Verwaltung dabei: das Widget braucht JavaScript. - Behoben: die Fehlerseite eines Formulars hatte keine Gestaltung (ADR 060). Wer ein Pflichtfeld vergaß, bekam eine nackte Seite – der Formularcode baute sie selbst auf und ließ Theme, Blockstile und Schriften weg. Jetzt rendert derselbe Controller wie sonst auch.
- Die Schriften liegen im Repository (
public/assets/fonts/, mitlato-OFL.txtundLIZENZ.mddaneben, wie die Open Font License es verlangt): auf einem frischen Server fehlt damit nichts, und es wird nie versehentlich von Google geladen. Andere Installationen holen ihre eigenen mitcms:fonts:fetch. docs/NEUE-WEBSITE.md: die Anleitung, mit der eine neue Kundenwebsite entsteht – Reihenfolge, Dokumentformat, Bilder, SEO, Prüfläufe, und am Ende die fünf Fehler, die in diesem Projekt immer wieder passiert sind.- Schutzkopfzeilen ergänzt und geprüft:
Permissions-Policy(Kamera, Mikrofon, Standort und Sensoren abgeschaltet) auf öffentlichen Seiten und in der Verwaltung;X-Powered-Bywird entfernt – der Server nannte bisher in jeder Antwort seine genaue PHP-Fassung. Die Browserprüfung misst die Kopfzeilen jetzt, statt sie anzunehmen. - Behoben: der Editor kannte Schema v17, der Validator v18. In einem aktuellen Dokument war die Beitragsnavigation nicht einfügbar. Ein Test liest die höchste Fassung jetzt aus dem Validator und vergleicht sie mit der des Editors – dieselbe Falle war schon bei v13 zugeschnappt.
- Getrennter Titel für Suchergebnisse (Migration
20260913000100): jede Seite und jeder Beitrag kann einen eigenen<title>tragen, unabhängig von der Überschrift auf der Seite. Leer heißt weiterhin: der Titel der Seite gilt. Er zählt auch fürog:title. - Schriften kommen vom eigenen Server (ADR 057): ein Knopf im Designbereich oder
php bin/console cms:fonts:fetchlädt die gewählten Google-Schnitte als WOFF2 nachpublic/assets/fontsund schreibt die@font-face-Regeln daneben. Damit lädt keine Seite mehr etwas von Google – das spart zwei fremde Verbindungen nacheinander (gemessen 820 ms) und die datenschutzrechtliche Angreifbarkeit gleich mit. Ohne Dateien bleibt es beim bisherigen Verweis. - Ein Stylesheet statt vier, und eine
.htaccess, die etwas tut (ADR 056): gebündelt, ohne Kommentare, mit Prüfsumme im Dateinamen; Kompression, ein Jahr Cache für Dateien mit Fassung oder Kennung in der Adresse, Schutz vor dem Ausliefern interner Dateien, HTTPS-Umleitung und HSTS vorbereitet und auskommentiert. Der Entwicklungsserver sendet dieselben Kopfzeilen, damit lokale Messungen etwas bedeuten. - Bilder in der Größe, in der sie erscheinen (ADR 058): ein Hintergrundbild, das nur für „Desktop“ gesetzt wurde, gilt auf dem Telefon in der Telefongröße (gemessen 189 KB → 40 KB);
<img>nennt seine feste Breite, wenn es eine hat, und sonstsizes="auto";fetchpriority="high"liegt auf dem ersten Bild des Inhalts statt auf dem Logo im Kopfbereich. - Lighthouse gegen den Nachbau von kant-it.de (
docs/quality/lighthouse-kantit.json, vier Adressen, je drei Läufe, Median): Barrierefreiheit, Best Practices, SEO und „agentic browsing“ 100 auf allen geprüften Seiten, mobil wie am Schreibtisch. Performance: Projekte-, Beitrags- und Impressumsseite 100/100, Startseite 98 mobil / 96 am Schreibtisch – dort liegen vier Sliderbilder und ein großflächiges Hintergrundbild über der Falz. Gemessen im Produktionsmodus mit denselben Kopfzeilen, diepublic/.htaccesssetzt; keine Felddaten und keine Messung auf echtem Hosting. - Verweise werden geprüft (
npm run test:links): folgt der Sitemap, sammelt jeden internen Verweis samt Bildern, Stylesheets und Hintergrundbildern und meldet jede Antwort ab 400. Im Nachbau 408 Adressen, kein toter Verweis. - Das Vorschaubild beim Teilen erscheint jetzt tatsächlich (ADR 056):
og:imagewird aus dem Feld der Seite aufgelöst, ersatzweise aus dem Beitragsbild – vorher speicherte das Feld und bewirkte nichts, keine KantSites-Seite hatte ein Teilen-Bild. Dazuog:site_name,og:locale,og:image:alt,twitter:card(klein ohne Bild, groß mit) und das Bild im strukturierten Datensatz eines Beitrags. Archive haben jetzt ebenfalls Open-Graph-Angaben. - Eigene robots.txt-Zeilen: 200 statt 30, Leerzeilen bleiben (ADR 056), damit sich Regeln je Bot in Blöcken schreiben lassen – etwa welche KI-Crawler lesen dürfen. Ein Sitemap-Index ist bewusst nicht gebaut: nötig wird er ab 50.000 Adressen.
- Beiträge sind optional (ADR 053): ohne einen einzigen veröffentlichten Beitrag antworten
/beitraegeund/feed.xmlmit 404, der Feedverweis fehlt im Kopf, die Sitemap kennt kein Archiv, und ein Beitragslisten-Baustein zeigt öffentlich nichts – der Hinweis „hier erscheinen Beiträge“ bleibt nur im Editor. Ein Kategorie- oder Schlagwortarchiv verschwindet ebenso, sobald es nichts mehr zu zeigen hat; der Begriff selbst bleibt. Dabei behoben: die Beitragsliste fragte je Beitrag einzeln nach Bild und Begriffen – jetzt je eine Abfrage für die ganze Liste, Startseite des Nachbaus 22 → 16 Abfragen. - Unterstriche nur noch im Fließtext (ADR 054), dort mit Schalter im Designbereich („Links im Fließtext unterstreichen“, Standard an). Karten, Menü, Kontaktzeilen, Rechtszeile und der „Ansehen“-Streifen über Kartenbildern haben keinen mehr; beim Überfahren erscheint er überall. Die aktuelle Seite im Menü wird über das Schriftgewicht kenntlich – die Akzentfarbe fiel im dunklen Fußmenü durch die Kontrastprüfung.
- Eigenes Symbol der Website (ADR 055): ein Bild aus der Mediathek in den Einstellungen wählen, beim Speichern entstehen daraus quadratische PNG-Dateien in 32 und 180 px (
/favicon.png,/apple-touch-icon.png). Ohne Auswahl bleibt es beim mitgelieferten Zeichen. - Die Browserprüfung prüft jetzt jede Installation, nicht nur die Demo: die geprüften Seiten kommen aus der Sitemap, Tastatur- und Ohne-JavaScript-Weg folgen dem ersten Verweis im Inhalt statt festen Linktexten der Demo. Das Ladebudget trennt Bilder (Inhalt) vom Beiwerk (≤ 12 Anfragen) und deckelt stattdessen das übertragene Gewicht (1,5 MB); Skripte werden gegen die Richtlinie der Seite gezählt, strukturierte Daten zählen nicht mit. Der Nachbau von kant-it.de besteht die vollständige Prüfung ohne Befund.
- Einblendungen laufen beim Scrollen (ADR 052):
animation-timeline: view()hinter Bewegungsschranke und@supports; auf dem Scrollweg nur Bewegung, keine Deckkraft, damit Text außerhalb des Blicks lesbar bleibt. Die drei Beitragsvorlagen bringen sie mit. - Vier Darstellungen der Beitragsliste (ADR 051): Karten, Liste, Kompakt und Wechselseitig – am Baustein einstellbar, dieselben Daten, nur andere Regeln.
- Drei Aufbauten für neue Beiträge (ADR 050): „Bildkopf“, „Klassisch“ und „Magazin“, wählbar beim Anlegen. Startpunkte, keine Schablonen – jeder Block bleibt änderbar.
- Dokumentschema v18 (ADR 049): Baustein „Beitragsnavigation“ – Vor/Zurück zwischen Beiträgen derselben Kategorie, vom Renderer gefüllt, ohne Umbruch am Rand. Dazu Autorotation für den Slider (Sekunden, eigenes Skript nur auf Seiten mit einem solchen Slider) und Blättern in der Beitragsliste.
- Dokumentschema v17 (ADR 043): eine Beitragsliste kann Datum, Auszug und Kategorie ausblenden und damit als Projektraster erscheinen; ein Bild kann verlinken (Logo auf die Startseite). Dazu die Einstellung „Kopfbereich beim Scrollen oben halten“. Behoben: im Editor ließ sich in Dokumenten ab v13 kein Baustein mehr einfügen.
- Die Gestaltung färbt jetzt auch die Bausteine (ADR 042): Symbole, Knöpfe, Rahmen, ruhiger Text, Fokusring und Einwilligungsdialog nehmen die Farben des Themes statt fester Werte aus
site.css. Der Einwilligungsdialog lässt sich zusätzlich eigens färben über eine Palettenfarbe mit dem Schlüsselconsent. Überschriften werden nicht mehr getrennt. - Installer (Arbeitspaket N, ADR 041):
/installführt in sechs Schritten von einer leeren Installation zu einer Website – Systemprüfung, Datenbank (SQLite oder MySQL, Verbindung sofort geprüft), Migration im Prozess ohne Konsole, Website mit Startseite, Kopf-/Fußbereich, Menü sowie Impressum und Datenschutzgerüst, erstes Administratorkonto, Abschlussliste. Solange keine Installation existiert, führt jede Adresse dorthin; sobald ein Konto existiert, antwortet/installmit 404. Davor liegtscripts/install.php: eine einzelne Datei, die das Releasearchiv mit fester Adresse und fester SHA-256 lädt, gegen ZIP-Slip gesichert entpackt und sich danach selbst löscht. - Fassungsverlauf, Einsendungen und Weiterleitungen blättern statt bei 30, 50 beziehungsweise 2000 Einträgen stillschweigend aufzuhören.
- Sicherung und Wiederherstellung (ADR 038, Arbeitspaket O):
cms:backupschreibt Datenbank (überVACUUM INTO, nicht als Dateikopie) und Medienordner nachvar/backups/, mit Manifest samt Prüfsumme, und rotiert die alten. In der Verwaltung gibt es die Übersicht mit Zustand je Sicherung und einen Knopf zum Anlegen. Zurückspielen nur auf der Konsole (cms:restore): Prüfsumme, Fassungsvergleich, Sicherheitskopie des aktuellen Stands, Wartungszustand, Datenbank ersetzen, Medien ergänzen statt ersetzen. Nicht enthalten:.env,vendor/, Quellcode – steht so im Manifest. - Seiten ohne Menü, andere Köpfe je Seite (ADR 037, Dokumentschema v16): jede Seite wählt Kopf- und Fußbereich – Standard, ein anderer Bereich oder keiner, für eine Landingpage ohne Navigation. Dafür sind mehrere Kopf- und Fußbereiche erlaubt, jeder mit eigener Adresse; der erste bleibt
default. Ein Verweis auf einen gelöschten Bereich fällt auf den Standard zurück statt die Navigation stillschweigend zu verlieren. - Vier Menüplätze statt zwei (Hauptnavigation, zweite Navigation, Fußzeile, Rechtliches) und drei Menüarten: waagerecht, untereinander, aufklappbar als Burger. Der Burger ist ein
details-Element und öffnet ohne JavaScript. - Die globale Seitenbreite wirkt jetzt auch nach oben.
containerWidthim Designbereich gab es, abersite.cssdeckelte bei 1180 px – schmaler ging, breiter nicht. Dritter Fehler dieser Art an einem Tag: eine Einstellung, die sich speichern ließ und nichts tat. - Vollflächige Bilder und volle Bildschirmhöhe: Abschnitte können randlos sein (ohne Begrenzung, ohne Rand, von Kante zu Kante) oder breit (1600 px). Für die Höhe gibt es jetzt die Einheiten dvh, svh, lvh –
100vhist auf dem Handy höher als der Bildschirm,100dvhist das, was gemeint ist. Die Vorlage „Vollbild-Hero“ setzt die ganze Kombination auf einen Klick: randlos, 100dvh (mobil 80dvh), Inhalt senkrecht zentriert, Hintergrundbild formatfüllend, dunkle Überlagerung, Überschrift, Text, Button. - Designer ausgebaut (ADR 036, Dokumentschema v15): 93 Gestaltungseigenschaften statt 50, je Gerät und teils für Hover. Neu: Bild-Eigenschaften, die auf das Bild im Block wirken (Ausschnitt, Bildmitte, Seitenverhältnis, Höhe, Ecken, Rahmen), Positionierung mit z-index, getrennte Zeilen- und Spaltenabstände, Flex-Eigenschaften, Innen- und Textschatten, acht Bildfilter, Mischmodus, Zusatzrahmen. Neun Einblendungen mit Verzögerung, sieben Hover-Effekte, alle innerhalb von
prefers-reduced-motion. Drei neue Bausteine: Symbol, Fortschritt (mitrole="meter") und Slider als Scrollbereich mit Einrasten – ohne JavaScript, ohne Autoplay. „Zeile, 2/3/4 Spalten“ legt eine Zeile samt Spalten auf einen Klick an. Der Vergleich mit Flatsome steht indocs/DESIGNER-VERGLEICH.md, mit dem was fehlt und warum. - Zwei Fehler dabei gefunden und behoben: Rahmenstärken je Seite erzeugten
border-width-top– kein gültiges CSS, vom Browser stillschweigend verworfen, also ohne jede Wirkung. Und eine Rundung auf einem Bildblock rundete den Rahmen, nicht das Bild. - Update über Git (ADR 035, Arbeitspaket N/UPDATE.md):
cms:updateund ein Bildschirm „System“ zeigen Fassung, Commit, verfügbaren Tag und eine Vorprüfung, die jede Bedingung einzeln nennt – unter anderem, ob Datenbank und Medienordner von Git ausgenommen sind. Der Ablauf: sichern, Wartungszustand (503 mitRetry-After,/adminbleibt offen), Checkout des Tags, Fassung prüfen, Composer, Migration, Cache, Rauchtest als eigener Prozess (cms:health). Scheitert ein Schritt, wird auf den vorherigen Commit zurückgesetzt, die Datenbank nur dann zurückgeschrieben, wenn die Migration schon begonnen hatte – und wenn der alte Stand antwortet, geht die Website wieder online.--check,--dry-run,--to=v1.2.0und--maintenance=an|ausgibt es auf der Konsole. - Statistik (ADR 034, Arbeitspaket L): Aufrufe je Tag und Stunde, meistaufgerufene Seiten, Verweise von außen und nicht gefundene Adressen, als CSV exportierbar. Gespeichert wird ein Zähler je (Tag, Stunde, Pfad, Status, Verweis-Domain), kein Datensatz je Aufruf – keine IP-Adresse, keine Kennung, kein Cookie, auch nicht gehasht. Deshalb braucht diese Messung keine Einwilligung. Diagramme zeichnet der Server als SVG, ohne Skript. Was sie nicht kann, steht auf dem Bildschirm: Aufrufe statt Besucher, keine Verweildauer, keine Absprungrate, kein Klickweg, kein Land. Nach 60 Tagen werden Stunden zu Tageswerten verschmolzen, nach 425 Tagen gelöscht (
cms:stats:prune). - Einwilligung (ADR 032, Dokumentschema v14): serverseitig gerendertes Banner ohne JavaScript, „Alle akzeptieren“ und „Nur notwendige“ gleich groß nebeneinander, Auswahl je Gruppe im Aufklappfeld. Die Entscheidung steht in einem eigenen Cookie – keine Kennung, kein Serverabgleich, ein halbes Jahr. Unter
/einwilligungjederzeit änderbar, von jeder Seite verlinkt. Der neue Block „Externer Inhalt“ lädt ohne Einwilligung nichts: kein iframe, keine Adresse im Markup,frame-src 'none'. Der Platzhalter nennt den Anbieter und gewährt auf Knopfdruck genau diese Gruppe. Ist der Hinweis abgeschaltet, laden die Inhalte – sonst würde das Abschalten jede Seite stillschweigend leeren. - Datenschutz und Impressum sind Pflicht, nicht Einstellung (ADR 033): fehlen sie, warnt die Verwaltung und legt sie auf Knopfdruck als Gerüst mit
NOCH AUSZUFÜLLEN-Markierungen an. Verlinkt werden sie aus dem Template, nicht aus dem Fußbereich, damit der Link nicht versehentlich gelöscht werden kann. - Kopf- und Fußbereich lagen falsch im Layout. Umut hat es im Screenshot gesehen: die Wortmarke stand als Buchstabenkolonne in einem 60 Pixel breiten Kasten. Zwei Ursachen, beide behoben –
.site-headerwar noch ein Flexcontainer für das alte, handgeschriebene Markup und quetschte die Sektion darin zusammen, unddesign.cssband alle Designklassen an#main, sodass Kopf- und Fußbereich außerhalb davon jede Designeinstellung ignorierten. - Benutzerverwaltung (ADR 030): Konten anlegen, Rolle ändern, Passwort setzen, Konto löschen – nur für Administratoren. Eine Rolle je Konto (Administrator, Veröffentlicher, Redakteur). Ein Passwort oder eine Rolle zu ändern meldet jede offene Sitzung dieses Kontos ab, weil die Sitzung Hash und Rollen gegen die Datenbank prüft; nur die Sitzung, die ihr eigenes Passwort ändert, bleibt angemeldet. Niemand kann sein eigenes Konto löschen oder herabstufen, deshalb bleibt immer ein Administrator übrig. Das eigene Passwort darf jeder ändern, aber nur mit dem aktuellen. Die Liste zeigt letzte Anmeldung, Passwortdatum und Anlagedatum.
- Mediathek als Seite (ADR 031):
/admin/mediazeigt die Bilder direkt, 24 je Seite, mit Blättern und Archivfilter. Hochladen und Bildangaben über gewöhnliche Formulare, ohne JavaScript. Hochgeladen wird über eine Ablagefläche: Bilder hineinziehen oder klicken; darunter steht, welche Dateien gewählt sind und welche übersprungen wurden. Darunter liegt ein echtes, per Tastatur erreichbares Dateifeld – ohne JavaScript bleibt es der Dateidialog, und abgeschickt wird immer erst auf Knopfdruck. Der Dialog bleibt für die Bildauswahl im Editor. - Verwaltung neu gestaltet (ADR 029): ein Rahmen für alle Bildschirme mit fester Seitenleiste, aktivem Eintrag über
aria-current, Titel, einem Satz Erklärung und Platz für die Hauptaktion. Vorher trug jede Seite ihre eigene Kopfzeile und eine eigene, unvollständige Linkliste. Je Seite eine Zeile mit dem Alltäglichen und einem Aufklappfeld „Einstellungen“, das an seinem Platz öffnet, statt sechs Kästen, die über den Rand hinausragten. Tabellen scrollen seitlich, statt die Karte aus dem Fenster zu schieben. Die Editorregeln stehen jetzt ineditor.css, das nur Editor und Mediathek laden. - Verwaltung neu gestaltet (ADR 029): ein Rahmen für alle Bildschirme mit fester Seitenleiste, aktivem Eintrag über
aria-current, Titel, einem Satz Erklärung und Platz für die Hauptaktion. Vorher trug jede Seite ihre eigene Kopfzeile und eine eigene, unvollständige Linkliste. Je Seite eine Zeile mit dem Alltäglichen und einem Aufklappfeld „Einstellungen“, das an seinem Platz öffnet, statt sechs Kästen, die über den Rand hinausragten. Tabellen scrollen seitlich. Der Editor behält seinen Aufbau und übernimmt die neue Gestaltung; seine Regeln stehen jetzt ineditor.css, das nur Editor und Mediathek laden. - Eigenes CSS je Element (Dokumentschema v13) im neuen Reiter „Erweitert“: Eigenschaften für das Element und verschachtelte Regeln mit
&. Der Selektor wird vom Server gesetzt, die Ausgabe neu serialisiert – eine überzählige}kann den Block nicht verlassen. Abgewiesen: @-Regeln, Backslash-Escapes,expression(), fremde Selektoren und jede Adresse außer einem einzelnen/oderhttps://. Für jeden mit Editorrolle. - Eigenes Skript je Element nur für Administratoren. Durchgesetzt beim Schreiben durch Vergleich mit dem gespeicherten Stand – ein Editor kann eine Seite mit Skript weiter bearbeiten, das Skript aber nicht anfassen, auch nicht über eine eigene Anfrage. Ausgeliefert als
/assets/seite/{id}.jsnur für veröffentlichte Seiten, jedes Skript in einer eigenen Closure mitelementundtry/catch. Nur eine Seite mit eigenem Skript sendetscript-src 'self', jede andere behältscript-src 'none'; inline ist nirgends erlaubt. In der Editorvorschau wird nichts ausgeführt. - Formatierter Text in Textblöcken (Dokumentschema v12): fett, kursiv, unterstrichen, durchgestrichen, Code, hervorgehoben, hoch-/tiefgestellt, Absätze, Listen und Links. Im Editor eine Werkzeugleiste plus HTML-Ansicht; eingefügter Inhalt kommt als reiner Text an. Gefiltert wird auf dem Server beim Speichern, nicht im Editor und nicht beim Rendern – was in der Datenbank liegt, ist schon sicher. Nicht erlaubte Elemente werden ausgepackt, der Text bleibt;
script,style,iframeund Verwandte werden samt Inhalt entfernt. Attribute fallen weg außerhrefnach Adressprüfung. Ältere Dokumente behalten Klartextsemantik und werden weiter maskiert. - Kopf- und Fußbereich sind Blockdokumente unter
/admin/partials, im selben Editor gestaltbar wie eine Seite. Im Template steht keine Marke und keine Navigation mehr; ein neuer Blockmenuzeigt einen Menüplatz. Geteilte Bereiche haben keinen Entwurf und keine Veröffentlichung – gespeichert heißt live – und dürfen keine Hauptüberschrift enthalten, weil jede Seite ihre eigene bringt. - Wiederverwendbare Sektionen werden verknüpft, nicht kopiert (Block
partialref, Dokumentschema v11): die Seite speichert nur die Adresse, eine Änderung erreicht jede Seite, die sie einbindet. Zyklen werden abgewiesen, Verschachtelung auf drei Ebenen begrenzt, eine entfernte Sektion hinterlässt keine Lücke und der Verweis bleibt gespeichert. Die Abschnittsvorlagen kopieren weiterhin – beide Wege stehen bewusst nebeneinander. - Frei wählbare Startseite unter Einstellungen. Es gibt jetzt eine Quelle dafür –
SiteSettings::homePath()undpublicPath(); das Literalhomekommt im Anwendungscode nicht mehr vor. Die gewählte Seite muss veröffentlicht und nicht im Papierkorb sein, ihre eigene Adresse leitet dauerhaft auf/, und solange sie die Wurzel ist, lässt sie sich nicht umbenennen oder löschen. /sitemap.xml,/robots.txt,/llms.txtund/feed.xmlwaren lokal nie erreichbar, obwohl jeder Kernel-Test sie bestand: der eingebaute PHP-Server antwortet für fehlende Pfade mit Dateiendung selbst mit 404.bin/dev-router.phpbildet die Rewrite-Regel des Hostings nach; Start- und CI-Befehl nutzen es, und der Browsertest ruft die vier Adressen jetzt über HTTP auf.- Barrierefreiheitsbefunde werden nach Ursache getrennt: ein Kontrastfehler aus Theme oder Stylesheet bricht den Lauf ab, einer aus einer im Editor gesetzten Blockfarbe wird bei jedem Lauf mit Seite, Farbe und Verhältnis ausgegeben und in
reports/browser.jsongeschrieben. Stillschweigend durchgelassen wird nichts. - 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.txtund 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}/revisionsmit Wiederherstellen. Das überschreibt nichts: die alte Fassung wird als neuer Entwurf gespeichert. revision.seqbehebt einen echten Fehler: die Reihenfolge des Verlaufs war nicht eindeutig, weilcreated_atnur 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_atund den Cron-Befehlcms: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<template>, der Editor wendet es überadoptedStyleSheetsan. Ausgeliefert wurde es die ganze Zeit korrekt – nur die Vorschau log. - Die Einrückung der Unterseiten in der Seitenliste hat nie funktioniert: sie lief über ein
style-Attribut, das dieselbe Richtlinie verwirft. Jetzt über Klassen. - Das Theme überschrieb alle Linkfarben und erzeugte damit 3,08:1 auf dunklem Grund. Die Regel gilt jetzt nur für Links in Textblöcken; die voreingestellte Linkfarbe
#a4510fliegt auf allen hellen Tönen über 4,5:1. - Bewegungseffekte (Schatten, Hover, Einblendung) bleiben als eigener Panelabschnitt für jeden Block: sie sind Keyframes und lassen sich mit freien Deklarationen nicht ausdrücken. Die alten Tokenwerte erscheinen nur noch, wo eine Seite sie trägt.
- Direktbearbeitung in der Vorschau: Doppelklick auf Überschrift, Text, Zitat, Aufruf-Band, Symbolkarte, Akkordeon-Eintrag, Tab-Titel oder Kennzahl bearbeitet den Text an Ort und Stelle; Escape beendet, Eingaben werden zu einem Undo-Schritt zusammengefasst. Die Vorschau führt dabei weiterhin kein eigenes JavaScript aus – der Editor greift in ihr Dokument. Eine Schaltfläche im Panel startet dasselbe ohne Maus.
- Kontrastwarnung im Designpanel: Verhältnis zwischen wirksamer Text- und Hintergrundfarbe nach WCAG 2.1 mit drei Stufen. Eine Warnung, kein Verbot.
- Gestaltungsvorlagen im Theme: eine Gestaltung unter einem Namen speichern und auf jedem Block jeder Seite anwenden, dazu Kopieren und Einfügen zwischen Blöcken innerhalb der Sitzung. Vorlagen werden mit demselben Compiler geprüft wie Blockstile.
- Hintergrundbilder werden über die Mediathek gewählt statt über eine eingetippte ID.
- Freie Gestaltung (Dokumentschema v10).
StyleSchemaerklärt jede Eigenschaft einmal mit Typ, CSS-Entsprechung und Grenzen; Validator, CSS-Erzeugung und Editorpanel lesen dieselbe Definition. Rund fünfzig Eigenschaften in sieben Gruppen: Typografie (Schriftart, Größe, Stärke, Lage, Zeilenhöhe, Buchstaben- und Wortabstand, Ausrichtung, Schreibweise, Textlinie, Farbe), Hintergrund (Farbe, Verlauf, Bild mit Größe/Ausschnitt/Wiederholung/Parallax, Überlagerung), Abstände je Seite, Größen, Rahmen je Seite und Ecken je Ecke, Layout (Flex, Raster, Verteilung, Abstand, Reihenfolge) und Wirkung (Deckkraft, freier Schatten, Verschieben, Skalieren, Drehen, Übergangsdauer). Werte je Gerät mit Vererbung Desktop → Tablet → Mobil und einzeln zurücksetzbar; farbige, numerische und Bewegungswerte zusätzlich für den Hover-Zustand. - Theme unter
/admin/theme: frei definierbare Farbpalette (bis 40 eigene Farben, im Editor als@schlüssel), Schriften aus Systemschrift oder Google Fonts mit Auswahl der Schnitte, und die Grundtypografie aus Schrift, Grundgröße, Zeilenhöhe, Überschriftenskala, Inhaltsbreite sowie Text-, Hintergrund- und Linkfarbe. Eine Palettenfarbe zu ändern wirkt überall, wo sie verwendet wird. - Der Server erzeugt pro Seite ein blockgenaues Stylesheet mit Media Queries und
:hover,:focus-within. Es steht inline und ist über seinen eigenen SHA-256-Hash erlaubt, nicht überunsafe-inline. Google Fonts auf ausdrückliche Nutzerentscheidung – das bedeutet eine Anfrage an Google je Seitenaufruf; die Richtlinie öffnet die Hosts nur, wenn tatsächlich eine Google-Schrift eingestellt ist. - SEO: Indexierungssteuerung und Vorschaubild je Seite, Unternehmensdaten unter
/admin/settings,sitemap.xml,robots.txt, Weiterleitungen mit 301 und 410, optionalellms.txt, Brotkrumen-Block (Dokumentschema v9) sowie strukturierte Daten fürOrganization/LocalBusiness,ArticleundBreadcrumbList. Alles Maschinenlesbare wird aus dem veröffentlichten Bestand abgeleitet: eine nicht indexierbare Seite verschwindet gleichzeitig aus der Sitemap und bekommt einnoindex-Meta. Ohne gepflegte Unternehmensdaten wird kein leerer Organization-Block ausgegeben.llms.txtist standardmäßig aus. - Dokumentschema v8: Formulare. Verwaltung unter
/admin/formsmit Feldtypen Text, E-Mail, Telefon, Mehrzeiliges, Auswahl und Einwilligung, Empfänger, Betreff, Antwortadresse und Erfolgsmeldung. Der Formularblock im Seiteninhalt enthält nur die Adresse – Felder und Empfänger liegen in der Datenbank, damit gespeicherter Inhalt nicht ändern kann, wohin eine Anfrage geht. Serverseitige Validierung mit einer Meldung je Feld überaria-describedby/aria-invalid, Eingaben bleiben bei Ablehnung erhalten (HTTP 422), gültige Absendung per HTTP 303 zurück auf die Seite. Spamschutz ohne Dienst Dritter: Honeypot, signierter Zeitstempel und fünf Absendungen je Absender und Stunde. Die Absenderadresse wird nie gespeichert, nur ein Hash; der Zeitpunkt der Einwilligung getrennt. Gespeichert wird vor dem Versand, damit ein kaputter Mailversand keine Anfrage verliert; nicht versendete Einsendungen sind in der Verwaltung gekennzeichnet. Voreingestellter Transportsendmail, perMAILER_DSNauf SMTP umstellbar. Kein CSRF-Token auf dem öffentlichen Weg, weil dafür eine Sitzung und damit ein Cookie auf jeder öffentlichen Seite nötig wäre; Begründung in ADR 017. - Dokumentschema v7: Tabs. Der Server liefert Ankerliste und alle Panels mit Überschriften, also vollständigen Inhalt ohne JavaScript;
public/assets/tabs.jsmacht daraus ein echtes Tab-Widget mittablist/tab/tabpanelund Pfeiltastenbedienung. Die strenge Richtlinie bleibt Voreinstellung: öffentliche Seiten senden weiterhinscript-src 'none', nur eine Seite mit Tab-Block sendetscript-src 'self'. Inline-Skript ist nie erlaubt. In der Editorvorschau erscheinen Tabs untereinander, weil die Vorschau kein JavaScript ausführt; ein Hinweis im Eigenschaftenpanel sagt das. - Dokumentschema v6 mit zehn Blöcken für die Pilotseite: Symbolkarte, Kennzahl, Kundenstimme mit Bewertung, Logoleiste, Aufruf-Band, Kontaktdaten, Trennlinie, Abstand, Akkordeon und Akkordeon-Eintrag. Symbole als Inline-SVG aus einer festen Liste, kein Symbolschriftsatz und keine externe Anfrage. Kontaktdaten werden zu
tel:- undmailto:-Verweisen. Akkordeon überdetails/summary, also ohne öffentliches JavaScript. Tabs sind bewusst nicht umgesetzt – ohne JavaScript gibt es dafür keine zugängliche Lösung; Begründung in ADR 015. - Beiträge: Datum, Autor, Auszug und Beitragsbild als Seitenspalten, gepflegt über ein Formular in der Seitenübersicht. Kategorien und Schlagwörter als Begriffe mit eigenen Archiven unter
/kategorie/{slug}und/schlagwort/{slug}, Gesamtübersicht unter/beitraegemit Paginierung über?seite=N, RSS unter/feed.xml. Der Blockpostlist(Dokumentschema v5) speichert nur Anzahl und Filter; die Beiträge stellt der Controller beim Ausliefern zusammen, damit ein gespeichertes Dokument keinen unveröffentlichten Beitrag zeigen kann. - Menüverwaltung unter
/admin/menusfür die Plätze Hauptnavigation und Fußzeile. Ein Eintrag verweist auf eine Seite, auf ein Sprungziel innerhalb einer Seite oder auf eine Adresse (HTTPS, lokaler Pfad,mailto:,tel:). Zwei Ebenen, bis 60 Einträge, optimistische Konflikterkennung. Bedienung über eine nummerierte Zeilenliste, vollständig ohne JavaScript; Untermenüs in der Ausgabe überdetails/summary. Seitenverweise speichern die Seiten-ID, nicht den Pfad – ein Eintrag folgt einer umbenannten Seite und überlebt eine gelöschte. - Dokumentformat v4: Blocktypen mit Namensraum (
acme.slider) gelten als Bausteine einer Erweiterung und werden erhalten statt abgewiesen. Öffentlich rendert der Kern aus ihnen nichts außer einem HTML-Kommentar, in der Editorvorschau einen Platzhalter. Im Editor bleiben sie auswählbar, verschiebbar, duplizierbar und löschbar, aber nicht bearbeitbar. v1 bis v3 bleiben unverändert unterstützt. - Login-Throttling nutzt in der Testumgebung einen Array-Cache. Zuvor überdauerten die Zähler den Testlauf und sperrten die Anmeldung nach wenigen Wiederholungen aus, was als Testfehler erschien.
- Lokale PHP-/Browser-/Lighthouse-Prüfungen und minimale GitHub-Actions-Konfiguration einschließlich vorbereitetem MySQL-Smokejob.
Geprüft
- 201 PHP-Tests / 716 Assertions unter PHP 8.2.12 und 8.4.22 erfolgreich; zusätzlich 15 JavaScript-Modelltests, der vollständige Playwright-Editortest und die öffentliche Browserprüfung. Abgedeckt sind Migration/Erhaltung, Versionen, ungültige Geräte-/Typ-/Design-/Effektwerte, Vererbung/Reset/Undo, Vorlage-/Galerie-IDs, Uploadvalidierung und Neuencodierung, Metadatenkonflikte, Archivierung sowie sichere Bildquellen.
- Neu geprüft (15 Tests): privater Entwurf nach dem Anlegen, verschachtelte Pfade mit gleichem Slug unter verschiedenen Eltern, abgewiesene Adressen (Großbuchstaben, Schrägstrich, leer, reserviert, unbekannter Typ, fehlendes Elternteil) ohne Schreibzugriff, doppelte Adresse, Tiefengrenze, Beiträge ohne Unterseiten, Löschen mit allen Revisionen samt Schutz für Elternseiten und veraltete Versionen, sowie dass die CMS-Routen nicht vom öffentlichen Auffangmuster verschluckt werden. Über HTTP zusätzlich: anonyme Schreibversuche, CSRF für Anlegen und Löschen, Adressableitung aus dem Titel, kein Löschangebot und HTTP 403 für die Editorrolle, erfolgreiches Löschen als Administrator.
- Verwaltung geprüft: axe ohne Verstöße auf Anmeldung, Seitenübersicht und Editor (Desktop und Mobil), 201 PHP-Tests grün. Nicht geprüft: kein Screenreader, und die Bildschirme Menüs, Formulare, Bereiche, Design, Einstellungen und Fassungen sind nur von Hand angesehen, nicht automatisiert auf Barrierefreiheit geprüft – die Browserprüfung deckt sie nicht ab.
- Aufgabenplan geprüft (14.09.2026): 12 Tests auf die Mechanik (nur Fälliges läuft; eine gesperrte Aufgabe wird übergangen, eine abgelaufene Sperre übernommen; ein Fehlschlag wird festgehalten und der nächste Versuch rückt weiter weg; eine abgeschaltete Aufgabe läuft nicht, von Hand gestartet doch; eine Anfrage arbeitet nicht alles auf einmal ab; eine fehlende Tabelle ist kein Fehler) und 5 über HTTP (der Bildschirm nennt jede Aufgabe und die Adresse des URL-Cronjobs; anonym führt niemand etwas aus, eine Redakteurin bekommt 403, ohne gültiges Formular passiert nichts; der Takt des Backends antwortet mit JSON; ein falscher Cron-Schlüssel bekommt 404). Von Hand auf der Entwicklungsinstallation alle vier Auslöser ausgeführt: alle fünf Aufgaben über die Konsole (Sicherung 36 ms, Updateprüfung 318 ms gegen das echte Repository), ein Seitenaufruf hat eine fällige Aufgabe abgearbeitet (Auslöser
besuchim Protokoll), das geöffnete Backend hat getaktet (POST /admin/aufgaben/takt→ 200), und der URL-Cronjob hat mit dem richtigen Schlüssel Text geliefert und mit einem falschen 404. Der Bildschirm wurde im Browser bedient (Abstand und Schalter gespeichert und wieder zurückgesetzt); dabei fiel eine Inline-Stilangabe auf, die die CSP der Verwaltung blockiert hat – sie ist jetzt eine Klasse. - Ladeverhalten geprüft: drei Tests – 30 Fassungen ergeben zwei Seiten mit richtiger Reihenfolge und richtigem Rest, eine Seite jenseits des Endes zeigt die letzte statt einer leeren; das erste Bild trägt
loading="eager"undfetchpriority="high", das zweitelazy; und die Startseite bleibt unter zwölf Abfragen. Dazu das Ladebudget in der Browserprüfung. - Wiederherstellung nachgewiesen, nicht nur gebaut – das war der älteste offene Punkt aus Arbeitspaket I. Acht Tests gegen eine wegwerfbare Installation, darunter der eigentliche: Sicherung anlegen, Seite und Bilddatei löschen, zurückspielen, beides ist wieder da. Dazu von Hand auf der Entwicklungsinstallation: Testseite angelegt (5 Seiten), gesichert, gelöscht (4), zurückgespielt (5, Seite mit ihrer Adresse wieder da), Testseite entfernt (4). Ebenfalls geprüft: eine manipulierte Sicherung wird abgewiesen, eine aus einer neueren Fassung nur mit
--force, Rotation behält die neuesten, und eine nach der Sicherung hochgeladene Datei überlebt die Wiederherstellung. - Seitenbereiche geprüft: Standard, anderer Bereich, „keiner“ (dann steht weder
site-headernochsite-footerim HTML, die Seite selbst aber schon), ein Verweis auf einen gelöschten Bereich fällt auf den Standard zurück, eine veraltete Fassungsnummer und eine ungültige Adresse werden abgewiesen. Menüarten: drei angenommen, eine erfundene abgewiesen, Burger alsdetails– und die Seite behältscript-src 'none'. Seitenbreite: die Regel insite.cssnimmt den Theme-Wert, und ein größerer Wert erscheint als--k-containerim Seiten-Stylesheet. - Vollbild geprüft: vier Abschnittsbreiten werden angenommen und eine erfundene abgewiesen; sieben Höhenangaben, darunter dvh, svh und lvh, und zwei ungültige; ein randloser Abschnitt mit formatfüllendem Bild erzeugt
#id{width:100%}und#id img{object-fit:cover;height:100dvh}. Die Hero-Vorlage ist im JavaScript-Modelltest nachgerechnet, Eigenschaft für Eigenschaft. - Designer geprüft: 16 Datensätze prüfen, dass jede Eigenschaftsform genau die versprochene Deklaration erzeugt – Rahmen je Seite in der richtigen Schreibweise, Ecken, Positionierung, Flex, getrennte Abstände, Innenschatten, Textschatten, Filter in fester Reihenfolge, Mischmodus, und die Bild-Eigenschaften in einer zweiten Regel
#id img{…}. Jeder davon muss außerdem den Validator überleben, sonst bietet der Editor etwas an, das nicht gespeichert werden kann. Ein Test hält Schema und Editorpanel zusammen: eine Gruppe, die das Panel nicht kennt, würde ihre Eigenschaften in einem unbenannten Aufklappfeld verstecken. - Neue Bausteine geprüft: vier Darstellungen (dekoratives Symbol vor Screenreadern verborgen, sprechendes Symbol mit Text, Fortschritt mit
role="meter"und Wert im Markup, Slider mit Klasse je Gerät) und acht abgewiesene Eingaben (Symbolgröße außerhalb der Liste, unbekanntes Symbol, sprechendes Symbol ohne Beschriftung, dekoratives mit Beschriftung, Fortschritt über 100, Fortschritt als Text, Slider mit sieben Folien, Slider ohne Gerätangabe). Keiner der drei braucht ein Skript: die Seite behältscript-src 'none'. - Updater geprüft: sechs Tests gegen echte Git-Repositorys in einem Temp-Verzeichnis – Vorprüfung benennt geänderte Dateien und eine nicht ausgenommene Datenbank, ein Update wechselt den Tag in der geprüften Reihenfolge und hebt den Wartungszustand auf, ein gescheiterter Schritt setzt Code und Datenbank zurück und bringt die Website auf dem alten Stand wieder online, ein Probelauf verändert nichts, ein unbekannter Tag wird beim Checkout abgewiesen, und eine bereits installierte Fassung ist kein Fehler. Zusätzlich am echten Repository durchgespielt (siehe QUALITY).
- Statistik geprüft: 32 Tests, darunter die Spaltenliste der Tabelle gegen die Zusage „keine Spalte, aus der eine Person hervorgeht“, 14 Anfragearten (Verwaltung, Bild, Stylesheet, Feed, Weiterleitung, POST, Googlebot, Überwachungsdienst, Headless, curl – alle nicht gezählt), sieben Verweisformen (nur der Host bleibt, die eigene Domain gilt nicht als Verweis), sechs Adressformen (Query verworfen, erfundene 404 gesammelt) und das Verschmelzen und Löschen alter Tage. Dazu durch echte Anfragen: fünf Aufrufe werden ein Zähler, ein 404 zählt nicht als Aufruf, Verwaltung und Feed zählen nicht.
- Einwilligung geprüft: 26 PHP-Tests, darunter sieben Cookiezustände (gewährt, abgelehnt, andere Gruppe, abgelaufen, ältere Fassung der Frage, Unsinn, unbekannte Gruppe) und zehn abgewiesene Einbettungsadressen (http, protokollrelativ,
javascript:,data:, mit Port, mit Zugangsdaten, Leerzeichen im Host, ohne Titel, unbekannte Gruppe, zu geringe Höhe). Im Browsertest zusätzlich: Banner erscheint, kein iframe und keine Adresse im Markup, Einwilligen über den Platzhalter lädt den Inhalt, Widerruf hält ihn wieder zurück, axe ohne Verstöße auf Banner und Einwilligungsseite. - Verwaltung geprüft: axe ohne Verstöße auf elf Bildschirmen – Anmeldung, Seitenübersicht, Benutzer, eigenes Konto, Mediathek, Menüs, Formulare, Bereiche, Design, Einstellungen und Editor in Desktop und Mobil. Vorher waren es drei.
- Benutzerverwaltung geprüft: 16 Tests, darunter zwei gleichzeitige Sitzungen, die zeigen, dass ein zurückgesetztes Passwort und eine geänderte Rolle die andere Sitzung abmelden, während die handelnde angemeldet bleibt. Acht abgewiesene Kontoanlagen (zu kurzes Passwort, mehr als 72 Bytes, Nullbyte, drei ungültige Namen, Doppelname, unbekannte Rolle) legen kein Konto an. Jeder Schreibweg braucht ein CSRF-Token und ist ohne Administratorrolle 403.
- Mediathek geprüft: Upload über das Formular mit zwei Dateien in einem Absenden, CSRF-Pflicht, Bildangaben mit Versionskonflikt, Archivfilter, Blättern bei 26 Bildern (24 auf Seite eins, korrekte Sortierung neueste zuerst,
?seite=99wird auf die letzte Seite begrenzt) und Rückkehr auf dieselbe Seite nach dem Speichern. Die Ablagefläche ist im Browsertest geprüft (gewählte Dateien erscheinen im echten Dateifeld und werden benannt) und von Hand mit einem echten Drop, bei dem eine Nicht-Bilddatei übersprungen wurde. - Updateverfahren geplant, nicht gebaut:
docs/UPDATE.mdbeschreibt, wie eine neue Version über einen Git-Tag gezogen wird – Vorprüfung, Sicherung, Wartungszustand, Checkout, Migration, Rauchtest, Rückweg. Zwei Entscheidungen hängen am Hosting: Composer auf dem Server odervendor/im Tag, und ob es überhaupt Shell-Zugriff gibt. - Nicht geprüft: kein Screenreader.
- Eigener Code geprüft: fünf CSS-Formen werden korrekt auf das Element bezogen, darunter
& .block-textmit erhaltenem Nachfahren-Leerzeichen. Zehn abgewiesene CSS-Eingaben erreichen kein Dokument: Ausbruch mit}, fremder Selektor,@import,@media,javascript:-Adresse, protokollrelative Adresse, Backslash-Escape,expression(), Eigenschaft ohne Wert und unsinniger Eigenschaftsname. Eigenes CSS erscheint im Seiten-Stylesheet, eigenes Skript nur als Datei mitscript-src 'self'ausschließlich auf dieser Seite; eine Seite ohne eigenes Skript behältscript-src 'none'und liefert keine Datei. Ein Skript aus einem unveröffentlichten Entwurf wird nicht ausgeliefert. Der Rollenvergleich weist Ändern, Entfernen und Hinzufügen eines Skripts ab und lässt unveränderte Skripte durch. - Formatierter Text geprüft: dreizehn Fälle durch den Filter, darunter erlaubte Auszeichnung und Listen, ein
scriptsamt Inhalt entfernt und der Text danach erhalten, Ereignisattribut entfernt,class/styleentfernt und das Element ausgepackt,javascript:-Link zu Text geworden, Bild mitonerrorentfernt,iframeentfernt, unausgeglichene Auszeichnung geschlossen, lokaler Link erhalten, externer Link mitrel="noopener", Klartext maskiert und Umbruch zu<br>. Dazu: gefiltert wird beim Speichern (die Datenbank enthält schon die sichere Fassung), ein nach dem Filtern leerer Textblock wird abgewiesen ohne zu schreiben, ab v12 erscheint Auszeichnung als Auszeichnung in einemdiv, vor v12 maskiert in einemp, und in einem geteilten Bereich gilt derselbe Filter. - Bereiche geprüft: die eingesetzten Kopf- und Fußbereiche erscheinen als gewöhnliche Blöcke, der Navigationsblock zeigt das Menü, und eine Änderung am Kopfbereich erreicht beide Seiten gleichzeitig. Ein leerer Bereich rendert nichts statt einer kaputten Hülle. Verknüpfte Sektion geprüft: auf zwei Seiten sichtbar, die Seitendokumente enthalten nur die Adresse, eine Änderung wirkt an beiden Stellen,
usedBynennt beide Seiten, und nach dem Entfernen bleiben die Seiten heil und der Verweis erhalten. Abgewiesen werden: Hauptüberschrift in einem geteilten Bereich, Sektion mit Selbstbezug, zweiter Kopfbereich und das Löschen von Kopf- oder Fußbereich. - Startseite geprüft: eine andere veröffentlichte Seite wird zur Wurzel, antwortet unter
/, ihre eigene Adresse leitet mit 301 dorthin, die bisherige Wurzel wird eine gewöhnliche Seite und ist wieder verschiebbar. Sitemap, strukturierte Daten und Menüauflösung folgen derselben Entscheidung. Solange eine Seite die Wurzel ist, werden Umbenennen und Papierkorb abgewiesen. Drei ungültige Wurzelangaben (nicht vorhanden, leer, Großbuchstaben) ändern nichts. - Öffentliche Browserprüfung wieder grün, inklusive der vier maschinenlesbaren Adressen über HTTP mit richtigem Inhaltstyp. Sechs Seiten-/Breitenkombinationen ohne Code-Verstoß; die redaktionelle Feststellung zu
/grundlagenwird ausgegeben. - Lighthouse 13.4.1: je drei Läufe mobil und Desktop, alle fünf Kategorien 100 mit Startseite, Theme, gehashtem Stylesheet und Routerskript.
- Seitenverwaltung geprüft: Verschieben ändert drei Adressen über zwei Ebenen, jede neue antwortet mit 200 und jede alte mit 301 auf das richtige Ziel, Slugs und Elternbeziehungen der Unterseiten bleiben erhalten. Vier Fälle werden abgewiesen, ohne etwas zu bewegen oder eine Weiterleitung zu erfinden: in die eigene Unterseite, in sich selbst, auf eine vergebene Adresse und mit einem unbrauchbaren Slug; dazu die Startseite. Papierkorb geprüft: Teilbaum geht mit, öffentlich 404, verschwunden aus Navigation, Sitemap, Archiv, Feed und Editorliste, Kind kann nicht vor dem Elternteil zurück, Wiederherstellen stellt nur die gewählte Seite zurück. Fassungen geprüft: Reihenfolge neueste zuerst, Wiederherstellen fügt eine Fassung hinzu statt zu überschreiben, eine fremde Fassung wird abgewiesen. Zeitplan geprüft: ein künftiger Zeitpunkt veröffentlicht nichts, ein vergangener genau einmal, danach ist der Zeitpunkt verbraucht, und eine Seite im Papierkorb wird nie veröffentlicht.
- Der vollständige Browser-End-to-End-Test läuft wieder und deckt jetzt das neue System ab.
npm run test:editorwar durch den Umbau kaputt (er steuerte die abgeschafften Auswahllisten) und wurde auf die freien Bedienelemente umgeschrieben: Einheitenfelder, Vererbung über drei Geräte, einzelnes Zurücksetzen, Palettenfarbe, Abstand je Seite, Hover-Regel über den Template-Weg, freie Rasterspalten und die weiterhin vorhandenen Bewegungseffekte. axe meldet null Verstöße auf Login, Seitenliste, Editor in Desktop- und Mobilbreite, Medienauswahl und der bearbeiteten veröffentlichten Seite. - Lighthouse 13.4.1 auf der Startseite mit dem neuen Theme-System: je drei Läufe mobil und Desktop, alle fünf Kategorien 100. Das ist die erste Messung seit dem Umbau des Editors.
- Im laufenden Editor durchgespielt: Doppelklick setzt das Element auf
contenteditable="plaintext-only", eine Eingabe landet im Modell (Speicherstatus wechselt auf „Ungespeicherte Änderungen“, der Strukturbaum zeigt den neuen Text), und das Verlassen beendet die Bearbeitung sauber. Die Kontrastwarnung meldete bei#ccccccauf#ffffffkorrekt „Kontrast 1.6:1 – zu wenig“. Die Schaltflächen zum Kopieren und Einfügen erscheinen mit richtigem Aktivierungszustand. Die Teständerungen wurden verworfen und nicht gespeichert. - Gestaltungsvorlagen über HTTP geprüft: anonymer Zugriff führt zur Anmeldung, CSRF-Token ist Pflicht, ein Stil mit eingeschleuster Deklaration und ein leerer Name werden mit HTTP 422 abgewiesen ohne zu schreiben, Speichern und Entfernen funktionieren, und das Designpanel erhält Vokabular, Palette und Vorlagen aus einer Quelle.
- Freie Gestaltung geprüft: ein v10-Dokument mit Farben, Abständen je Seite, Ecken je Ecke, Schatten, Hover und eigenem Mobilwert wird unverändert gespeichert und zu blockgenauem CSS samt Media Query und Hover-Regel. Vierzehn abgewiesene Eingaben schreiben nichts: unbekannte Eigenschaft, unbekanntes Gerät, Farbe als Wort, Farbe als Funktion, Länge ohne Einheit, Länge als
calc(), ein eingeschleustes;position:fixed, Auswahlwert außerhalb der Liste, Zahl außerhalb des Bereichs, Seitenwert als Einzelwert, unbekannte Seite, Hover auf einer Eigenschaft ohne Hover, Schriftschlüssel mit Anführungszeichen und Hintergrundbild als Pfad. Zusätzlich geprüft, dass freie Stile Schema v10 verlangen. - Sicherheitsrichtlinie geprüft: das erzeugte Stylesheet wird über seinen eigenen SHA-256-Hash erlaubt,
unsafe-inlinekommt nicht vor und Skripte bleiben aus. Ohne Google-Schrift wird kein fremder Host erlaubt und keiner kontaktiert; mit Google-Schrift erscheinenfonts.googleapis.cominstyle-srcundfonts.gstatic.cominfont-src. - Theme geprüft: Palettenfarben werden zu CSS-Variablen und normalisiert, die Schriftvariable enthält Familie und Ersatz, Grundgröße, Zeilenhöhe und Inhaltsbreite wirken auf jeder Seite. Acht ungültige Themes schreiben nichts: Farbe als Wort, doppelter Schlüssel, Schriftname als Stapel, Google-Schrift ohne Namen, Grundfarbe ohne Paletteneintrag, Grundgröße ohne Einheit, Zeilenhöhe außerhalb des Bereichs und leere Palette.
- Im Editormodell geprüft: Vererbung vom größeren Gerät, einzelnes Zurücksetzen, das Entfernen leerer Reste, Kopieren der Gestaltung ohne geteilte Objekte und ein vollständiges Undo auf den Ausgangszustand.
- SEO geprüft: die Sitemap ist wohlgeformtes XML und enthält weder eine
noindex-Seite noch einen Entwurf, dafür Beitragsübersicht und Begriffsarchiv;robots.txtverweist auf sie und sperrt/admin; eine nicht indexierbare Seite trägt zusätzlich einnoindex-Meta. Weiterleitungen antworten vor dem 404 mit 301 beziehungsweise 410, eine vorhandene Seite gewinnt gegen eine gleichnamige Weiterleitung, und Selbstbezug, Kette, Großbuchstaben sowie ein anderer Statuscode werden abgewiesen. Strukturierte Daten entstehen nur aus gepflegten Werten: ohne Angaben kein Block, danachLocalBusinessmit Anschrift und entdoppelten Profilen,Articlemit Autor aus den Beitragsdaten undBreadcrumbListaus der Hierarchie.llms.txtantwortet ausgeschaltet mit 404 und listet eingeschaltet weder einenoindex-Seite noch einen Entwurf. Fünf ungültige Unternehmensangaben schreiben nichts, und inrobots.txtüberleben weder Markup noch eine zweite Sitemap-Zeile. - Formulare geprüft: Ausgabe ohne Skript, Honeypot außerhalb der Tabreihenfolge, gültige Absendung wird gespeichert und per HTTP 303 zurückgeleitet, Einwilligungszeitpunkt festgehalten, keine Absenderadresse in der Zeile sondern ein 64-stelliger Hash, Erfolgsmeldung auf der Zielseite. Fehlerhafte Absendung: HTTP 422, nichts gespeichert, vier Meldungen an den richtigen Feldern mit
aria-describedbyundaria-invalid, genau eine Zusammenfassung mitrole=alert, alle Eingaben erhalten. Vier Spamfälle (gefüllter Honeypot, zu schnell, gefälschte Signatur, zu alter Zeitstempel) werden still verworfen und wie ein Erfolg beantwortet. Das Limit stoppt die sechste Absendung innerhalb einer Stunde. Die Formularverwaltung verlangt Anmeldung, Editorrolle und CSRF-Token und weist eine ungültige Empfängeradresse mit Meldung ab. - Tabs geprüft: beide Panels samt Inhalt und Überschrift stehen ohne JavaScript im HTML und sind serverseitig nicht versteckt, die Ankerliste verweist auf die Panels, das Skript wird als externe Datei geladen und nie inline. Die Richtlinie sendet
script-src 'self'ausschließlich auf der Seite mit Tab-Block; eine Seite ohne Tabs und die 404-Seite behaltenscript-src 'none'und laden kein Skript. Ein Tab außerhalb einer Tabgruppe, eine Tabgruppe mit Textkind und Tabs in Schema v6 werden abgewiesen. Im Modell: eine neue Tabgruppe bringt zwei Tabs mit eigenen IDs mit, nimmt nur Tabs auf, und ein Tab lässt sich weder in eine Sektion noch in ein Akkordeon einfügen. - Neue Blöcke geprüft: eine Seite aus allen zehn Abschnittsarten wird unverändert gespeichert, veröffentlicht und ausgegeben; Kontaktdaten werden zu
tel:- undmailto:-Verweisen mit bereinigter Nummer, das Akkordeon ist ein echtesdetails-Element, Symbole sind Inline-SVG, die Bewertung ist für Screenreader ausgeschrieben, und die Ausgabe enthält keinscript. Vierzehn abgewiesene Eingaben schreiben nichts: unbekanntes Symbol, fehlender Titel,javascript:-Ziel, Bewertung außerhalb 0–5 und als Zeichenkette, leerer Kontaktblock, ungültige Telefonnummer und E-Mail-Adresse, unbekannte Abstandsgröße, unerlaubtes Linkschema, Text in Logoleiste und Akkordeon, Akkordeon-Eintrag ohne Akkordeon sowie Kinder an einer Trennlinie. Zusätzlich geprüft, dass die neuen Blöcke in Schema v5 weiterhin abgewiesen werden. - Beiträge geprüft: Sortierung nach Datum mit Paginierung und Begrenzung zu großer Seitenzahlen, Seiten und unveröffentlichte Beiträge bleiben aus dem Archiv, Kategorie- und Schlagwortarchive einschließlich getrennter Namensräume, erneutes Speichern ersetzt die Zuordnung statt sie zu ergänzen, ein leer gewordenes Archiv antwortet weiter mit HTTP 200 und leerer Liste, Löschen löst die Zuordnungen und lässt die Begriffe bestehen, Veröffentlichen stempelt ein fehlendes Datum und lässt ein gepflegtes unangetastet, Seiten werden nie gestempelt. Sechs abgewiesene Metadateneingaben schreiben nichts und erhöhen die Version nicht. Die Beitragsliste zeigt nur Beiträge des gefilterten Begriffs, und das gespeicherte Dokument enthält weiterhin ausschließlich Anzahl und Filter. Der Feed ist wohlgeformtes XML mit absoluten Adressen und enthält keinen unveröffentlichten Beitrag.
- Menüs geprüft: Seiten-, Anker- und externe Ziele einschließlich
tel:in der Ausgabe,aria-currentnur auf der tatsächlich aktuellen Seite und nicht auf Ankereinträgen, gelöschte und unveröffentlichte Seiten fallen aus der Navigation ohne die übrigen Einträge zu verlieren, Kinder eines übersprungenen Eintrags rücken nach, gespeicherte Einträge bleiben erhalten und erscheinen nach dem Veröffentlichen wieder. Zehn abgewiesene Eingaben (leere und überlange Beschriftung, unbekannte Zielart, fehlende Seite, ungültige Sprungziele,javascript:,http:, protokollrelative Adresse, dritte Ebene) schreiben nichts. Über HTTP zusätzlich: anonymer Schreibversuch, CSRF, veraltete Version, Aufbau des Baums aus flachen Formularzeilen mit Position und Ebene, normalisiertes Sprungziel und die Bestätigung, dass die Navigation nicht mehr aus dem Template stammt. - Plugin-Erhalt geprüft: ein v4-Dokument mit
acme.slidersamt verschachteltem Text wird Byte für Byte gespeichert, veröffentlicht und wieder geladen; weder Props noch Kindinhalte noch einejavascript:-URL erscheinen in der öffentlichen Ausgabe, die übrige Seite rendert normal. Fehlerhafte Namensräume und Plugin-Blöcke in v1 bis v3 werden weiterhin abgewiesen, ohne zu schreiben. Im JavaScript-Modell zusätzlich: Verschieben, Duplizieren mit neuen IDs, unverändertes Undo, kein Einfügen in den Baustein und keine Erzeugung über die Elementbibliothek. - Testumgebung:
tests/bootstrap.phpverwirftvar/cache/testeinmal pro Lauf. Ohne Debug prüft Symfony weder den kompilierten Container noch die Vorlagen auf Aktualität; das hat zweimal ein falsches Ergebnis erzeugt (eine Vorlagenänderung wurde übergangen, später schlug ein geänderter Konstruktor aus einem veralteten Container fehl). Zusätzlich lädt Twig in der Testumgebung automatisch neu. - Migration
20260912000600zuerst gegen eine Kopie der lokalen Datenbank ausgeführt und das Ergebnis kontrolliert: acht Anweisungen, vorhandene Seiten und Dokumente unverändert, neue Tabellentermundpage_term, Sortierindex angelegt. Danach nach Backup untervar/backups/before-posts-*.dblokal angewendet. - Migration
20260912000500zuerst gegen eine Kopie der lokalen Datenbank ausgeführt, dann nach Backup untervar/backups/before-menus-*.dblokal angewendet. Seeding legt fehlende Menüs an und lässt vorhandene unverändert; zweimal hintereinander ausgeführt. - Migration
20260912000400zuerst gegen eine Kopie der lokalen Datenbank geprüft und die erzeugten Anweisungen kontrolliert: vier reineALTER TABLE … ADD COLUMN, kein Tabellenneuaufbau. Danach nach konsistentem Backup untervar/backups/before-hierarchy-20260912-102402.dblokal angewendet; Backfill, Indexumstellung und unveränderte Dokumente überprüft. Wiederholtes Seeding und Produktionsbuild erfolgreich. - Composer-/Format-/Twig-/YAML-/Containerprüfungen, Migration, wiederholtes Seeding und Produktionsbuild erfolgreich.
- Frische Quellkopie ohne bestehende DB/Secrets/Dev-Abhängigkeiten erfolgreich eingerichtet; gespeicherte Seite als HTTP 200 gerendert.
- Browserprüfung aller drei öffentlichen Seiten mobil und Desktop; keine axe-Verstöße, keine Überläufe, funktionierende Sprunglink-/Navigationsabläufe auch ohne JavaScript.
- Editor-End-to-End in isolierter Installation: Anmeldung, Auswahl im Iframe, Texteingabe, Undo/Redo, Einfügen/Duplizieren/Verschieben/Drag-and-drop/Löschen, identisches Neuladen, private Entwürfe, Veröffentlichung, Dialog-Fokus/Escape, simulierter Netzwerkfehler mit Wiederholung, 409-Konflikt mit JSON-Export und Server-Neuladen. Einschränkungen der axe-Frameprüfung sind in QUALITY dokumentiert.
- Ausbau im Browser geprüft: Struktursuche und Auswahl per Tastatur, Einklappen, Abstände, Schriftwerte und Vererbung in echten Gerätebreiten, Rücksetzen/Undo, responsive Zweispaltenvorlage und identische Styles nach Speichern/Publizieren auf allen drei Geräten.
- Editoroberfläche, Login und Seitenübersicht ohne axe-Verstöße; identisch gerenderter bearbeiteter öffentlicher Inhalt separat geprüft. Desktop- und Mobil-Screenshots visuell durchgesehen.
- Lighthouse-Startseite: jeweils drei echte mobile und Desktop-Läufe, Median in allen fünf Kategorien 100. Nur lokaler Prototyp; keine Kundenwebsite-Abnahme. Umfang und Grenzen siehe QUALITY.
- Gestaltete v2-Testseite mit Design-CSS und responsiver Abschnittsvorlage: zusätzliche sechs Lighthouse-Läufe, alle fünf Kategorien jeweils 100; LCP-Median mobil 901 ms/Desktop 240 ms, CLS/TBT 0. Echte Seitenbestände und geöffnete Browserentwürfe unangetastet. Bericht:
docs/quality/editor-design-lighthouse.json. - v3-Galerieseite mit echten optimierten Testbildern und Effekten: sechs Lighthouse-Läufe, alle fünf Kategorien jeweils 100; LCP-Median mobil 937 ms/Desktop 279 ms, CLS/TBT 0. Upload, Metadaten, Mehrfachauswahl per Tastatur, Bildbeschriftung, Sortier-Undo, Archivieren/Wiederherstellen, Bewegungsreduktion und öffentliche Bilder im isolierten Browserablauf bestanden. Keine axe-Verstöße in Desktop-/Mobil-Mediathek; Screenshots geprüft. Berichte:
docs/quality/editor-media-browser.jsonundeditor-media-lighthouse.json. - Keine bekannten Dependency-Advisories im ausgeführten Composer-Audit/npm-Installationsaudit.
Gemessen am Nachbau selbst (13.09.2026, Fassung 1.4.3): je drei mobile und drei Desktop-Läufe auf /, /projekte und /impressum im Produktionsmodus, Median. Barrierefreiheit, Best Practices, SEO und agentisches Browsen überall 100. Leistung: /projekte und /impressum 100/100, die Startseite 99 mobil / 97 Desktop – dort bleibt das blockierende Stylesheet (rund 300 ms erste Anzeige) das Letzte, und das ist zu einem guten Teil der Entwicklungsserver ohne Kompression und Cache-Kopfzeilen. Bericht: docs/quality/kantit-lighthouse.json. Auf dem echten Hosting nachmessen – dort greifen die Regeln aus public/.htaccess.
Bekannte Einschränkungen
- Gestaltung über geprüfte Tokens, keine beliebigen Pixelwerte, eigenen Schriften oder freien Farben. Noch keine Inline-Textbearbeitung, Formulare, dynamischen Menüs, Plugin-/Themeverwaltung oder WordPress-Import. Abschnittsvorlagen sind lokale Blockrezepte, keine globale Templateverwaltung.
- Mediathek derzeit für statische Bilder: keine Videos/PDFs/SVGs, Lightbox/Slideshow, freie Bildzuschnitte, Ordner, Paginierung oder physische Löschung. Bild-URLs sind öffentlich; Archivieren schützt keine vertraulichen Dateien. Originale werden durch optimierte Varianten ersetzt. Einblendungen starten beim Laden, nicht beim Scrollen.
- Sektionen bieten die vorhandenen Farb-/Breitentokens geräteübergreifend. Benutzerverwaltung und Passwortänderung sind noch nicht als Browseroberfläche verfügbar.
- Der Aufgabenplan ist von Anfragen abhängig. Ohne Besucher und ohne geöffnetes Backend läuft nichts; minutengenau ist nur ein echter Cronjob oder der URL-Cronjob des Hosters. Unter Apache mit mod_php belegt eine laufende Aufgabe einen PHP-Prozess, nachdem die Seite beim Besucher ist (dort gibt es kein
fastcgi_finish_request()) – deshalb sind die Grenzen je Seitenaufruf klein (2 Aufgaben, 20 Sekunden) und der Auslöser abschaltbar. Die Sicherung ist die schwerste Aufgabe und läuft trotzdem mit: eine tägliche Sicherung ist mehr wert als keine. Nicht geprüft: das Verhalten unter mod_php und unter echter Last – hier lief alles gegen den eingebauten PHP-Server. - Offene redaktionelle Feststellung:
/grundlagenträgt einen im Editor gesetzten Textblock in#d08080auf hellem Grund – 2,75:1, unter der AA-Schwelle. Die Farbe wurde nicht verändert, weil sie eine Nutzerentscheidung ist; die Prüfung nennt sie bei jedem Lauf, und die Kontrastwarnung im Editor meldet sie ebenfalls. - Aus Umuts erweitertem Auftrag noch offen (Paket G2 im Ausbauplan): die Neugestaltung der Verwaltung.
- Eigenes CSS kennt keine Gerätevarianten – dafür sind die Vorschaugrößen im Designbereich da. Eigenes Skript hat keine Fassungsverwaltung außer der Seitenhistorie und keine Prüfung auf Syntaxfehler; ein fehlerhaftes Skript schreibt in die Browserkonsole und lässt die Seite sonst unangetastet.
- Formatierter Text gilt nur für Textblöcke. Überschrift, Button, Zitat und die Titel von Karten, Tabs und Akkordeon bleiben Klartext; Auszeichnung in einer Überschrift bringt mehr Ärger als Nutzen. Die Werkzeugleiste ist nicht mit einem Screenreader geprüft.
- Offen: der Papierkorb wird nicht automatisch geleert, es gibt keine Aufbewahrungsfrist, und Weiterleitungen aus einem Verschiebevorgang lassen sich nur einzeln in den Einstellungen entfernen.
- Weiterhin keine Erprobung mit einem Screenreader. Bei der Direktbearbeitung wiegt das: dass der Fokus sinnvoll wandert und der Zustandswechsel angekündigt wird, ist durch axe nicht abgedeckt.
- Gestaltungsvorlagen lassen sich in der Oberfläche nur anlegen und anwenden, nicht umbenennen; das Entfernen geht bisher nur über die Schnittstelle. Es gibt keine Vorschau einer Vorlage.
- SEO-Titel und -Beschreibung sind weiterhin die Dokumentfelder; getrennte SEO-Titel gibt es nicht. Das Vorschaubild wird über seine Mediathek-ID eingetragen und noch nicht als
og:imageaufgelöst. Weiterleitungen werden nur bei genauer Übereinstimmung des Pfades gefunden, ohne Muster und ohne Berücksichtigung von Abfrageparametern; Seiten umbenennen erzeugt noch keine automatische Weiterleitung (Paket F). - Dateiuploads in Formularen fehlen – Anhänge brauchen einen nicht öffentlichen Ablageort mit Zugriffsschutz und eine eigene Typprüfung; Begründung in ADR 017. Ebenfalls offen: Formulare lassen sich nicht löschen oder umbenennen, Einsendungen nicht exportieren oder löschen, und es gibt keine Aufbewahrungsfrist. Der tatsächliche E-Mail-Versand ist nie ausgeführt worden: die Tests nutzen einen Transport, der nichts sendet.
- Das Tab-Skript ist nicht im Browser geprüft: Pfeiltastenbedienung,
aria-selected, das Verstecken der Panels und die Auswahl über ein Sprungziel sind nur im Code umgesetzt, nicht ausgeführt. Geprüft ist bisher ausschließlich die serverseitig erzeugte Auszeichnung und die Sicherheitsrichtlinie. - Die neuen Blöcke lassen sich nicht in einer Vorlage bündeln: die drei Abschnittsvorlagen sind unverändert. Die Logoleiste hat keine Graustufen- oder Größenoption, die Kundenstimme kein Bild, die Symbolkarte keine eigene Farbe.
- Beiträge haben noch keine Vorschau des Beitragsbilds in der Verwaltung: das Bild wird über seine Mediathek-ID eingetragen. Es gibt keine Begriffsverwaltung (Umbenennen, Zusammenführen, Löschen ungenutzter Begriffe), keine Beitragssuche und keine Archivbeschreibungen. Die Paginierung der Übersicht ist auf zehn Beiträge je Seite festgelegt.
- Menüs kennen nur die beiden festen Plätze Hauptnavigation und Fußzeile; weitere Plätze, eigene Menüs pro Seite und automatisch aus der Hierarchie erzeugte Einträge gibt es nicht. Die Einträge werden über Position und Ebene sortiert, nicht per Drag-and-drop.
- Seiten lassen sich anlegen und löschen, aber noch nicht umbenennen oder verschieben: eine Adressänderung müsste die Pfade aller Unterseiten mitziehen und Weiterleitungen anlegen. Beiträge werden gespeichert und ausgegeben, haben aber noch keine Übersichtsseite, keine Sortierung nach Datum und keine strukturierten Daten. Die Hauptnavigation ist weiterhin fest im Template; verschachtelte Seiten erscheinen dort nicht automatisch.
- Für diesen Schritt wurden bewusst keine Browser-, Lighthouse- oder axe-Läufe ausgeführt (Nutzerentscheidung). Oberfläche und Ausgabe der neuen Formulare sind nur über die Funktionstests geprüft, nicht visuell.
- SQLite tatsächlich getestet. MySQL-/MariaDB-Kompatibilität und CI erst vorbereitet, nicht nachgewiesen; Anbieter-/Tarifdetails und Livebetrieb offen.
- Der Erhaltungsvertrag für Plugin-Blöcke steht, ein Pluginsystem nicht: Registrierung, Hooks, Abhängigkeiten und Ressourcenladen folgen in Phase 4. Kinder eines Plugin-Blocks werden gespeichert, aber nicht gerendert. Kern-Designtokens gelten für sie nicht.
- Kein Inhaltscache, nur Symfony-Programmcache. Seiten senden
no-store; Veröffentlichung ist dadurch ohne Cacheinvalidierungsverzögerung sichtbar. - Noch keine repräsentativen Kundeninhalte/Bilder/Blog-/Formularseite, keine Felddaten und keine vollständige manuelle Barrierefreiheitsprüfung.
- Die vor der Hostingantwort erzeugte
.venvbleibt ungenutzt und von Git ausgeschlossen. Die automatische Freigabeprüfung blockierte ihren Löschversuch mit „blocked by policy“; keine Umgehung versucht. Auchvar/fresh-check, DB, Secrets und Berichte sind lokal ignoriert.
Arbeitspaket H, Teil 1: die Vorschau wird zur Arbeitsfläche
Erledigt. Der Abgleich mit dem Flatsome UX Builder steht in docs/FLATSOME-ABGLEICH.md:
die Gestaltung ist Flatsome bereits voraus (93 freie Eigenschaften je Gerät und für Hover gegen
feste Optionslisten), die Lücken liegen beim Elementkatalog (20 offene Elemente ohne Shop) und
bei der Bedienung. Umuts Entscheidung: Bedienung zuerst, Shop-Elemente ausgelassen.
Umgesetzt ist die erste Hälfte der Bedienung:
- Werkzeugleiste am Element in der Vorschau: Name des Elements, Elternelement auswählen, nach oben, nach unten, duplizieren, löschen. Sie trägt den Namen des Elements („Sektion duplizieren“) statt des nackten Verbs – die Seitenleiste hat dieselben fünf Aktionen, und zwei gleichnamige Bedienelemente auf einem Bildschirm sind für eine Sprachausgabe nicht unterscheidbar. Beide Wege rufen dieselben Funktionen auf.
- Auswahl- und Überfahrrahmen mit Beschriftung direkt in der Vorschau.
- Einfügepunkte: ein Pluszeichen an der Ober- und Unterkante des gewählten Elements setzt eine Marke; das nächste Element aus dem Werkzeugkasten landet genau dort statt am Ende.
- Ziehen aus dem Werkzeugkasten in die Vorschau, mit Ablegemarke zwischen den Elementen und Rahmen um einen Behälter, der das Element aufnimmt. Der Strukturbaum zieht in dieselbe Fläche.
- Im Modell dafür neu:
insertBlockAt,moveBlockTounddropList– punktgenaues Einfügen und Verschieben statt „hinter das Gewählte“.
Die Ebene liegt im Editordokument über dem Rahmen, nicht darin: die Vorschau wird bei jeder Änderung vollständig ersetzt, und eingezeichnete Bedienelemente wären damit weg und stünden außerdem in der Auszeichnung, die die Vorschau wahrheitsgetreu zeigen soll. Sie trägt denselben Skalierungsfaktor wie der Rahmen; Leiste und Pluszeichen werden gegenskaliert und bleiben in der Tablet- und Mobilvorschau fingergroß.
Geprüft. npm test 22 von 22 (vier neue Tests für Einfügen, Verschieben, Behälterregeln und
die oberste Ebene). php vendor/bin/phpunit 434 von 434. npm run test:editor vollständig,
mit einem neuen Abschnitt für Werkzeugleiste, Einfügepunkt und Ziehen; alle axe-Prüfungen ohne
Verstoß, auch die mobile Editoransicht. Screenshot unter reports/editor/canvas-overlay.png.
Dabei gefunden und behoben: die ganze Ebene war zunächst aria-hidden und enthielt
fokussierbare Schaltflächen – axe hat das gemeldet. canContain las block.type an einem
fehlenden Block und zerbrach beim Ziehen aus dem Werkzeugkasten, wo der Block noch nicht
existiert. Beim Verschieben ans Listenende wurde der Index begrenzt, bevor die Entnahme
verrechnet war; der Block landete eine Stelle zu früh.
Bekannte Grenzen. Die Vorschau wird weiterhin nach 250 ms serverseitig neu erzeugt – das Ablegen fühlt sich dadurch nicht so unmittelbar an wie bei Flatsome, die Wahrheit bleibt aber der Server. Die Werkzeugleiste ist nicht mit einem Screenreader erprobt, nur mit axe. Spalten lassen sich noch nicht in der Vorschau breiter ziehen, der Werkzeugkasten ist weiterhin eine Textliste ohne Vorschaubilder, und es gibt je Option noch keinen eigenen Geräteschalter.
Arbeitspaket H, Teil 2: der Werkzeugkasten kennt den Zusammenhang
Erledigt. Umuts Einwand: Zeile und Spalte standen oben in der Auswahl, als wären sie Seitenanfänge. Sie gehören in den Inhalt – erst eine Sektion, dann eine Zeile darin. Das war nicht nur eine Anzeigefrage: eine Zeile nahm bisher jedes Kind an, eine Überschrift konnte neben den Spalten landen, und eine Spalte durfte überall stehen. Die Struktur, die der Editor in seinem eigenen Hinweistext beschreibt – Sektion → Zeile → Spalte → Inhalt –, galt für die Schaltflächen nicht.
Übernommen ist Flatsomes Regelmodell, weil es die Fälle abdeckt und weil es das ist, was Umut in
den Händen hat (inc/builder/core/server/src/Collections/Elements.php):
| Regel | Flatsome | KantSites jetzt |
|---|---|---|
allow |
row => ['col'], accordion => ['accordion-item'] |
ALLOW – Zeile nimmt nur Spalten |
require |
col => ['row'], tab => ['tabgroup'] |
REQUIRE – Spalte lebt nur in einer Zeile |
| — | (Flatsome kennt das nicht) | ROOT_NEEDS_CONTAINER – eine Zeile ist kein Seitenanfang |
Der dritte Punkt ist der Zusatz: Flatsome lässt eine Zeile auch lose am Dokumentanfang zu. Genau das ist das „aus dem Nichts“, das Umut stört, also wird es unterbunden – in jedem Behälter dagegen bleibt die Zeile erlaubt, auch in einem Tab oder einem Akkordeon-Eintrag.
Daraus folgt die Bedienung:
- Der Werkzeugkasten ist nach Aufbau gruppiert – Aufbau, Inhalt, Bausteine, Gruppen, Seite und Daten – statt einer flachen Liste aus 31 Typen.
- Was an der gewählten Stelle nicht hingehört, ist gesperrt und sagt beim Überfahren warum („Spalte: gehört in Zeile. Wähle dort zuerst ein Element aus.“). Eine Regel, die man erklärt bekommt, lernt man; eine Schaltfläche, die nur nicht mehr reagiert, drückt man weiter.
- Was möglich ist, sagt wohin es kommt („Wird in Zeile eingefügt.“).
- Die Zeile wird nach Spaltenzahl angeboten (2, 3, 4) und bringt ihre Spalten mit. Der separate Zeilenbereich über der Liste ist entfallen.
- Eine Regel für alle Wege:
insertionTargetbeantwortet „wohin käme das?“, ohne etwas einzufügen. Knopf, Einfügepunkt, Ziehen und die Sperrlogik des Werkzeugkastens nutzen dieselbe Funktion – vorher entschiedinsertBlockallein, niemand konnte vorher fragen, undinsertRowhatte eine eigene zweite Kopie derselben Platzierungslogik.
Ein Element, das der gewählte Behälter nicht nimmt, wird nicht gesperrt, sondern steigt auf, bis ein Elternteil es annimmt: eine Überschrift bei gewählter Zeile landet hinter der Zeile in der Sektion. Die Absicht ist eindeutig, nur die Zeile ist der falsche Ort.
Nebenbei behoben: postnav fehlte seit Schema v18 in labels und icons – der
Strukturbaum zeigte für die Beitragsnavigation „Unbekannt“ und ein Fragezeichen.
Geprüft. npm test 27 von 27 (fünf neue: Zeilen-/Spaltenregel, oberste Ebene, Übereinstimmung
von Auskunft und Tat, Kategorie-Vollständigkeit, Zeile mit Spalten an fester Stelle).
php vendor/bin/phpunit 446 von 446. npm run test:editor vollständig mit neuem Abschnitt
„the toolbox follows the structure“; alle axe-Prüfungen ohne Verstoß. Screenshot unter
reports/editor/toolbox.png.
Bekannte Grenzen. Inhalt darf weiterhin lose am Dokumentanfang stehen – der Validator erlaubt es, und eine weitere Einschränkung wäre erfunden statt übernommen. Bestehende Dokumente werden nicht nachträglich geprüft: eine Überschrift, die früher direkt in einer Zeile gelandet ist, wird weiterhin ausgegeben und lässt sich auswählen und löschen, nur nicht mehr dorthin verschieben. Der Werkzeugkasten hat weiterhin keine Vorschaubilder und keine Suche.
Arbeitspaket H, Teil 3a: die Optionen selbst
Erledigt. Drei Bedienungen, die Flatsome an jeder Option hat und die hier fehlten.
Geräteschalter je Option. Bisher entschied ein einziger Schalter über der Vorschau, welches Gerät das ganze Bedienfeld bearbeitet. „Ist die Überschrift auf dem Telefon kleiner?“ ließ sich nur durch Umschalten und Hinsehen beantworten. Jetzt trägt jede Option drei Chips (D, T, M): sie zeigen, wo ein eigener Wert liegt, und ein Klick wechselt dorthin. Das ist mehr als Flatsome bietet – dort schaltet man um, hier sieht man vorher, ob es überhaupt etwas zu sehen gibt. Bedienfeld und Vorschau wechseln gemeinsam: einen Wert für ein Gerät zu setzen, das man nicht ansieht, ist blindes Ändern.
Abstand als Rahmen. Innenabstand, Außenabstand, Rahmenstärke und Ecken standen als vier Felder untereinander, beschriftet „Oben, Rechts, Unten, Links“ – man musste lesen, um zu wissen, welche Kante man setzt. Jetzt sitzt jedes Feld an seiner Kante, die Lage ist die Beschriftung. In der Mitte verkettet ein Schalter alle vier. Er startet aus: er ersetzt den früheren Knopf „Alle Seiten gleich setzen“, und das war eine bewusste Handlung. Die Einheit bleibt eine Wahl für die ganze Eigenschaft – vier Einheitenmenüs passen nicht in ein 240 Pixel breites Feld. Seiten mit unterschiedlichen Einheiten werden als „gemischt“ geführt und bleiben unangetastet, bis eine Einheit tatsächlich gewählt wird; ein gespeichertes 2rem wird nicht still zu 2px.
Ziehfelder. Zahlenfelder lassen sich seitwärts ziehen. „24“ tippen, schauen, „26“ tippen, schauen, „22“ tippen ist drei Anläufe für eine Entscheidung; Ziehen beantwortet sie in einer Bewegung. Umschalt grob, Alt fein. Tippen und Pfeiltasten bleiben unverändert. Nur für Maus und Stift: auf einem Tippgerät ist ein seitliches Ziehen über ein Feld die Art, wie das Bedienfeld gescrollt wird.
Dabei gefunden und behoben: Der Zug war zunächst nach einem Pixel zu Ende – jede Änderung
zeichnete das Bedienfeld neu und ersetzte damit genau das Feld, an dem der Zeiger hing. Das
Bedienfeld bleibt während eines Zuges stehen und wird einmal neu gezeichnet, wenn losgelassen
wird (keepPanel). Der Kettenschalter verlor aus demselben Grund seinen Zustand, sobald er
gedrückt wurde; er liegt jetzt außerhalb der Darstellung. Und das Gleichziehen nahm den Wert des
ersten Feldes, auch wenn es leer war – ein Druck auf die Kette löschte damit die Seiten, die
einen Wert hatten. Es nimmt jetzt den ersten vorhandenen Wert.
Geprüft. npm test 27 von 27. php vendor/bin/phpunit 452 von 452. npm run test:editor
vollständig mit neuem Abschnitt „device chips, spacing box and scrub fields“: Chips zeigen den
richtigen Zustand und wechseln Bedienfeld und Vorschau zusammen, der Rahmen ändert ohne Kette
nur eine Seite und mit Kette alle vier, die Einheitenumstellung erhält die Zahlen, und ein Zug
über 20 Pixel hebt 72 auf 92 und ist ein Undo-Schritt. Alle axe-Prüfungen ohne Verstoß.
Screenshot unter reports/editor/options.png.
Bekannte Grenzen. Die Chips zeigen einen eigenen Wert, aber nicht, welchen – dafür muss man weiterhin hinwechseln. Auf Tippgeräten gibt es kein Ziehen. Der Rahmen zeigt geerbte Werte als Platzhaltertext, nicht als eigene Kennzeichnung. Noch offen aus Teil 3: Vorschaubilder und Suche im Werkzeugkasten, Vorlagen je Element mit Bild, und das Ziehen der Spaltenbreite in der Vorschau.
1.5.3: der Editor blieb nach dem Update schwarz
Der Fehler. Nach 1.5.2 meldete der Editor im Browser
The requested module './editor-model.js' does not provide an export named 'dropList', und der
Werkzeugkasten war leer.
Ursache war nicht der Code, sondern das Zwischenspeichern. public/.htaccess setzt für jede
.js-Datei Cache-Control: public, max-age=31536000, immutable – ein Jahr. Die Fassungskennung
?v= hängt aber nur an den Einstiegsdateien, die die Vorlage schreibt. Die fünf Module, die der
Editor über import './….js' lädt, tragen keine: der Browser holte das neue editor.js und
behielt das alte editor-model.js. Ein neues canvas-overlay.js traf damit auf ein Modell ohne
dropList.
Beide gemeldeten Symptome hatten diese eine Wurzel: seit 1.5.1 baut das Skript den Werkzeugkasten
auf (buildLibrary()), und editor.js importiert canvas-overlay.js in Zeile 5. Bricht der
Import, läuft keine Zeile danach – und die Elementliste bleibt leer. Das gemeldete „Menü fehlt“
war der Werkzeugkasten, nicht die Verwaltungsnavigation. Die wurde geprüft: in Chrome 152 stehen
::details-content, ausgeblendeter Schalter und sichtbare Liste korrekt.
Die Behebung. Eine Importkarte in base.html.twig hängt die Fassung an die aufgelöste
Adresse jedes Moduls, ohne dass eine einzige Importzeile sie kennen muss. Das behält das
Zwischenspeichern über ein Jahr – die Leistung war nie das Problem, nur die fehlende Kennung.
Eine Importkarte muss eingebettet sein; die Spezifikation kennt keine externe. Unter
script-src 'self' wurde sie wortlos verworfen. AdminHeadersSubscriber erzeugt deshalb je
Verwaltungsseite einen Nonce, nennt ihn in der Richtlinie und legt ihn in die Anfrage; die
Twig-Funktion csp_nonce() gibt ihn der Karte mit. Der Nonce erlaubt genau dieses eine Skript –
'self' gilt unverändert für alles andere.
Geprüft. Der Browsertest schreibt jede angeforderte .js-Adresse mit und verlangt an jeder
eine Fassungskennung; vor der Behebung nennt er die fünf fehlenden beim Namen, danach trägt sie
alle acht. Dazu ein Test, der jeden ./….js-Import in public/assets gegen die Importkarte
hält – ein neues Modul, das dort fehlt, fällt damit im Test auf und nicht erst beim nächsten
Update beim Kunden. Gegenprobe gemacht: mit einem entfernten Eintrag schlägt er fehl.
npm test 28 von 28, php vendor/bin/phpunit 452 von 452, npm run test:editor vollständig.
Bekannte Grenze. Ein Modulfehler legt den Editor weiterhin vollständig lahm, und der Werkzeugkasten ist seit 1.5.1 nichts ohne sein Skript. Für einen Editor ist das vertretbar – die öffentliche Website läuft ohne JavaScript –, aber die Fehleranzeige dafür ist noch die Browserkonsole und kein Satz in der Oberfläche.
Arbeitspaket H, Teil 3b: der Werkzeugkasten bekommt Bilder und eine Suche
Erledigt. Die Elementwahl war eine Liste aus Schriftzeichen – „▣“ für Sektion, „☰“ für Zeile. Jetzt trägt jeder der 34 Einträge eine Strichzeichnung, die zeigt, was er baut: die Sektion ein gefülltes Band, „Zeile, 3 Spalten“ tatsächlich drei Kästen, das Bild einen Rahmen mit Bergzug.
Zeichnungen statt Bilddateien. Flatsome liefert je Element ein PNG mit. Gezeichnet ist hier
besser: library-previews.js ist ein paar hundert Byte statt eines Ordners voller Bitmaps, die
Formen nehmen die Farbe der Fläche an, auf der sie liegen – gesperrte Einträge werden damit von
selbst blass – und sie bleiben auf jedem Bildschirm scharf. Die Zeilenvorschau wird gerechnet,
nicht gezeichnet: zwei, drei oder vier Kästen aus derselben Funktion.
Suche. Ein Feld über der Liste filtert über alle Gruppen. Gesucht wird über den Namen, den internen Typ und den Gruppennamen – wer „slider“ eingibt, muss nicht wissen, dass er unter „Gruppen“ einsortiert ist. Gruppen ohne Treffer verschwinden, damit die Liste keine Lücken behält, und darunter steht, wie viele von wie vielen übrig sind.
Dabei bemerkt: Es gab damit zwei Suchfelder mit dem Namen „Element suchen“ auf einem Bildschirm – für eine Sprachausgabe nicht unterscheidbar. Sie heißen jetzt nach dem, was sie durchsuchen: „Element zum Einfügen suchen“ im Werkzeugkasten, „Element auf der Seite suchen“ im Strukturbaum.
Geprüft. Der Browsertest zählt die Vorschaubilder gegen die Einträge (alle 34 haben eines),
sucht nach „galerie“ (ein Eintrag, eine Gruppe), nach „aufbau“ (fünf Einträge über den
Gruppennamen), nach etwas Nichtvorhandenem und wieder nach nichts. npm test 28 von 28,
php vendor/bin/phpunit 452 von 452, npm run test:editor vollständig ohne axe-Verstoß.
Noch offen aus Teil 3. Vorlagen je Element mit Vorschaubild. Und das Ziehen der
Spaltenbreite: das geht heute nicht ohne einen Zwischenschritt, denn eine Zeile ist ein
CSS-Raster mit gleich breiten Spalten (repeat(n, minmax(0,1fr))); ein span je Spalte
gibt es nicht. Dafür braucht es erst ein Zwölf-Spalten-Modell im Designtoken-Manifest, und das
berührt die öffentliche CSS und damit die Messwerte aus AGENTS.md. Das ist ein eigener Schritt,
kein Nebenbei.
1.5.5: die Verwaltungsnavigation war am Schreibtisch unerreichbar
Der Fehler. In der ausgelieferten Fassung fehlte in der Verwaltung am Schreibtisch die gesamte linke Navigation – Marke oben, Fußzeile unten, dazwischen nichts. Kein Weg zu Seiten, Mediathek, Einstellungen außer über die Adresszeile.
Die Navigation steckt in einem <details>: auf dem Telefon ein Klappmenü, am Schreibtisch die
stehende Spalte. Am Schreibtisch wurde der Schalter mit .shell-menu > summary { display: none }
ausgeblendet – aber das Element trug kein open, und ein geschlossenes details versteckt seinen
Inhalt selbst. Schalter weg und Inhalt weg.
Die Behebung. ::details-content holt den Inhalt am Schreibtisch zurück, während der
Schalter ausgeblendet bleibt. Kennt ein Browser die Regel nicht, fällt über @supports die ganze
Gruppe weg: dann steht der Schalter da und die Liste ist einen Klick entfernt – statt gar nicht
da. Auf dem Telefon bleibt es beim Klappmenü.
Der Code dafür lag bereits im Arbeitsverzeichnis: eine parallel laufende Sitzung hatte ihn geschrieben, aber nicht committet, und damit war er in keinem Release. Übernommen ist hier nur diese Korrektur, nicht die übrige Arbeit jener Sitzung.
Wie das durchrutschen konnte – und was dagegen getan ist. Beim vorigen Bericht wurde die
Navigation für in Ordnung erklärt, nachdem eine Nachbildung der Regel in einer leeren Seite
geprüft worden war. Die Nachbildung enthielt die Korrektur, die ausgelieferte Datei nicht; der
Befund war damit wertlos. Jetzt prüft der Browsertest die echte Verwaltungsseite: am
Schreibtisch kein Schalter, Navigation sichtbar, der Eintrag „Seiten“ mit messbarer Fläche; auf
390 px Breite ein Schalter, Liste zunächst zu, ein Klick öffnet sie. Gegenprobe gemacht – gegen
die ausgelieferte admin.css schlägt der Test fehl.
Geprüft. npm run test:editor vollständig, npm test 28 von 28,
php vendor/bin/phpunit 452 von 452.
1.6.0: der Aufgabenplan wird veröffentlicht
Diese Fassung bringt den Aufgabenplan ohne Cronjob (ADR 073, oben beschrieben) in ein Release. Er entstand in einer parallel laufenden Sitzung und lag bis hierher unveröffentlicht im Arbeitsverzeichnis – die Releases 1.5.0 bis 1.5.5 wurden bewusst aus dem jeweiligen Tag gebaut und enthielten ihn deshalb nicht.
Vor der Veröffentlichung geprüft, nicht bloß eingepackt:
php vendor/bin/phpunit452 von 452,npm test28 von 28,npm run test:editorvollständig.- Die Migration
20260914000100legt nur die Tabellescheduled_taskan und nimmt sie beim Zurückrollen wieder weg. Sie fasst keine bestehenden Daten an – das ist die risikoarme Sorte. - Seit einer Stunde keine Schreibzugriffe der anderen Sitzung mehr; der Stand ist ruhig.
Dabei zwei Fehler behoben:
config/services.yamlverwendet!tagged_iterator. Das ist gültiges Symfony, aberphp bin/console lint:yaml configkennt eigene Tags nur mit--parse-tagsund gab 1 zurück – der CI-Lauf in.github/workflows/ci.ymlwäre daran gescheitert. Das Flag ist ergänzt.src/EventSubscriber/AdminHeadersSubscriber.php(aus 1.5.3) entsprach nicht dem Stil vonphp-cs-fixer;composer lintgab 8 zurück. Behoben, jetzt 0 von 128 Dateien zu ändern.
Nächster konkreter Schritt
Arbeitspaket H, Teil 3b: Vorschaubilder und Suche im Werkzeugkasten, Vorlagen je Element mit
Vorschaubild und das Ziehen der Spaltenbreite in der Vorschau. Danach
Arbeitspaket I: banner, textbox, stack, imagebox, logo, messagebox. Reihenfolge und
Begründung in docs/FLATSOME-ABGLEICH.md.
Unverändert offen: den Editor mit einem Screenreader prüfen und die übrigen Verwaltungsbildschirme in die automatische Barrierefreiheitsprüfung aufnehmen. Danach der Rest von Arbeitspaket G (Inline-Ausbau, globale Bereiche für Header und Footer, wiederverwendbare Sektionen). Danach Arbeitspaket F aus docs/AUSBAUPLAN.md: Seiten umbenennen und verschieben mit Aktualisierung aller Kindpfade und automatischer Weiterleitung, frei wählbare Startseite, geplantes Veröffentlichen, Revisionsliste mit Wiederherstellung und ein Papierkorb statt sofortigem Löschen.
kant-it.de ist seit 12.09.2026 als Pilotwebsite bestimmt. Der öffentliche Bestand wurde erhoben (WordPress 7.1, Flatsome 3.20.9 mit eigenem Child-Theme, WPForms Lite, Complianz, Google-Rezensionen, eigenes Plugin kant-nova-chat) und die noch offene Arbeit daraus in docs/AUSBAUPLAN.md in elf Arbeitspakete aufgeschlüsselt. Erhoben wurde ausschließlich die öffentlich ausgelieferte Startseite; ohne Backendzugang sind Beitragsbestand, Formularkonfiguration, Benutzer und Flatsome-Shortcodes nicht inventarisierbar. Keine externe Veröffentlichung erfolgt.
Lokale Vorschau
Öffentlich: http://127.0.0.1:8080/. Verwaltung: http://127.0.0.1:8080/admin. PHP 8.4.22 im Symfony-Produktionsmodus; der Browser wurde als umut angemeldet. Der Prozess ist sitzungsabhängig; zum erneuten Start README verwenden.