Ihr Malerservice Drews (Gernsbach) as its own installation: seed script with theme, header, footer, eight pages, 31 posts in two categories, contact form, consent-gated map and videos, redirects; the wording lives in a generated content file. Reset script, launch entry on port 8093, Cormorant Garamond served locally with its OFL text. Two CMS fixes found while building: wide and full sections lost against the theme's .kant .shell max-width, and the scroll-top link sat outside every landmark (axe "region"). Lighthouse on six pages: accessibility, best practices, SEO and agentic browsing 100, performance 98-100; axe zero violations on nine pages at 1440 and 390 px. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
50 KiB
KantSites
Lokaler CMS-Prototyp für PHP-Webhosting mit erstem visuellen Editor. Symfony 7.4, Twig, Doctrine DBAL/Migrations; SQLite lokal, MySQL/MariaDB als Hostingziel. Drei gespeicherte Beispielseiten werden ohne JavaScript als HTML ausgeliefert. Anmeldung, geschützte Bearbeitung und getrennte Veröffentlichung sind implementiert; weitere Layoutwerkzeuge folgen.
Die übernommenen Originaldokumente sind AGENTS.md und ENTWICKLUNGSPLAN.md. Den tatsächlichen Stand enthält docs/STATUS.md, Entscheidungen und Datenmodell stehen in docs/ARCHITECTURE.md.
Spaltenbreiten einer Zeile durch Ziehen (ab 1.16.0)
Wähle eine Zeile oder eine ihrer Spalten: auf jeder Grenze zwischen zwei Spalten liegt ein Griff. Ziehen macht die eine Spalte breiter und die andere schmaler, Loslassen ist ein Schritt für „Rückgängig“; die Pfeiltasten verschieben um fünf Prozentpunkte. Der Wert steht auch im Design-Bedienfeld der Zeile unter Layout → Spaltenbreiten, etwa 2 1 oder 25 50 25, und gilt je Gerät. Wo die Spalten gestapelt sind, etwa auf dem Handy, gibt es keinen Griff und die Spalten bleiben untereinander.
Eigene Absenderadresse je Formular (ab 1.15.1)
Unter Formulare → Zustellung gibt es neben dem Empfänger ein Feld Absender. Bisher ging jede Nachricht von der Empfängeradresse an sich selbst; das scheitert, sobald der Empfänger ein Postfach bei einem anderen Anbieter ist, weil viele Hoster nur zustellen, was von einer Adresse der eigenen Domain kommt. Leer heißt weiterhin: Absender ist der Empfänger. Die Antwortadresse aus dem Formularfeld bleibt davon unberührt.
Formulare, Statistik, Design und Seitenleiste überarbeitet (ab 1.15.0)
Formulare sind ein Editor: jedes Feld eine Karte mit Nummer, Beschriftung, Typ, technischem Namen, Hilfetext und Pflicht-Schalter, dazu „Feld hinzufügen“ und ein Entfernen-Knopf je Karte. Der technische Name folgt der Beschriftung, bis du ihn selbst schreibst. Unter Aussehen bekommt jedes Formular eine Buttonbeschriftung sowie Rahmen- und Hintergrundfarbe der Felder, Button- und Buttonschriftfarbe – aus der Palette oder als eigener Wert, leer heißt „wie die Website“. Die Vorschau daneben folgt jeder Eingabe, zeichnet den Absendeknopf mit dem Standardbutton des Designs und nennt den Kontrast der Buttonschrift.
Die Statistik beginnt mit Kennzahlen: Aufrufe mit Vergleich zum gleich langen Zeitraum davor, Schnitt je Tag, stärkster Tag, aufgerufene Seiten, Aufrufe über Verweise, nicht gefundene Adressen. Der Verlauf hat Rasterlinien, jeder Balken nennt beim Überfahren Tag und Zahl, der stärkste Tag ist hervorgehoben; neu ist ein Wochentagsdiagramm im Schnitt. Die Ranglisten tragen Rang und Anteilsbalken.
Im Design ist die Palette eine Reihe von Farbkarten, deren Fläche der Farbwähler selbst ist. Sieben fertige Paletten (Wald, Ozean, Sand, Koralle, Schiefer, Olive, Nacht) lassen sich per Klick übernehmen; eigene Farben bleiben stehen. Text-, Seiten- und Linkfarbe wählst du aus der Palette oder trägst einen eigenen Wert ein. Jede Schrift zeigt eine Probe in sich selbst, die Schnitte sind Chips, und eine Typografie-Vorschau zeigt Schrift, Größe, Zeilenhöhe, Skala, Farben und Unterstreichung live.
Die Seitenleiste endet in einer Kontokarte mit Avatar, Name, Rolle und Abmeldesymbol, darunter die Fassung mit dem Update-Hinweis. Auf Tablets bleiben Konto und Abmelden rechts im Bild, nur die Liste wischt.
Fußzeile des Einwilligungshinweises (ab 1.14.2)
„Einzeln wählen“ steht links, Datenschutz und Impressum rechts auf einer Linie; aufgeklappt nimmt die Einzelwahl die volle Breite, die Links rutschen rechtsbündig darunter.
Einwilligungshinweis gestalten, Nach-oben-Knopf auch bei offener Frage (ab 1.14.1)
Der Einwilligungshinweis lässt sich gestalten, unter Einstellungen → Einwilligung → Aussehen des Hinweises: Anordnung (Karte unten in der Mitte, Leiste über die volle Breite, kleine Karte rechts unten), Form (abgerundet, rund, eckig – gilt für Fläche und Knöpfe zusammen), eigene Überschrift und vier Farben (Fläche, Schrift, Knopf „Alle akzeptieren“, Schrift auf dem Knopf), jeweils als #hex oder @schlüssel aus der Palette. Leer heißt Papier, Tinte und Akzent aus dem Design. Die Fläche trägt einen schmalen Streifen und ein Symbol in der Knopffarbe; „Nur notwendige“ bleibt ein gleich großer Rahmenknopf daneben. Dieselben Farben gelten auf /einwilligung und in den Beitragsarchiven.
Der Nach-oben-Knopf war ausgeblendet, solange die Einwilligungsfrage offen stand – auf dem Telefon bleibt sie das oft, und der Knopf kam dort nie. Jetzt setzt er sich auf die Oberkante der Leiste, wo sie die Ecke abdeckt; Browser ohne Ankerpositionierung blenden ihn dort weiter aus. Der reservierte Abstand unten reicht auf schmalen Bildschirmen wieder für die ganze Leiste.
Im Designbereich sind belegte Eigenschaften jetzt in Orange beschriftet, nicht nur mit Punkt und Fettdruck; die Gerätechips D/T/M stehen weiter auseinander, und der Fokusring bleibt in der Lücke.
Schriften vom eigenen Server, Hinweise auf jedem Bildschirm (ab 1.14.0)
Schriften kommen ausschließlich von deinem Server. Die Ausgabe verlinkt kein Stylesheet mehr von fonts.googleapis.com – weder die öffentliche Seite noch die Editorvorschau, die es bisher bei jedem Aufruf tat. Fehlen die Dateien, zeigt die Website die eingestellte Ersatzschrift und sagt es dir. Geholt werden sie wie bisher einmalig unter Design → Schriften ausliefern. Zusätzlich wird jetzt jeder Schnitt des Designs vorab geladen: bisher waren es die ersten zwei je Familie, weshalb die Überschriften sichtbar von der Ersatzschrift auf die richtige Schrift sprangen.
Offene Hinweise stehen auf jedem Verwaltungsbildschirm, mit dem Knopf daneben: „Fassung X ist verfügbar“ → Jetzt aktualisieren, „Schriftdateien fehlen“ → Schriften holen. Keiner der beiden fragt beim Öffnen im Netz nach.
System und Update ist auf dem Telefon benutzbar: Die Seite lief seitlich aus dem Bild, die Sicherungstabelle wird zu gestapelten Blöcken, und die Texte sind knapper – das Kleingedruckte steht zugeklappt.
Im Designbereich zeigen die Gerätechips D/T/M jetzt in Orange, welches Gerät einen eigenen Wert trägt; das gerade bearbeitete Gerät hat einen Rahmen statt des bisherigen roten Rings, der am Rand der Spalte abgeschnitten wurde.
Öffnungszeiten, Einsatzgebiet und FAQ in den strukturierten Daten (ab 1.14.0)
Unter Einstellungen → Unternehmensdaten gibt es Öffnungszeiten (je Wochentag Öffnen und Schließen; leer heißt geschlossen) und das Einsatzgebiet (ein Ort je Zeile). Beides erscheint in den strukturierten Daten der Startseite (openingHoursSpecification, areaServed); gleiche Zeiten werden zu einer Angabe zusammengefasst. Der sichtbare Text auf der Website bleibt unabhängig davon.
Ein Akkordeon wird per Kästchen Als FAQ auszeichnen zur FAQPage: die Titel sind die Fragen, die Textbausteine im Eintrag die Antwort. Einträge ohne Text erscheinen nicht. Das gilt nur für Akkordeons mit gesetztem Kästchen – ein Akkordeon mit Leistungen bleibt ein Akkordeon.
Mehr Symbole, Menü-Knopf gestalten, Nach-oben-Knopf zuverlässig (ab 1.13.0)
Der Symbolsatz hat drei Zeichnungen mehr: Menschen, Musik und Haus. Sie stehen überall zur Wahl, wo es Symbole gibt – Symbolbaustein, Symbolkarte und Menüeinträge. Eine am Symbolbaustein eingestellte Farbe wirkt jetzt auch wirklich auf die Zeichnung; bisher blieb sie in der Akzentfarbe der Website stecken.
Der Menü-Knopf (der aufklappbare „Menü“-Knopf auf Tablet und Telefon) lässt sich gestalten: Navigation auswählen → Inhalt → Menü-Knopf wählt zwischen Symbol und Text, Nur Text und Nur Symbol; „Nur Symbol“ behält den Namen für Vorlesesoftware. Unter Design → Menü-Knopf stehen Schriftfarbe, Fläche, Rahmenfarbe, Rahmenstärke, Ecken und Schriftgröße, je Gerät und mit Palettenfarben. Das aufgeklappte Menü legt sich im Kopfbereich als Fläche über die volle Breite unter den Kopf, statt die Kopfzeile auseinanderzuschieben.
Der Nach-oben-Knopf erscheint wieder überall. Er blendete sich über eine Scroll-Animation ein; Browser, die diese Eigenschaft zwar kennen, aber nicht ausführen, ließen ihn dauerhaft unsichtbar. Jetzt entscheidet eine Klasse, die das kleine Scroll-Skript setzt – und ohne JavaScript bleibt der Knopf einfach sichtbar.
Soziale Profile (ab 1.12.0)
Unter Einstellungen → Unternehmensdaten → Soziale Profile je Netzwerk eine Adresse eintragen (Facebook, Instagram, LinkedIn, YouTube, TikTok, X, Threads, Pinterest, Xing, WhatsApp, Google-Unternehmensprofil). Das Element Soziale Profile (Werkzeugkasten → Bausteine) zeigt sie dann an jeder Stelle – im Kopf- oder Fußbereich, in einer Kontaktsektion – und bestimmt dort Größe, Form (nur Symbol, runde oder eckige Fläche), Anordnung, Beschriftung und ob nur bestimmte Netzwerke erscheinen. Farbe, Abstand und Ausrichtung wie bei jedem Element unter Design. Eine Adresse ändern heißt: überall geändert. Die Profile stehen automatisch in den strukturierten Daten (sameAs). Ohne Eintrag zeigt das Element auf der Website nichts.
Slider-Steuerung auf dem Bild (ab 1.11.4)
Die Steuerung liegt jetzt auf dem Slider, wie beim Hero-Slider von kant-it.de: runde weiße Pfeile links und rechts in der Mitte, Punkte unten rechts mit dem aktiven Punkt als Strich in der Akzentfarbe, ein kleiner Pause-Knopf daneben, kein Zähler mehr (der bleibt für Vorlesesoftware). Auf Bildschirmen bis 600 px verschwinden die Pfeile, gewischt wird mit dem Finger. Unter Design → Slider-Steuerung gibt es zusätzlich „Aktiver Punkt“.
Farben der Slider-Steuerung (ab 1.11.3)
Slider auswählen → Design → Slider-Steuerung: Farbe der Punkte, Farbe von Pfeilen und Zähler sowie Hintergrund der Knöpfe, je Gerät und mit Palettenfarben. Ohne eigene Werte sind die Punkte halbtransparent weiß und die Pfeile weiß mit Akzentfarbe. Die Farben gelten auch für die skriptfreien Punkte, die der Browser selbst zeichnet.
Slider auf dem iPhone (ab 1.11.2)
Safari bricht sanftes Weiterblättern in einem einrastenden Slider ab und springt zur vorigen Folie zurück; die Automatik und die Pfeile stotterten deshalb auf dem iPhone. Während der Fahrt ist das Einrasten jetzt ausgeschaltet und wird bei Ankunft auf der Folie wieder eingeschaltet. Die Punkte unter dem Slider sind echte Punkte, der aktive gefüllt, bei unveränderter Trefffläche von 44 px.
Kopfbereich beim Scrollen und gleich hohe Karten (ab 1.11.1)
Der Kopfbereich kann für die gescrollte Seite eigene Farben tragen, wie der transparente Header in Flatsome: Kopfbereich im Editor öffnen, die Sektion wählen, Design → Hintergrund. Dort steht unter „Hintergrundfarbe“ die Zeile „Hintergrundfarbe beim Scrollen“; dasselbe gibt es für Textfarbe, Rahmenfarbe, Schattenfarbe und Deckkraft. Der normale Wert gilt am Seitenanfang, der Scroll-Wert sobald die Seite mehr als 40 px bewegt wurde, mit weichem Übergang. Für einen durchsichtigen Kopf am Anfang #ffffff00 als normale Hintergrundfarbe eintragen. Damit der Kopf über der ersten Sektion liegt statt darüber zu stehen, unter Einstellungen „Kopfbereich über dem Inhalt schweben lassen“ einschalten; zusammen mit „oben halten“ bleibt er beim Scrollen sichtbar. Ohne JavaScript behält der Kopf seine normalen Farben.
Karten, Kundenstimmen und Aufruf-Bänder nebeneinander in einer Zeile sind wieder gleich hoch. Der Abstand, den gestapelte Karten in einer Spalte untereinander brauchen, hatte in der Zeile die zweite bis letzte Karte um 18 px niedriger gemacht.
Editor mit einer Seitenleiste und Bewegung wie Flatsome (ab 1.11.0)
Der Editor folgt jetzt dem Aufbau des Flatsome UX Builders: eine dunkle Kopfzeile mit Gerätewahl in der Mitte, Rückgängig/Wiederholen, Bewegung abspielen, Speichern und Veröffentlichen; links eine Seitenleiste mit den Reitern Elemente, Struktur und Seite; rechts die Vorschau. Ein Klick auf ein Element in der Vorschau öffnet seine Optionen in der Seitenleiste: Name, Pfad (Seite › Sektion › Zeile), die Reiter Inhalt/Design/Erweitert, unten Verschieben, Duplizieren, Löschen. ‹ wählt das übergeordnete Element, + fügt in einen Container ein oder direkt hinter das Element, ✕ oder Escape hebt die Auswahl auf. Rechtsklick in der Vorschau öffnet ein Kontextmenü mit denselben Aktionen. Entf löscht das gewählte Element, sofern kein Feld den Fokus hat.
Die Elemente stehen als Kartenraster mit Symbol und Namen; was an der gewählten Stelle nicht passt, ist ausgegraut und sagt beim Überfahren warum. In der Struktur wählt ein Klick nur aus, Bearbeiten oder Doppelklick öffnet das Element. Die Sichtbarkeit je Gerät steht jetzt am Ende von Inhalt und Erweitert. Die verstellbaren Panelbreiten entfallen.
Bewegung: Einblendungen, Hover-Bewegungen und Slider-Automatik spielen standardmäßig bei jedem Besucher, wie bei Flatsome und den meisten Websites. Manche Browser melden „Bewegung reduzieren“, ohne dass jemand es eingestellt hat; bisher blieb dann jede Einblendung aus. Unter Einstellungen → Einblendungen und Hover-Bewegungen lässt sich „Browservorgabe beachten“ wählen; dann trägt <html> die Klasse motion-respect, und Skript wie CSS halten sich wieder an die Vorgabe. Einblendungen verstecken einen Block, bis er ins Bild scrollt, und blenden ihn dann von unsichtbar ein; wer per Sprungmarke oder schnellem Scrollen an einem Block vorbeikommt, sieht ihn sofort. Ohne JavaScript ist alles sichtbar.
Gezielt prüfen (isolierte Kopien; PHP mit SQLite3-Erweiterung):
$env:PHP_BINARY = 'C:\php84\php.exe'
node scripts/motion-check.mjs
node scripts/editor-check.mjs
npm.cmd test
Formularbutton und Bewegungseffekte (ab 1.10.1)
„Absenden“ übernimmt den Website-Standardbutton aus Design/Typografie: Form, Darstellung, Größe, Schrift und normale/Hover-Farben. Bestehende Formulare aktualisieren sich ohne erneutes Speichern; der Button hat seine natürliche Breite statt automatisch die gesamte Formularbreite. Formularfelder, Versand und Validierung bleiben unverändert.
Einblendungen starten nach dem Wechsel von reduzierter zu erlaubter Bewegung wieder ohne Neuladen. Bereits gezeigte Elemente werden nicht erneut animiert. Im Editor zeigt eine Meldung, wenn der Browser Bewegung reduziert. Eine weiterhin aktive Browservorgabe wird beachtet; dieses Update überschreibt keine Besucherpräferenzen.
Sichtbarkeit und Hamburger-Menüs (ab 1.10.0)
Element auswählen → Sichtbarkeit: auf allen Geräten, nur Desktop/Tablet/Mobil, auf einem Gerät ausblenden oder überall ausblenden. Die Auswahl steht in Inhalt, Design und Erweitert. Ausgeblendete Elemente bleiben links in der Struktur auswählbar; dort steht auch ihre Sichtbarkeit. „Auf allen Geräten“ setzt die Gerätebeschränkung zurück. Eigene Layoutwerte bleiben erhalten. Ausgeblendete Elternbereiche verbergen auch ihre Inhalte.
Waagerechte Navigation wird automatisch auf Tablet und Mobil zum Hamburger, auch auf großen iPads mit Touchbedienung ohne Maus. Unter Inhalt → Automatisch als Hamburger lässt sich stattdessen „Nur Mobil“ oder „Nie“ wählen. Vertikale Menüs bleiben vertikal und die Menüart „Burger“ bleibt immer aufklappbar. Bereits getrennt angelegte Desktop- und Mobilmenüs lassen sich über Sichtbarkeit passend zuordnen. Das Menü funktioniert ohne JavaScript und lässt sich mit Enter oder Leertaste öffnen und schließen.
Sichtbarkeitsgrenzen: Desktop ab 1025 px, Tablet 768–1024 px, Mobil bis 767 px. Undo/Redo, Speichern und Veröffentlichung gelten wie gewohnt. Neue Optionen verwenden Schema 22; Rückweg siehe ADR 088.
Gezielt prüfen (isolierte Kopie der lokalen Kant-IT-Daten; PHP mit SQLite3-Erweiterung):
$env:PHP_BINARY = 'C:\php84\php.exe'
npm.cmd run test:responsive-navigation
php vendor/bin/phpunit tests/ResponsiveVisibilityTest.php
npm.cmd test
Bericht und Screenshots: reports/responsive-navigation/. Produktionsbuild: composer build.
Buttons, Sprungziele und Menüs (ab 1.9.0)
Unter Design/Typografie konfiguriert „Standardbutton“ Form, Darstellung, Größe, Schrift und Farben für die Website. Acht Vorlagen zeigen das Ergebnis direkt, einschließlich Hover-Vorschau. Einzelne Buttons können im Editor davon abweichen, auch je Gerät: eckig, abgerundet, Pille, Rahmen, dezent oder Textlink. Eigene freie Designwerte können diese Vorgaben übersteuern.
Sektionen, Zeilen und Spalten haben im Inhaltsbereich das Feld „Sprungziel“. Dort beispielsweise kontakt eintragen und einen Link auf #kontakt setzen. Ziele sind eindeutig; die internen Block-IDs bleiben erhalten. Duplikate erhalten kein kopiertes Sprungziel.
Menüeinträge am Punktgriff ziehen oder per Pfeiltasten verschieben und einrücken. Optional ein Symbol vor oder nach dem Text auswählen. Änderungen anschließend mit „Menü speichern“ übernehmen. Neben der Fassung links erscheint für Administratoren ein Link, sobald die bestehende tägliche Updateprüfung eine neuere Version gefunden hat. Der Aufgaben-Worker muss dafür laufen; die Sidebar selbst führt keine Netzwerkanfrage aus.
Gezielte Entwicklungsprüfungen: npm run test:buttons und npm run test:navigation-ux verwenden eine isolierte Kant-IT-Kopie. Responsive Button-CSS nach Änderungen mit npm run build:buttons erzeugen.
Lokal starten
Voraussetzungen: PHP ab 8.2 mit Composer 2, PDO SQLite, Ctype, Iconv, Session, XML und GD mit WebP-Unterstützung. EXIF wird für die automatische Ausrichtung von JPEG-Fotos empfohlen. Für Tests zusätzlich die von composer install geprüften Erweiterungen, insbesondere DOM, XMLWriter und Mbstring. Für Hosting eine aktuell gepflegte PHP-Patchversion verwenden. Node 24 wird für Browserprüfungen und das Generieren der Design-CSS gebraucht, nicht im Hostingbetrieb.
composer install
php bin/setup.php
php vendor/bin/doctrine-migrations migrate --no-interaction
php bin/console cms:seed-demo
php -d upload_max_filesize=8M -d post_max_size=10M -d memory_limit=256M -S 127.0.0.1:8080 -t public bin/dev-router.php
Danach http://127.0.0.1:8080/ öffnen. Der Routerskript-Parameter ist nicht optional: der eingebaute PHP-Server liefert vorhandene Dateien selbst aus und antwortet für jeden fehlenden Pfad mit Dateiendung mit 404, sodass /sitemap.xml, /robots.txt, /llms.txt und /feed.xml den Frontcontroller nie erreichen. Auf echtem Hosting erledigt das public/.htaccess; bin/dev-router.php bildet dasselbe lokal nach. Unter Windows hier vorhanden: C:\xampp\php\php.exe (8.2.12) und C:\php84\php.exe (8.4.22). Die Befehle verwenden die PHP-Version im PATH; alternativ den vollständigen Pfad einsetzen. Diese vorhandenen Patchstände sind kein empfohlenes Produktionsimage.
bin/setup.php erzeugt ein zufälliges Secret und var/. Die Konfiguration steht ausschließlich in der nicht versionierten .env; vorhandene Einstellungen und Secrets bleiben erhalten. Die Datenbank liegt in var/cms.db. Wiederholtes Seeding erhält existierende Seiten einschließlich ihrer Bearbeitungen. Standardzugangsdaten gibt es nicht.
Installation ohne Konsole läuft über /install: Systemprüfung, Datenbank, Migration, Website, erstes Konto. Der Installer antwortet nur, solange keine Installation existiert – sobald ein Konto angelegt ist, liefert jede Adresse darunter 404, und die Sperre hängt am Zustand der Datenbank, nicht an einer Datei. Solange nichts installiert ist, führt jede Adresse dorthin. Davor genügt eine einzelne hochgeladene Datei: install.php aus einem Release lädt das Archiv von einer fest eingetragenen Adresse, vergleicht die SHA-256, entpackt es und löscht sich selbst. Ohne passende Prüfsumme wird nichts entpackt. Editor-Endpunkte benötigen Anmeldung, Berechtigung und bei POST einen CSRF-Token.
Eigene 404-Seite (ab 1.8.0)
Unter Einstellungen → 404-Seite eine vorhandene Seite per Livesuche auswählen und 404-Zuordnung speichern. Oder 404-Vorlage anlegen anklicken, den angelegten Entwurf im Editor gestalten und veröffentlichen. Die Ausgabe übernimmt den Standard-Header, Standard-Footer, Schriften und Farben der Website. 404-Vorschau öffnet die tatsächlich sichtbare Fehlerseite.
Ohne veröffentlichte Auswahl erscheint automatisch eine Standard-404 im Website-Design. Entwürfe bleiben privat; Umbenennen erhält die Zuordnung, nach dem Löschen greift wieder der Standard. Das Entfernen der Auswahl setzt die Zuordnung zurück und erhält den Seiteninhalt. Die Zuordnung benötigt Veröffentlichungsrechte. Unbekannte URLs bleiben unverändert und antworten mit HTTP 404; die Fehlerseite wird nicht in Sitemap/llms.txt aufgenommen. Prüfung mit isolierter Kant-IT-Kopie: npm run test:404, Berichte und Screenshots unter reports/not-found/. Datenvertrag und Rückweg: ADR 082.
Anmelden und bearbeiten
Scroll-Einblendungen prüfen: Block auswählen → Design → Bewegung und Effekte → Einblendung beim Erscheinen → „Von links“ oder „Von rechts“. Anschließend speichern und veröffentlichen. Im Editor startet „Bewegung testen“ die Vorschauwiedergabe. Öffentliche Seiten animieren beim ersten Erscheinen; die Systemoption „Bewegung reduzieren“ unterdrückt Animationen. node scripts/motion-check.mjs prüft Scroll-Auslösung, beide Richtungen, Bewegungsreduktion und Ausgabe ohne JavaScript mit einer isolierten Kant-IT-Kopie. PHP mit SQLite3-Unterstützung über PHP_BINARY wählen, etwa in PowerShell $env:PHP_BINARY='C:\php84\php.exe'. Ergebnisse stehen unter reports/motion/.
Bildausrichtung (ab 1.8.4): Bild auswählen → Design → Layout → Eigene Ausrichtung quer: Mitte. Zentriert den Bildblock und auch kleinere Bilder innerhalb des Rahmens. Funktioniert mit Prozentbreite, maximaler Breite und Bildlinks; Anfang/Ende und Gerätewerte werden ebenfalls berücksichtigt. In nebeneinander angeordneten Flex-Gruppen bleibt die Querachse vertikal. Prüfen: npm run test:image-alignment auf einer isolierten Kant-IT-Kopie; Screenshots unter reports/image-alignment/.
Akkordeon-Inhalte (ab 1.8.3): Einträge bleiben im Editor geöffnet. Im Eintrag auf Inhalt hinzufügen klicken und links Text, Bild oder ein anderes Element auswählen; alternativ direkt in die sichtbare Inhaltsfläche ziehen. Eintrag hinzufügen steht unter dem Akkordeon und in dessen Inhaltsoptionen. Die Eintragsübersicht rechts und die Struktur links zeigen die Titel. Beim Laden geöffnet bestimmt ausschließlich den Zustand der veröffentlichten Seite. Prüfen: npm run test:accordion, Screenshots unter reports/accordion/.
Farbauswahl im Editor (ab 1.8.2): Unter Design zeigt jedes Farbfeld eine Vorschau mit Palettenname und Hexwert. Anklicken öffnet die Website-Farben als Farbkacheln. Eigene Farben über den Farbwähler oder als Hexwert eingeben und mit Enter/✓ übernehmen. Transparente Hexfarben werden auf einem Schachbrett dargestellt. „Geerbten Wert verwenden“ entfernt die eigene Überschreibung für das aktive Gerät. Prüfen: npm run test:colors mit isolierten Kant-IT-Daten; Screenshots unter reports/color-picker/.
Footer-Links (ab 1.8.1): Datenschutz, Impressum und weitere Links werden selbst im Footer oder Seiteninhalt platziert. Das CMS hängt keine zusätzliche Rechtszeile an. Der optionale Baustein „Rechtliches“ verwendet die Adressen aus den Einstellungen und erscheint nur an seiner eingefügten Position.
Für eine neue Installation im interaktiven Terminal ein Konto anlegen:
php bin/console cms:user:create meinname
Das Passwort wird verdeckt zweimal abgefragt (14–72 Bytes). Bestehende Benutzer werden nicht überschrieben. Optional --role=editor für Entwürfe oder --role=publisher für Entwürfe plus Veröffentlichung; Standard ist admin. Es gibt noch keine Benutzerverwaltung oder Passwortänderung im Browser. In der aktuellen lokalen Installation wurde auf Nutzerwunsch bereits der Administrator umut angelegt und angemeldet; keine Zugangsdaten sind im Repository abgelegt.
Unter http://127.0.0.1:8080/admin eine Seite auswählen. Links Blöcke hinzufügen/auswählen, in der Mitte die Vorschau, rechts Eigenschaften. Die Auswahl ist zwischen Strukturbaum und Vorschau synchron. Sektion, Zeile, Spalte, Überschrift, Text, Bild, Button und Galerie sind vorhanden. Bilder können aus der Mediathek oder über vorhandene lokale/HTTPS-Adressen eingebunden werden.
Bild prozentual verkleinern (ab 1.7.2): Bild auswählen → Inhalt → Bildbreite (%). Regler oder Zahlenfeld setzen 1–100 % der verfügbaren Breite in Sektion/Spalte. Desktop, Tablet und Mobil können eigene Werte erhalten; kleinere Geräte übernehmen zunächst den Wert vom größeren Gerät. „Breite zurücksetzen“ entfernt den eigenen Wert und stellt die Vererbung beziehungsweise automatische Breite wieder her. Originalbreite/-höhe beschreiben weiterhin die Datei. Der Regler bearbeitet denselben Wert wie Design → Größe → Breite; eine dort gesetzte maximale Breite bleibt wirksam. Prüfung mit kopierten Kant-IT-Daten: npm run test:image-size, Bericht und Screenshots unter reports/image-size/.
Links und Bildvorschauen (ab 1.7.3): Unter Linkziel steht In neuem Tab öffnen – für Buttons, verlinkte Bilder, Symbole, Symbolkarten und Aufruf-Bänder. Im Text zuerst den Link über die Werkzeugleiste öffnen; bei Menüeinträgen steht das Häkchen in den aufgeklappten Einstellungen. Ausgewählte Bilder und Galerie-/Logobilder zeigen eine Vorschau mit Entfernen-Funktion. „Bild entfernen“ entfernt das Bild aus dem Inhalt; die Datei bleibt in der Mediathek. Rückgängig stellt die Verwendung wieder her. Design → Hintergrund zeigt auch geerbte Bilder: Entfernen blendet das Bild auf dem aktiven und kleineren Geräten aus, Zurücksetzen stellt die Vererbung wieder her. Bildaustausch erhält Link, Bildunterschrift und eingestellte Größe. Prüfung mit isolierter Kant-IT-Kopie: npm run test:link-media, Bericht und Screenshots unter reports/link-media/. Datenvertrag und Rückweg: ADR 081 in docs/ARCHITECTURE.md.
Blöcke per Drag-and-drop verschieben: auf einen Container ziehen, um hineinzuverschieben; auf einen Text-/Bild-/Linkblock ziehen, um davor einzufügen. Alternativ unten am Strukturbaum die Schaltflächen für Auf/Ab, Verschachteln und Herausheben verwenden. Duplizieren vergibt neue Block-IDs und wandelt eine kopierte H1 in H2 um. Löschen lässt sich rückgängig machen. Jede speicherbare Seite benötigt genau eine H1.
Undo/Redo gilt für die lokale Bearbeitung, Strg+S speichert den Entwurf. Strg+Z/Strg+Umschalt+Z gelten außerhalb der Texteingaben für das Dokument; Texteingaben behalten ihr natives Undo. Erst speichern, dann separat veröffentlichen. Die Bestätigungsdialoge unterstützen Escape und Fokus-Rückkehr. Reine Redakteure können nicht veröffentlichen.
Ein Speicherkonflikt überschreibt nichts: lokale Änderungen als JSON sichern und den Serverstand bewusst neu laden. Auch Netzwerk-/Sitzungsfehler lassen den lokalen Stand erhalten. Die Vorschau behält bei ungültigen Eingaben die letzte gültige Ausgabe.
Rechts zwischen Inhalt und Design wechseln. Unter Design je nach Block Abstände, Schriftgröße/-stärke, Zeilenhöhe, Ausrichtung, Spalten und Ecken wählen. Oben Desktop/Tablet/Mobil bestimmt das bearbeitete Gerät: Desktop ist die Basis, Tablet überschreibt bis 1024 px, Mobil bis 767 px. „Übernehmen“ entfernt einen eigenen Wert und stellt die Vererbung wieder her. Gerätewerte lassen sich gemeinsam zurücksetzen und rückgängig machen. Farben und Inhaltsbreite einer Sektion stehen unter Inhalt.
Die Vorschau rendert auf Desktop mit mindestens 1240 px Layoutbreite und wächst bei mehr Platz mit. Tablet/Mobil verwenden 768/390 px; bei weniger Platz wird die Vorschau passend skaliert. Links lassen sich fertige Intro-, Leistungs- und Kontaktabschnitte einfügen. Der Werkzeugkasten kann für mehr Platz geschlossen werden. Die Struktursuche findet Text oder Elementtypen und zeigt den Pfad zum Treffer; Dreiecke klappen Teilbäume ein. Oberhalb der Vorschau führen die Pfadschaltflächen direkt zum übergeordneten Element.
Platz und Blockgestaltung im Editor (1.6.1)
Leere Layouts ab 1.6.2: Sektionen und Spalten zeigen eine umrahmte Einfügefläche. In einer leeren Sektion direkt 1–4 Spalten wählen; in einer Spalte Inhalt hinzufügen drücken und links das gewünschte Element auswählen. Alternativ ein Element auf die Fläche ziehen: die Markierung nennt das genaue Ziel, am Vorschaurand wird mitgescrollt. Neue Container werden in den sichtbaren Ausschnitt gebracht. Diese Hilfen erscheinen nur im Editor, verschwinden nach dem Befüllen und kehren bei Undo zurück.
Neue Sektion auf Seitenebene steht dauerhaft oben links und fügt nach dem aktuellen Seitenabschnitt ein, unabhängig von der ausgewählten Verschachtelung oder einem gesetzten Plus-Einfügepunkt. Auch „Sektion“ im Werkzeugkasten verwendet ohne expliziten Einfügepunkt die Seitenebene. Verschachtelte Sektionen sind weiterhin über die Plus-Einfügepunkte oder Drag-and-drop möglich.
Die Trennlinien links und rechts der Vorschau lassen sich ziehen. Mit Tab fokussieren und mit den Pfeiltasten ändern; Umschalt vergrößert den Schritt, Home/End wählen die Grenzen, Doppelklick setzt die jeweilige Breite zurück. Die Breiten werden im Browser behalten und bei schmalen Fenstern an den verfügbaren Platz angepasst. Auf dem Telefon stehen die Panels unter der Vorschau.
Text und Überschriften nutzen die Containerbreite. Die Sektion wählt unter Inhalt → Inhaltsbreite zwischen Websitebreite und randloser Ausgabe. Design → Größe → Maximale Breite begrenzt ein einzelnes Element ausdrücklich, etwa auf 60ch; „Ohne Begrenzung“ hebt einen eigenen Grenzwert auf. Der Feinschliff zeigt passende Optionen für Text, Bild oder Container zuerst. Alle Eigenschaften anzeigen macht die übrigen Optionen zugänglich. Bereits gespeicherte Werte bleiben sichtbar.
Produktionsmodus lokal prüfen
composer build
$env:APP_ENV = 'prod'
$env:APP_DEBUG = '0'
php -d upload_max_filesize=8M -d post_max_size=10M -d memory_limit=256M -S 127.0.0.1:8080 -t public bin/dev-router.php
Unter Linux dieselben PHP-Argumente mit vorangestelltem APP_ENV=prod APP_DEBUG=0 verwenden. Vorher eine laufende Instanz auf diesem Port beenden. Zum Entwickeln APP_ENV=dev und APP_DEBUG=1 setzen. Debug-Seiten senden absichtlich X-Robots-Tag: noindex; Audits laufen im Produktionsmodus. Der eingebaute PHP-Server bleibt ein lokaler Entwicklungsserver.
Medien, Galerien und Effekte
Links im Werkzeugkasten Medienverwaltung öffnen oder auf der Seitenübersicht Medienverwaltung wählen. JPEG/PNG/WebP hochladen, Titel und Alternativtext bearbeiten und speichern. Bilder lassen sich suchen, archivieren und wiederherstellen. Die Dateien werden als optimierte WebP-Varianten in vier Breiten gespeichert (480, 960, 1600, 2400 px; ein schmaleres Bild bekommt weniger Stufen und wird nie hochskaliert). Originaldateien werden nicht aufgehoben. Bildbausteine liefern srcset, Hintergrundbilder wählen die Variante je Gerät. Uploadgrenzen: 8 MB, 12 Megapixel und 8000 px Kantenlänge; Hostinglimits können niedriger sein. Bild-URLs sind öffentlich erreichbar, auch wenn sie noch in keinem veröffentlichten Inhalt verwendet werden.
Für ein einzelnes Bild Bild aus Mediathek wählen anklicken. Für eine Galerie den Block Galerie einfügen und Galeriebilder auswählen öffnen. Mehrere Bilder markieren, Alt-Texte speichern oder die dekorative Verwendung wählen und Auswahl übernehmen. Im Strukturbaum einzelne Bilder auswählen, beschriften und sortieren; Reihenfolge und Einfügen lassen sich rückgängig machen. Unter Design Spaltenzahl und Abstände pro Gerät einstellen.
Unter Design → Bewegung und Effekte Schatten, Hover-Effekte und Einblendung beim Erscheinen wählen. Die gewählte Richtung, Zoom, Unschärfe oder Überblendung startet beim Eintreten in den Bildschirm. Das kleine Bewegungsmodul lädt nur auf Seiten mit Einblendungen; ohne JavaScript bleiben die Inhalte sichtbar. Slider haben Pfeile, optionale Punkte sowie bei aktivierter Automatik Pause/Wiedergabe. Manuelle Bedienung pausiert die Automatik, Wiedergabe setzt sie fort. Bewegungsreduktion schaltet Einblendungen und Automatik aus; manuelles Blättern bleibt möglich. Im Editor startet Bewegung testen diese Vorschau, Bewegung stoppen beendet sie. Die Vorschau-Sandbox bleibt ohne Skriptfreigabe.
Medien liegen standardmäßig in var/media; mit MEDIA_DIRECTORY lässt sich ein absoluter Ordner außerhalb von public konfigurieren. Er muss für PHP beschreibbar sein. Auf dem Hosting upload_max_filesize=8M, post_max_size=10M und ausreichend Arbeitsspeicher (lokal 256M) einstellen, soweit der Tarif dies erlaubt. Datenbank und Medienordner gemeinsam sichern. Archivieren erhält Bild-URLs; es ist keine Dateilöschung.
Prüfen
composer validate --strict
composer lint
composer test
php bin/console lint:twig templates
php bin/console lint:yaml config
php bin/console lint:container
composer audit
composer build
npm.cmd ci
npx.cmd playwright install chromium
npm.cmd run test:browser
npm.cmd run test:links
npm.cmd test
npm.cmd run test:editor
npm.cmd run test:editor -- --audit
npm.cmd run audit
Unter Linux/macOS npm und npx ohne .cmd; in CI npx playwright install --with-deps chromium. test:browser und audit benötigen den laufenden lokalen Produktionsmodus auf Port 8080. Ein anderes Ziel lässt sich über CMS_URL konfigurieren. test:editor startet einen eigenen PHP-Prozess auf einem freien Port mit isolierter Datenbank und zufälligem Testkonto; es ändert keine echten Seiten oder Benutzer. PHP_BINARY kann dafür einen alternativen PHP-Pfad angeben. Testdaten bleiben unter var/browser-tests, Berichte/Screenshots unter reports/. Das Skript erneuert zuvor den Produktionscache.
Datenbank: SQLite ab Fassung 3.16 (Doctrine liest das Schema mit Tabellenfunktionen, die es erst seit dieser Fassung gibt) oder MySQL/MariaDB. Auf älteren Systemen – manche Shared Hoster liefern noch SQLite 3.7 – bleibt MySQL; die Systemprüfung des Installers nennt die gefundene Fassung und bietet SQLite gar nicht erst an, wenn sie zu alt ist.
Die Verwaltung liegt unter /admin. php bin/console cms:admin:path meine-verwaltung legt sie auf eine andere Adresse: der Wert landet in .env, gilt für Routen, Firewall und Schutzkopfzeilen, und wird in der robots.txt bewusst nicht genannt. Ohne Argument zeigt der Befehl die aktuelle Adresse.
composer format formatiert PHP. composer build wärmt den Symfony-Produktionscache; öffentliche CSS-Dateien sind direkt auslieferbar, es ist kein Frontend-Bundler nötig. Bei Änderungen an public/assets/design-tokens.json einmal npm.cmd run build:design ausführen und die erzeugte CSS-Datei mitliefern; Node ist auf dem Hosting nicht erforderlich. Nach Änderungen an Produktionskonfiguration/Templates php bin/console cache:clear --env=prod --no-debug ausführen. Falls lokale Tests alte Templates verwenden, vor PHPUnit auch php bin/console cache:clear --env=test --no-debug ausführen.
test:links folgt der Sitemap, sammelt jeden internen Verweis samt Bildern, Stylesheets und Hintergrundbildern und meldet jede Antwort ab 400 – gebaut, nachdem im Nachbau von kant-it.de vier Verweise ins Leere zeigten, die kein Test finden konnte.
Das Lighthouse-Skript misst dreimal je Profil und prüft die Mediane gegen die Projektziele. Welche Adressen geprüft werden, sagt CMS_PATHS (mit Komma getrennt, Standard /); in der Git-Bash davor MSYS_NO_PATHCONV=1 setzen, sonst wird aus / ein Windows-Pfad. test:editor -- --audit prüft die im isolierten Editortest gestaltete v3-Seite mit Abschnittsvorlage, Galerie, tatsächlichen Bildvarianten und Effekten; Berichte stehen unter reports/lighthouse-editor. Uploads und Datenbank des Tests liegen getrennt unter var/browser-tests. Diese kleinen Prototypseiten ersetzen nicht die spätere Referenzsuite mit Kundeninhalten, Fotos, Beitrag und Formular. Messgrenzen und Ergebnisse: docs/QUALITY.md.
Inhalte und Entwicklung
Die Formate v1/v2/v3 und erlaubten Blöcke stehen in src/Content/DocumentValidator.php, Gestaltungstokens in public/assets/design-tokens.json, Entwicklungsinhalte in src/Content/DemoContent.php. Designwerte erfordern mindestens v2, Medien/Galerien/Effekte v3. Die erste entsprechende Aktion migriert den lokalen Entwurf erhaltend; erst Speichern schreibt eine neue Revision. Bestehende v1/v2-Seiten bleiben lesbar. Migrations- und Rückweg: ADR 008/009 in docs/ARCHITECTURE.md. PageRepository stellt saveDraft, publish und findPublished bereit. Speichern und Publizieren erfordern die erwartete Seitenversion; veraltete Zugriffe brechen mit StaleWrite ab.
Ein JSON-Dokument lässt sich zusätzlich lokal über php bin/console cms:save-draft PAGE_ID PFAD_ZUR_DATEI ERWARTETE_VERSION speichern. Der Befehl gibt die neue Revisions-ID aus. Die redaktionelle Bearbeitung und Veröffentlichung erfolgen jetzt über den geschützten Editor.
Wiederkehrende Aufgaben
Sicherung, geplante Veröffentlichung, Statistik verdichten, Updateprüfung und Aufräumen laufen über einen eingebauten Plan. Auf geteiltem Hosting gibt es oft keinen Cronjob – deshalb stößt hier die Anfrage die Arbeit an, nach der ausgelieferten Antwort (kernel.terminate), sodass kein Besucher darauf wartet. Der Bildschirm Verwaltung → Aufgaben zeigt Plan, letztes Ergebnis und Protokoll und schaltet jede Aufgabe einzeln.
Vier Auslöser, in aufsteigender Verlässlichkeit:
- Besuch – jeder Seitenaufruf prüft, ob etwas fällig ist. Der Normalfall kostet eine gelesene Datei (
var/scheduler.json) und keine Datenbankabfrage. Standardmäßig an. - Geöffnete Verwaltung – das Backend fragt jede Minute nach, solange der Reiter sichtbar ist. Damit läuft der Plan auch auf einer Website ohne Besucher. Standardmäßig an.
- URL-Cronjob – wenn der Hoster nur das Aufrufen einer Adresse anbietet: die Adresse steht auf dem Bildschirm „Aufgaben“ (
/cms-cron/<Schlüssel>, ausAPP_SECRETabgeleitet), zum Beispiel alle fünf Minuten. - Konsole –
php bin/console cms:tasks:run, für einen echten Cronjob jede Minute:
* * * * * cd /pfad/zur/installation && php bin/console cms:tasks:run
cms:tasks zeigt denselben Plan wie die Verwaltung (--log zusätzlich das Protokoll), cms:tasks:run --task=backup startet eine bestimmte Aufgabe sofort, cms:tasks:run --loop=60 --max-time=3600 läuft als Dauerläufer unter einem Prozessverwalter. Die alten Einzelbefehle cms:backup, cms:stats:prune und cms:publish-due gibt es weiterhin; wer sie in einen Cronjob einträgt, schaltet die entsprechende Aufgabe im Plan besser ab, damit nicht beides läuft.
Die Grenze gehört dazu: ohne Besuch und ohne geöffnetes Backend passiert nichts. Eine Aufgabe für 03:00 Uhr läuft auf einer stillen Website erst beim nächsten Aufruf, vielleicht um 07:15 Uhr. Wer Minutengenauigkeit braucht, richtet Weg 3 oder 4 ein und schaltet den Besuchs-Auslöser ab.
PHP-Webhosting
Archiv-Updates prüfen: node scripts/archive-update-check.mjs <altes-release.zip> <neues-release.zip> testet im Produktionsmodus den vollständigen HTTP-Updateablauf mit isolierter Kopie von var/kantit.db. PHP mit SQLite3 über PHP_BINARY wählen. Downloadtransport lokal, Originaldatenbank unverändert; Protokolle unter var/archive-check-*. Ein drittes Argument 500 prüft ausdrücklich den bekannten Altfehler samt Wiederaufnahme. Um den nächsten Updateaufruf des korrigierten 1.10.2-Pakets ohne Folgerelease zu testen, dasselbe Paket zweimal angeben und ausschließlich für diesen Test UPDATE_SOURCE_VERSION=1.10.1 setzen; geändert wird nur die Versionsdatei der Wegwerfinstallation.
500 direkt nach einem Archiv-Update aus 1.10.1 oder älter: /admin/system direkt öffnen und angebotene Migrationen abschließen. Der alte Updater kann nach erfolgreichem Dateitausch seinen eigenen Cache löschen und beim Antworten abbrechen. Ab 1.10.2 behalten laufende Anfragen ihren Cache; neue Releases erhalten ein eigenes Cacheverzeichnis. Details und Grenzen stehen in ADR 090. Nicht erneut installieren oder die Datenbank ersetzen.
Anforderungen vor dem Pilotbetrieb beim Anbieter bestätigen:
- Gepflegte PHP-Version ab 8.2, PDO MySQL und die Composer-Plattformanforderungen.
- MySQL 8 oder kompatible MariaDB; tatsächliche Version und Berechtigungen noch überprüfen.
- Domain-Dokumentenstamm muss auf
public/zeigen.vendor/,var/, Konfiguration und Secrets gehören außerhalb des öffentlichen Verzeichnisses. - Apache mit Rewrite-Unterstützung für
public/.htaccessoder entsprechende FastCGI/Nginx-Konfiguration. - Schreibzugriff für PHP auf
var/, HTTPS und eine Möglichkeit, Migrationen/Cache-Kommandos kontrolliert auszuführen.
Serverkonfiguration über die nicht öffentliche .env; echte Server-Umgebungsvariablen dürfen sie gezielt überschreiben: APP_ENV=prod, APP_DEBUG=0, eigenes zufälliges APP_SECRET, SITE_ORIGIN=https://eigene-domain.example und DATABASE_URL=mysql://USER:URL_ENCODED_PASSWORD@HOST:3306/DATABASE?charset=utf8mb4. Das ist eine Vorlage, keine funktionierende Zugangskonfiguration.
Composer wird auf dem Hosting nicht vorausgesetzt. Ein Release wird auf einem Rechner gebaut, der Composer hat: php scripts/build-release.php legt var/releases/kantsites-<Fassung>.zip samt .sha256 an. Das Archiv enthält die Anwendung mit fertigem vendor/ ohne Entwicklungspakete, aber keine Tests, keine Entwicklungsskripte und kein var/. Eine .env aus der unvertraulichen .env.example liegt für die Erstinstallation bei. Der Webinstaller schreibt die eigenen Werte in .env, migriert und legt das erste Benutzerkonto an. Beim Update die eigene .env erhalten und niemals mit der Vorlage aus dem Archiv überschreiben. Node wird dort ebenfalls nicht gebraucht. Keine Installation ins öffentlich erreichbare Projektstammverzeichnis.
Ein Livegang, Hostzugriff, Restore-Test und Produktionsfreigabe sind nicht erfolgt. Bei bestehenden Installationen vor dem Update Datenbank und Medien sichern und php vendor/bin/doctrine-migrations migrate --no-interaction ausführen. Migration 20260912000200 ergänzt Benutzer, 20260912000300 die Medientabelle; beide verändern keine Seitendokumente. Migration 20260912001100 ergänzt drei Datumsspalten an cms_user (drei ADD COLUMN, kein Tabellenneubau). Benutzerverwaltung und Passwortänderung gibt es jetzt in der Oberfläche unter „Benutzer“ und „Mein Konto“; php bin/console cms:user:create bleibt der Weg, wenn kein Konto mehr funktioniert. Migration 20260912001300 ergänzt zwei Spalten an page für Kopf- und Fußbereich je Seite (zwei ADD COLUMN). Migration 20260914000100 legt die Tabelle des Aufgabenplans an (neue Tabelle, kein Eingriff in Bestehendes); ohne sie läuft der Plan nicht, meldet das aber, statt eine Seite zu zerstören. Migration 20260912001200 legt die Zählertabelle der Statistik an (neue Tabelle, kein Eingriff in Bestehendes); php bin/console cms:stats:prune und php bin/console cms:backup erledigt jetzt der eingebaute Aufgabenplan (Abschnitt „Wiederkehrende Aufgaben“); ohne Cronjob genügen Besuche und das geöffnete Backend – die Sicherung schreibt nach var/backups/, rotiert selbst und enthält Datenbank und Medien, aber weder .env noch vendor/. Zurückspielen: php bin/console cms:restore <name>. Der Code liegt auf https://git.kant-it.de/kantarci/KantSites.git; Fassungen sind Tags der Form v1.0.1, und die laufende Fassung steht in VERSION. Ein Update zieht php bin/console cms:update (mit --check, --dry-run, --to=v1.2.0) oder der Bildschirm „System“ in der Verwaltung; --maintenance=an|aus schaltet den Wartungszustand von Hand. Ändert ein Update die Abhängigkeiten (composer.lock) und ist Composer auf dem Server nicht aufrufbar, bricht der Updater ab und verweist auf das Releasearchiv; Updates ohne Abhängigkeitsänderung laufen auch ohne Composer durch. Ablauf und Grenzen stehen in docs/UPDATE.md. Der Abgleich mit der Pilotwebsite bleibt der nächste Schritt.
Verwaltung ab 1.7.0
Seiten und Beiträge sind getrennte Menüpunkte. Suche, Status einschließlich Papierkorb und 20/50 Einträge begrenzen jede Ergebnisliste bereits in der Datenbank. Vor/Zurück erhält die Filter; übergeordnete Seiten lassen sich unabhängig von der aktuellen Ergebnisseite suchen.
Ab 1.7.1 aktualisieren sich Seiten, Beiträge und Mediathek beim Tippen per AJAX, ohne Enter oder Neuladen. Status, Anzahl und Blättern reagieren ebenfalls direkt. Der Cursor bleibt im Suchfeld; Zurück/Vor im Browser stellt die Filter wieder her. Die Mediathek sucht in Titel und Alternativtext über alle Ergebnisseiten. Menüs, Seitenauswahl und Editor-Suchfelder reagieren ebenfalls beim Tippen. Bei Ladefehlern bleiben die bisherigen Treffer sichtbar und „Erneut suchen“ wiederholt die Anfrage. Ohne JavaScript bleibt das Suchformular mit „Anwenden“ benutzbar.
Unter Menüs zuerst den Menüplatz wählen, links Inhalte suchen und zum Menü hinzufügen. Rechts lassen sich Beschriftung, Ziel, Reihenfolge und Unterpunkte bearbeiten. Pfeilbuttons sind per Tastatur erreichbar; Rückgängig stellt entfernte oder umgeordnete Einträge wieder her. Erst Menü speichern ändert die Website. Validierungs-, Verbindungs- und Versionsfehler erhalten die lokalen Eingaben. Ein paralleler Speicherstand muss bewusst neu geladen werden.
Bereiche verwenden denselben Editor; Speichern wirkt sofort auf allen verknüpften Seiten. Unter Formulare ist die Meldung nach dem Absenden ein mehrzeiliges Feld über die verfügbare Formularbreite.
Konfigurationswechsel von älteren Fassungen
Beim ersten Start von 1.7.0 wird eine vorhandene .env.local einmalig mit .env zusammengeführt. Die bisher wirksamen Werte aus .env.local bleiben dabei erhalten, insbesondere Datenbank, Medienordner und Secret. Beide Originale liegen anschließend unter var/backups/environment-<Zeitpunkt>-<Kennung>/; erst nach erfolgreichem Schreiben wird .env.local entfernt. Danach gelten Änderungen direkt in .env. System zeigt die tatsächlich genutzte Datenbank und die Quelle .env oder Server-Umgebung an.
Weitere Symfony-Overlays (.env.prod, .env.prod.local, .env.local.php) werden nicht mehr automatisch geladen. Falls ein Hosting sie bewusst verwendet hat, deren gewünschte Werte vor dem Wechsel in .env oder echte Servervariablen übernehmen. Der Installer, Setup und der Admin-Pfad-Befehl erzeugen keine .env.local mehr. Rückweg: alten Code und die beiden gesicherten Konfigurationsdateien gemeinsam wiederherstellen.
Zweite lokale Referenzwebsite: Elektro Bernhard
Neben dem Kant-IT-Piloten liegt eine vollständige Kundenwebsite in einer eigenen Installation: Datenbank var/elektro-bernhard.db, Medien in var/elektro-bernhard-media. Beide werden über Umgebungsvariablen gewählt, die Anwendung selbst bleibt unverändert:
DATABASE_URL="sqlite:///<Projektpfad>/var/elektro-bernhard.db" MEDIA_DIRECTORY="<Projektpfad>/var/elektro-bernhard-media" php -S 127.0.0.1:8088 -t public bin/dev-router.php
In der App-Vorschau erledigt das die Startkonfiguration elektro-bernhard in .claude/launch.json (Port 8088).
Der Bestand wird nicht von Hand gepflegt, sondern aufgebaut: scripts/seed-elektro-bernhard.php <Ordner mit den Originalbildern> legt Gestaltung, Medien, Formular, Kopf- und Fußbereich, elf Seiten, vier Menüs, Einstellungen, Weiterleitungen und ein Administratorkonto an. Das Skript weigert sich, wenn die Datenbank schon Seiten enthält oder DATABASE_URL nicht auf diese Installation zeigt. sh scripts/reset-elektro-bernhard.sh <Bilderordner> wirft die Installation weg, migriert neu und baut sie wieder auf; das Passwort des Kontos steht am Ende der Ausgabe. Vorlage ist das statische Paket Elektro-Bernhard-Website; die drei Stellen, an denen der Nachbau bewusst abweicht, stehen in docs/STATUS.md.
Vierte lokale Referenzwebsite: Ihr Malerservice Drews
Der Nachbau von maler-drews.com (Malermeister Axel Drews, Gernsbach) liegt in var/maler-drews.db mit Medien in var/maler-drews-media; die Startkonfiguration maler-drews in .claude/launch.json nutzt Port 8093. scripts/seed-maler-drews.php <Bilderordner> baut Gestaltung, Kopf- und Fußbereich, acht Seiten, 31 Beiträge in zwei Kategorien, Menüs, Formular, Einstellungen und Weiterleitungen auf; der Wortlaut der Beiträge, des Impressums und der Datenschutzerklärung steht in scripts/seed-maler-drews-inhalte.php. sh scripts/reset-maler-drews.sh [Bilderordner] (Standard var/maler-drews-assets, Zuordnung in manifest.json) wirft die Installation weg und baut sie neu auf; die Schriften Lato 400/700 und Cormorant Garamond 700 holt es nur, wenn die Cormorant-Dateien in public/assets/fonts fehlen, weil cms:fonts:fetch die geteilte fonts.css sonst nur für dieses Design neu schreibt (Kant-IT und Elektro Bernhard brauchen Lato 900). Für den Abruf liegt var/cacert.pem (curl.se) bereit, weil das PHP 8.4 auf diesem Rechner keine Stammzertifikate kennt. Die bewussten Abweichungen vom Original stehen im Kopf des Skripts.
Zusätzliche Prüfung mit den lokalen Kant-IT-Inhalten: node scripts/admin-quality-check.mjs (PHP über PHP_BINARY wählbar). Das Skript benötigt var/kantit.db und var/kantit-media, erstellt eine eigene Datenbankkopie und ein zufälliges Testkonto darin; es ändert die Pilotdatenbank nicht. Screenshots und Bericht landen in reports/admin-quality/.
Die Livesuche prüft npm run test:search mit derselben isolierten Pilotkopie: Eingabe ohne Enter, langsame Antworten, Fehler/Wiederholen, Filter, Browserhistorie, Mediathek, Tastaturfokus, mobile Ansicht und axe. Ergebnisse stehen in reports/live-search/. Die allgemeine Verwaltungsprüfung ohne erneuten öffentlichen Bewegungstest startet npm run test:admin -- --admin-only.