# Projektstatus ## 1.17.0 – Gesamtprüfung, Adresse mit oder ohne www, Rollen wie versprochen (15.09.2026) **Anlass:** Umut: „Komplette Analyse – Bugs, Durchgängigkeit, ob alles sauber ist; was man besser machen kann; und: die URL soll aus env kommen, www oder ohne, die Seite soll darauf reagieren.“ Nachgeschoben: „prüfst du auch, ob man ohne Admin etwas verändern kann?“ **Prüfung.** Sechs getrennte Durchsichten: Backend (Controller, Content, Security, Twig), Betrieb (Ops, Commands, Subscriber, Config, Installer, CI), Editor (JS, Admin-Templates, Modelltests), öffentliche Ausgabe (Templates, CSS, Skripte, SEO, CSP, Einwilligung), Dokumentation und Repository (README, docs, Skripte, Ports, Versionen, git-Hygiene) sowie eine Routenmatrix aller 63 Verwaltungs- und 15 öffentlichen Routen mit Firewall, Rolle, CSRF und Testabdeckung. Vorab: `phpunit` 512 grün, `npm test` 33 grün, Twig/YAML/Container-Lint ohne Befund, php-cs-fixer eine vorbestehende Zeile in `CmsTest`. Repository-Hygiene in Ordnung (313 Dateien, keine Geheimnisse, `var/`, `reports/`, `.env` ignoriert, `VERSION` = Tag = STATUS). **Antwort auf die Rollenfrage:** Ohne Anmeldung kann niemand Inhalte, Einstellungen oder Konten ändern – alle Verwaltungsrouten liegen hinter der Firewall, jede schreibende prüft Rolle und CSRF-Token. Zwei echte Lücken gab es trotzdem, beide behoben: (1) Der Installer öffnete sich wieder, sobald die Datenbank nicht antwortete (Wartung beim Hoster, gedrehtes Passwort) – dort hätte jeder eine eigene Datenbank samt Administratorkonto eintragen können. (2) Die Rolle Redakteur durfte Design, Einstellungen (Startseite, `robotsExtra`, Turnstile, Einwilligung), Bereiche, Menüs und Formulare samt Empfängeradresse live ändern, entgegen der Rollenhilfe. ADR 100. **www / SITE_ORIGIN (ADR 099):** `CanonicalHostSubscriber` leitet die jeweils andere Schreibweise mit 301 auf die eingetragene Adresse um (Pfad und Abfrage bleiben, Schema und Port aus `SITE_ORIGIN`, auch für die Verwaltung); fremde Hosts, IP-Adressen und localhost bleiben erreichbar, nur GET/HEAD, kein HTTPS-Zwang. Neu `php bin/console cms:site:origin `; der Bildschirm „System“ zeigt Adresse, Quelle und einen Hinweis bei abweichendem Host. Der www-Block in `public/.htaccess` ist weg, `.env.example` erklärt die Einstellung, LIVEGANG Punkt 3 und README angepasst. **Behoben (Auswahl, alles mit Test):** Sitzungscookie folgt `ADMIN_PATH` (Anmeldung nach `cms:admin:path` war unmöglich); `%env(resolve:)%` gegen prozentkodierte Passwörter durch `projectdir:`-Prozessor ersetzt; Einwilligung nur von dieser Website und kein `/\`-Rücksprung; JSON-LD mit `JSON_HEX_TAG`; Komma im eigenen CSS; Skriptprüfung beim Wiederherstellen einer Fassung; keine Unterseite unter einer Seite im Papierkorb; geplante Veröffentlichung blockiert bei einem Fehler nicht die anderen; Archiv escaped alte Bereiche; Feed-Zeitzone UTC; Sitemap-`lastmod` aus der Fassung; Vollbild in Embeds; Tabs/Bildbetrachter in Bereichen und Sektionen; Schatten bei Überfahren/Scrollen erbt Form; Download nur über HTTPS-Weiterleitungen; Composer/Git-Umgebung und PHP-Kommandozeile im Befehlsläufer; Updater fängt jeden Fehler. Editor: Schemafassung für Menü/Beitragsliste/Slider, gesperrte Design-Knöpfe bleiben gesperrt, Ausrücken/Baum-Zug mit Regeln, kein leerer Verlaufsschritt bei Ablehnung, Bild auf Akkordeon meldet sich, Klartext unter v12, Zahlenfelder ab 0, Farbwähler ohne `onComplete`. Docs: `.env.local`-Reste, `motion`/`archive-update` als npm-Skripte, README-Einleitung und dritte Referenzwebsite, UPDATE.md, `install.php` erkennt `.env`, Release-Builder ohne `LICENSE`. **Geprüft:** `phpunit` vollständig mit PHP 8.4 grün (neu: `CanonicalHostTest` 24 Fälle, `SiteOriginCommandTest`, `ProjectDirEnvProcessorTest`, Rollen-Matrix und Cookie-Pfad in `EditorSecurityTest`, Installer-Fälle in `InstallationTest`, Systembildschirm, Tabs im Fuß, Sitemap-`lastmod`); `npm test` 33 grün (Fassungstabelle angepasst); Twig/YAML/Container-Lint ohne Befund; php-cs-fixer ohne Befund; `cms:health` gegen die Entwicklungsdatenbank mit dem neuen Prozessor `OK`; `npm run test:editor` (Playwright + axe, `PHP_BINARY=C:/php84/php.exe`) vollständig bestanden – ein erster Lauf scheiterte an einer seit 1.16.1 veralteten Zusicherung des Skripts („Spalte in der Sektion weiterhin gesperrt“), die jetzt die erlaubte Spalte erwartet. Nicht geprüft: Firefox/Safari, ein echtes Touchgerät, das Hosting, ein echter MySQL-Server. **Rückstand (aus der Prüfung, nicht umgesetzt):** Archivseiten auf die Aufbaukette der Seiten heben (Kopf/Fuß-Stile, Schriften, Sektionen, Nach-oben); Formulare, Brotkrumen und Beitragslisten in Bereichen/Sektionen sind dort tot; doppelte Block-IDs bei mehrfach verknüpfter Sektion; Breakpoints 650 vs. 767 px vereinheitlichen; feste Farbwerte (`.tone-ink`, Skip-Link, CTA-Button, Fehlerrot, `color-scheme`) bei dunklen Paletten; MySQL ohne Sicherung, Archiv-Update und Rollback; Archiv-Update überschreibt `.htaccess` (HTTPS-Schalter); Release-Caches wachsen; Rollback-Kopie des Git-Updaters ohne Sperre; Archiv komplett im Speicher; Canonical/`rel=prev/next` blätternder Beitragslisten; Druckstile; Editor-Vorschau ohne eigenes CSS/Bereichsstile/Formulare; Skripte in Bereichen laufen nie; vier Blocktypen direkt nach dem Einfügen ungültig (Kontakt, Formular, Sektion, Embed); Rich-Text-Normalisierung erst nach Neuladen sichtbar; `legal.label` unvalidiert; verrutschte Docblocks; CI-`mysql`-Job ohne Tests; Ports 8090/8092 in QUALITY/STATUS sind Messserver, nicht die Startkonfigurationen. **Veröffentlicht als 1.17.0** (kein neues Dokumentschema, keine Migration; bestehende Redakteurskonten verlieren Design, Einstellungen, Bereiche, Menüs und Formulare – siehe Eintrag oben). **Nächster Schritt:** `SITE_ORIGIN` auf `web.kant-it.de` und dem Drews-Hosting prüfen und die HTTPS-Zeilen in `.htaccess` einschalten. ## 1.16.1 veröffentlicht (15.09.2026) Tag `v1.16.1` (Commit `937344e`), Release: https://git.kant-it.de/kantarci/KantSites/releases/tag/v1.16.1 – einzelne Spalte in einer Sektion, Ausrichtung für Buttons, breite und volle Abschnitte gegen die Design-Inhaltsbreite, Nach-oben-Knopf in einer Landmarke, Cormorant Garamond lokal. **Anlass:** Die neu aufgebaute Drews-Datenbank trägt das Token „Ausrichtung“ an Buttons; unter dem auf dem Hosting laufenden 1.16.0 lehnte der Validator diese Dokumente beim Rendern ab (500 auf jeder öffentlichen Seite, Verwaltung lief). Das Release bringt Code und Datenbank wieder zusammen. **Gebaut aus einem sauberen Checkout des Tags** (`git worktree add var/release-source-1.16.1 v1.16.1`, `php scripts/build-release.php` darin): 3552 Dateien im Archiv, 4,7 MB. **Anhänge:** `kantsites-1.16.1.zip` (SHA-256 `2922027abb648839b89962351aa3dbeaa9e354dd985c94a7a2efddabb86d6731`), `.sha256`, `install.php` mit eingetragener Prüfsumme; hochgeladen mit `var/publish-release-1.16.1.py`, erst Entwurf, dann veröffentlicht. **Geprüft:** HTTP-Archiv-Update 1.16.0 → 1.16.1 `ok` (Update 302, System 200, Abschluss 302, öffentliche Seite 200; `scripts/archive-update-check.mjs`); nach der Veröffentlichung ohne Anmeldung alle drei Anhänge bytegleich, `latest` = `v1.16.1`. Die Prüfungen des Codes stehen in den beiden Einträgen darunter. **Nächster Schritt:** auf dem Drews-Hosting über System → Update aus dem Archiv einspielen (keine Migration), danach `var/cache/prod` prüfen; die kopierte Datenbank kann bleiben. ## Einzelne Spalte in einer Sektion, Buttons ausrichten (15.09.2026) **Anlass:** Umut konnte im Editor keine Spalte direkt in eine Sektion legen (ausgegraut) – so zentriert er einen Button –, und der Button „Alle Mitteilungen“ auf der Drews-Startseite stand links, ohne eine Option dafür. **Editor (`editor-model.js`):** `REQUIRE.column` ist jetzt `['row', 'section']`. Eine einzelne Spalte darf direkt in einer Sektion stehen (schmaler Inhalt, zentrierte Elemente), in einer Spalte oder lose im Dokument weiter nicht. Test angepasst. **Buttons (`design-tokens.json`, `site.css`, `design.css` neu gebaut):** Das Design-Token „Ausrichtung“ gilt jetzt auch für Buttons. Weil ein Button `inline-flex` ist, zentriert ihn `margin:auto` nicht; `site.css` macht ihn bei `align-center`/`-right` zum Block mit eigener Breite (`display:flex;width:fit-content;margin-inline:auto`, dazu `justify-self` für Raster), je Gerät an den Breakpoints von `design.css` (1024/650 px). Das Drews-Aufbauskript nutzt das Token für alle mittigen Buttons statt der wirkungslosen auto-Ränder. **Geprüft:** `npm test` 33 grün (darunter der geänderte Einfügetest), PHP-Tests zu Design-Tokens 11 grün; Drews-Startseite nach Neuaufbau: „Alle Mitteilungen“ und „Alle Projekte ansehen“ mittig, Aufruf-Buttons mittig, auf 1440 und 390 px. Unveröffentlicht, gehört mit den beiden Korrekturen darunter in 1.16.1. ## Ihr Malerservice Drews nachgebaut, 15.09.2026 **Anlass:** Umut wollte maler-drews.com (Flatsome-Auftritt, Malermeister Axel Drews, Gernsbach) nach dem bekannten Prinzip in eigener Installation nachbauen – exakt im Viewer, mit Lighthouse, axe und maschinenlesbaren Ausgaben auf 100. **Vorlage ausgelesen, nicht geschätzt.** Alle 9 Seiten und 31 Beiträge der laufenden Website als HTML geholt, Struktur, eigenes CSS (`malerdrews-*`, `drews-*`), Abschnittsfarben und Inline-Werte extrahiert, Screenshots in 1440 und 390 px als Vergleich. Befund: Lato + Cormorant Garamond 700, Gold `#c6a36b`, Creme `#f7f5f2`, dunkle Bänder `#232323`/`#111`/`#0a0a0a`, Kopf 90 px weiß mit Versalmenü, Startbild als Slider mit drei Folien und goldener Welle, Kartenraster aus Bild und Verlauf, Zickzackliste der Projekte, Zeitleiste der Mitteilungen. `/about-us` und `/innenaustattung` sind auf der Vorlage Lorem-ipsum-Reste, `/little-greene` ein 404. **Nachbau (`scripts/seed-maler-drews.php`, `scripts/seed-maler-drews-inhalte.php`, `scripts/reset-maler-drews.sh`).** Eigene Installation `var/maler-drews.db`, `var/maler-drews-media`, Bildquellen `var/maler-drews-assets` (139 Bilder, auf 2400 px verkleinert, Zuordnung in `manifest.json`), Startkonfiguration `maler-drews` auf Port 8093. Acht Seiten (Start, Aktuelles, Unsere Projekte, Kunstmalerei, Kontakt, Impressum, Datenschutz, 404), 31 Beiträge mit Bildkopf, Text, Galerie mit Bildbetrachter, Nachbarn und Aufruf, Kategorien „Aktuelles“ (5) und „Projekte“ (26); die Listen sind Beitragslisten-Bausteine (Karten auf der Startseite, Liste als Zeitleiste, Wechselseitig als Zickzack). Kopf mit Marke und Versalmenü, Fuß in vier Spalten mit Instagram, Öffnungszeiten und Rechtszeile. Formular serverseitig, Google-Karte und elf Videos als eingebettete Inhalte hinter der Einwilligungsleiste. Impressum und Datenschutz wörtlich übernommen (die Datenschutzerklärung nennt Google Analytics und reCAPTCHA, die der Nachbau nicht nutzt – vor dem Livegang zu bereinigen). Weiterleitungen für die drei toten Vorlagenadressen und die eine über 80 Zeichen lange Beitragsadresse. Bewusste Abweichungen stehen im Kopf des Skripts. **Für 100 geändert, nicht für die Optik:** Gold auf Weiß erreicht 2,4:1 – Kleinzeilen, Datum, Links und aktive Menüpunkte auf hellem Grund tragen `@gold-text` (`#8a6a2f`, 5,0:1), Buttons dunkle statt weiße Schrift auf Gold (6:1), der Einwilligungsknopf ebenso; vor den Beitragslisten steht eine nur für Vorlesesoftware sichtbare H2; der Copyright-Link ist unterstrichen. Das Startbild wird über den äußeren Abschnitt vorgeladen (nur das erste Hintergrundbild einer Seite bekommt ein `preload`). **Zwei Lücken im CMS behoben (`site.css`, `page.html.twig`, unveröffentlicht, gehört in 1.16.1):** Abschnitte mit Breite „breit“ oder „voll“ blieben so schmal wie alle anderen, weil `.kant .shell{max-width:var(--k-container)}` des Designs die Breitenregeln schlug – jetzt mit `.kant` davor. Der Nach-oben-Knopf stand außerhalb jeder Landmarke (axe „region“) und steckt jetzt in einem `nav`. Beim Bauen gelernt: eigenes CSS erlaubt kein `@media`, keine Anführungszeichen, keine Klammern in Selektoren (`:not()`, `nth-child()`), keine Kommaselektoren, und eine leere `&{…}`-Regel geht verloren – Eigenschaften des Elements ohne `&{}` schreiben. **Geprüft:** Lighthouse 13.4.1 (`scripts/audit.mjs`, je drei Läufe Mobil und Desktop, Mediane) gegen den lokalen Produktionsmodus (`APP_ENV=prod`, Port 8092) auf sechs Adressen (`/`, `/aktuelles`, `/unsere-projekte`, `/kunstmalerei`, `/kontakt`, `/friedrichsbad-baden-baden`): **Barrierefreiheit, Best Practices, SEO und agentisches Browsing überall 100**, Leistung 98–100 (Startseite 99 mobil / 98 Desktop, Kunstmalerei 99/99, Projekte und Beitrag 99 mobil, sonst 100; LCP ist jeweils das Foto hinter dem Seitenkopf, mobil 1,6–2,1 s, Desktop 0,6–1,0 s; CLS ≤ 0,01, TBT ≤ 7 ms). Berichte in `reports/lighthouse-maler-drews/`. Drei Messrunden davor zeigten Barrierefreiheit 91–93 (Gold-Kontrast, Überschriftenfolge, Link nur durch Farbe), dann 96–100 (aktiver Menüpunkt, Formularbeschriftungen), jetzt 100. axe (WCAG 2.0/2.1 A+AA und Best Practice, Playwright, 1440 und 390 px, nach dem Durchscrollen) auf neun Seiten einschließlich Impressum und Datenschutz: **null Verstöße** (`var/drews-axe.mjs`); vorher 58, davon 36 Kontrast, 18 „region“ am Nach-oben-Knopf, 2 Überschriftenfolge, 2 Link im Text. Maschinenlesbar: `llms.txt` mit allen Seiten und Beiträgen, `sitemap.xml` mit 41 Adressen, `robots.txt`, `feed.xml`, `LocalBusiness` mit Adresse, Öffnungszeiten, Einsatzgebiet und Instagram als `sameAs`. Alle 41 Adressen der Vorlage antworten mit 200 oder 301. Screenshots des Nachbaus in 1440 und 390 px gegen die der Vorlage verglichen (Startseite, Aktuelles, Projekte, Kunstmalerei, Kontakt, Beitrag, Impressum). PHP-Tests 512 grün (PHP 8.4), `npm test` 33 grün, Twig-Lint, php-cs-fixer auf den neuen Skripten. **Nicht geprüft:** der Editor auf den Drews-Dokumenten im Browser (kein Anmelden mit Passwort in dieser Sitzung), Firefox/Safari, ein echtes Touchgerät, das Hosting. Die Videos liegen weiter auf maler-drews.com und brauchen für den Livegang einen eigenen Speicherort. ## 1.16.0 veröffentlicht (15.09.2026) Tag `v1.16.0` (Commit `32ee288`), Release: https://git.kant-it.de/kantarci/KantSites/releases/tag/v1.16.0 – Spaltenbreiten einer Zeile durch Ziehen im Editor. **Gebaut aus einem sauberen Checkout des Tags** (`git worktree add var/release-source-1.16.0 v1.16.0`, `php scripts/build-release.php` darin): 227 Quelldateien, `vendor/` ohne Entwicklungspakete, 3549 Dateien im Archiv. **Anhänge:** `kantsites-1.16.0.zip` (4,7 MB, SHA-256 `8aa8d40d79dfe7bf972ca005ed92d688cf63beb93a4807c20d5dcabb9b2f9264`), `.sha256`, `install.php` mit eingetragener Prüfsumme; hochgeladen mit `var/publish-release-1.16.0.py`, erst Entwurf, dann veröffentlicht. **Geprüft:** HTTP-Archiv-Update 1.15.1 → 1.16.0 `ok` (Update 302, System 200, Abschluss 302, öffentliche Seite 200; `scripts/archive-update-check.mjs`); nach der Veröffentlichung ohne Anmeldung alle drei Anhänge bytegleich, `latest` = `v1.16.0`. Die Prüfungen des Codes selbst stehen im Eintrag darunter. **Nächster Schritt:** Update auf `web.kant-it.de` über System → Update aus dem Archiv (keine Migration), dann im Editor eine Zeile wählen und die Spaltengrenze ziehen – etwa bei einer Karte mit Symbol links und Text rechts. ## 1.16.0 – Spaltenbreiten einer Zeile durch Ziehen (15.09.2026) **Anlass:** Umut, mit der Karte „HausKlar“ (Symbol links, Text rechts) als Beispiel: „bei Rows die Breite ändern, am besten per Drag and Drop im Editor.“ Zeilen kannten bisher nur gleich breite Spalten. Entscheidung und Begründung: ADR 098. **Umsetzung:** Neue freie Stileigenschaft `columnWidths` an der Zeile (Typ `fractions`, `StyleSchema::FRACTIONS`, z. B. `66.7 33.3`), je Gerät, ohne neues Dokumentschema. `StyleCompiler::columnTracks()` macht daraus `grid-template-columns:minmax(0,66.7fr) minmax(0,33.3fr)`, füllt fehlende Anteile mit dem Durchschnitt auf, kappt überzählige und setzt ein Gerät, dessen Aufteilung stapelt (`one` oder `gridColumns` 1), auf eine Spur – sonst schlüge die ID-Regel des Desktops die Stapel-Klasse des Handys. Im Editor (`canvas-overlay.js`) liegt auf jeder Grenze zwischen zwei nebeneinander stehenden Spalten der gewählten Zeile (oder der Zeile um die gewählte Spalte) ein Griff `.ov-resize`: Ziehen stellt die Zeile in der Vorschau live um, Loslassen schreibt einen Verlaufsschritt (`onResize` in `editor.js` → `setStyle`), Pfeiltasten verschieben um fünf Punkte und behalten den Fokus. Ausgangswert sind die gemessenen Spaltenbreiten. Im Design-Bedienfeld gibt es dazu das Textfeld „Spaltenbreiten“ (Layout, nur bei Zeilen). Kein Griff bei gestapelten Spalten und während ein Element gezogen wird. **Geprüft:** `phpunit` vollständig mit PHP 8.4 512 von 512 grün, mit PHP 8.2 512 grün mit der vorbestehenden Warnung in `UpdaterTest`; neu `tests/ColumnWidthsTest.php` (Spuren, Stapeln je Gerät, Auffüllen und Kappen, Ablehnung ungültiger Werte). `npm test` 33 grün (Relevanz nur für Zeilen). `npm run test:editor` mit `PHP_BINARY=C:/php84/php.exe` vollständig bestanden – neu darin: echter Mauszug am Griff verbreitert die erste Spalte, der Wert steht im Bedienfeld der Zeile, auf „Mobil“ gibt es keinen Griff, ein Rückgängig stellt gleiche Spalten her (`reports/editor/column-widths.png`). php-cs-fixer ohne Befund. Ein erster Lauf des Checks scheiterte an `openGroup`, das im Skript erst später definiert ist – die Gruppe wird jetzt an Ort und Stelle geöffnet. **Nebenbei:** `.claude/launch.json` trägt in der Arbeitskopie den noch nicht committeten Eintrag `maler-drews` einer anderen Sitzung auf Port 8091; die Pilotkonfiguration ist deshalb lokal auf 8092 verschoben, die Datei bleibt aus diesem Commit heraus. ## 1.15.1 veröffentlicht (15.09.2026) Tag `v1.15.1` (Commit `d05dba3`), Release: https://git.kant-it.de/kantarci/KantSites/releases/tag/v1.15.1 – eigene Absenderadresse je Formular. **Gebaut aus einem sauberen Checkout des Tags** (`git worktree add var/release-source-1.15.1 v1.15.1`, `php scripts/build-release.php` darin): 227 Quelldateien, `vendor/` ohne Entwicklungspakete, 3549 Dateien im Archiv. **Anhänge:** `kantsites-1.15.1.zip` (4,7 MB, SHA-256 `e15a7a69f299c16d86d372b65610b52f15b40b93904fa6402ea6991de7ed21f7`), `.sha256`, `install.php` mit eingetragener Prüfsumme; hochgeladen mit `var/publish-release-1.15.1.py`, erst Entwurf, dann veröffentlicht. **Geprüft:** HTTP-Archiv-Update 1.15.0 → 1.15.1 `ok` (Update 302, System 200, Abschluss 302, öffentliche Seite 200; `scripts/archive-update-check.mjs`); nach der Veröffentlichung ohne Anmeldung alle drei Anhänge bytegleich, `latest` = `v1.15.1`. Die Prüfungen des Codes selbst stehen im Eintrag darunter. **Nächster Schritt:** Update auf `web.kant-it.de` über System → Update aus dem Archiv (keine Migration), dann unter Formulare eine Absenderadresse der eigenen Domain eintragen und eine Testanfrage senden. ## 1.15.1 – Eigene Absenderadresse je Formular (15.09.2026) **Anlass:** Umut: „Versandadresse bei Kontaktformularen sollte man noch festlegen können – aktuell wird's über die gleiche Adresse verschickt wie der Empfänger?" Stimmte: `FormController::deliver()` setzte `From` auf die Empfängeradresse. Das geht schief, sobald der Empfänger ein Postfach bei einem anderen Anbieter ist (z. B. eine GMX- oder Gmail-Adresse), weil viele Hoster nur zustellen, was von einer Adresse der eigenen Domain kommt, und SPF/DMARC des fremden Anbieters die Post als Fälschung abweisen. **Umsetzung:** Neue Formulareinstellung `sender` (freiwillig). `FormRepository::validateSettings()` prüft sie wie den Empfänger mit `FILTER_VALIDATE_EMAIL`, leer bleibt erlaubt; die Verwaltung (`forms.html.twig`, Abschnitt Zustellung, Feld „Absender“ direkt unter dem Empfänger mit Hinweis) und `FormAdminController` reichen sie durch. Beim Versand gilt `sender ?: recipient` – bestehende Formulare ohne den Schlüssel verhalten sich wie bisher, es ist keine Migration nötig. Die Antwortadresse aus dem Formularfeld bleibt `Reply-To`. **Geprüft:** `phpunit` vollständig mit PHP 8.4 509 von 509 grün, mit PHP 8.2 509 grün mit der vorbestehenden Warnung in `UpdaterTest`; `npm test` 33 grün; `npm run test:editor` mit `PHP_BINARY=C:/php84/php.exe` vollständig bestanden, darin die axe-Prüfung der Formularseite. Neu in `EditorSecurityTest`: `testAFormMaySendFromItsOwnAddress` (From = Absender, To = Empfänger, Reply-To = Feld; ungültiger Absender abgewiesen, Leerzeichen werden zu leer) und im bestehenden Absendetest die Prüfung, dass ohne Absender From = To ist – beides über `getMailerMessage()` mit dem `null://`-Transport der Tests. php-cs-fixer ohne Befund. In der Pilotkopie im Browser (`kantsites-pilot`, jetzt Port 8091, weil 8087 von einer anderen Sitzung belegt war): Feld sichtbar, `formular@kant-it.de` gespeichert und nach dem Neuladen erhalten, Meldung „Das Formular wurde gespeichert.“ Ein echter Versand über SMTP wurde nicht geprüft. **Nächster Schritt:** Auf `web.kant-it.de` nach dem nächsten Update unter Formulare eine Absenderadresse der eigenen Domain eintragen (z. B. `formular@kant-it.de`), dann eine Testanfrage senden und im Postfach Absender und Zustellung kontrollieren. ## 1.15.0 veröffentlicht (15.09.2026) Tag `v1.15.0` (Commit `160c394`), Release: https://git.kant-it.de/kantarci/KantSites/releases/tag/v1.15.0 – Formulare, Statistik, Design und Seitenleiste der Verwaltung überarbeitet, Formulare mit eigenen Farben und Vorschau. **Gebaut aus einem sauberen Checkout des Tags** (`git worktree add var/release-source-1.15.0 v1.15.0`, `php scripts/build-release.php` darin): 227 Quelldateien, `vendor/` ohne Entwicklungspakete, 3549 Dateien im Archiv. **Anhänge:** `kantsites-1.15.0.zip` (4,7 MB, SHA-256 `86ef728812db3b2f565f07dd1d8aebf8d5d41b75875695544134684b77a193ed`), `.sha256`, `install.php` mit eingetragener Prüfsumme; hochgeladen mit `var/publish-release-1.15.0.py`, erst Entwurf, dann veröffentlicht. **Geprüft:** HTTP-Archiv-Update 1.14.2 → 1.15.0 `ok` (Update 302, System 200, Abschluss 302, öffentliche Seite 200; `scripts/archive-update-check.mjs`, aus dem Hauptrepository heraus, weil der saubere Checkout weder `vendor/` noch `var/kantit.db` hat); nach der Veröffentlichung ohne Anmeldung alle drei Anhänge bytegleich, `latest` = `v1.15.0`, Installeradresse 200. Die Prüfungen des Codes selbst stehen im Eintrag darunter. **Nächster Schritt:** Update auf `web.kant-it.de` über System → Update aus dem Archiv (keine Migration), dann unter Formulare das Aussehen des Kontaktformulars wählen. ## 1.15.0 – Formulare, Statistik, Design und Seitenleiste überarbeitet (15.09.2026) **Anlass:** Umut fand die Formularanlage unschön und wollte Felder löschen können, die Statistik „lieblos“, auf der Designseite vordefinierte Farben, eigene Farben für Link- und Seitenfarbe und eine Vorschau der Schriften, und den Fuß der Seitenleiste (Fassung, Benutzer, Abmelden) besser. Nachgeschoben: eine Vorschau des Kontaktformulars, in der Rahmen-, Hintergrund- und Buttonfarbe direkt gewählt werden. **Formulare (`forms.html.twig`, neu `form-editor.js`).** Jedes Feld ist eine Karte mit Nummer, Beschriftung, Typ, technischem Name, Hilfetext, Pflicht-Schalter und einem Entfernen-Knopf; „Feld hinzufügen“ klont eine `