Einwilligung, die tatsächlich zurückhält, und Pflichtseiten

Ein Hinweis, der nur fragt und sonst nichts ändert, ist Theater. Deshalb zwei
Hälften, und die zweite ist die wichtigere.

Die Frage: serverseitig gerendert, per Formular beantwortet, ohne JavaScript -
die öffentlichen Seiten laufen mit script-src 'none', und ein Banner mit
Skript wäre das eine, was diese Regel bricht. "Alle akzeptieren" und "Nur
notwendige" sind dieselbe Art Knopf, gleich groß, nebeneinander. Die
Entscheidung steht in einem eigenen Cookie: Fassung der Frage, gewährte
Gruppen, Zeitpunkt. Keine Kennung, kein Serverabgleich, ein halbes Jahr. Kein
CSRF-Token, absichtlich: die Anfrage ändert nichts auf dem Server, und ein
Token bräuchte für jeden Besucher eine Sitzung - genau das, was ein
Einwilligungshinweis vermeiden soll. Geprüft wird stattdessen das
Rücksprungziel.

Das Zurückhalten: der neue Block "Externer Inhalt" (Dokumentschema v14) lädt
ohne Einwilligung nichts - kein iframe, keine Adresse im Markup, frame-src
'none'. Der Platzhalter nennt den Anbieter; sein Knopf gewährt genau diese
Gruppe und nimmt die vorher gewährten mit. Erst danach kommt der Host in die
Sicherheitsrichtlinie, und nur dieser. Im Editor bleibt es immer ein
Platzhalter.

Datenschutz und Impressum sind keine Einstellung mehr: fehlen sie, warnt die
Verwaltung und legt sie auf Knopfdruck als Gerüst an, mit NOCH AUSZUFÜLLEN an
jeder Pflichtangabe. Den Text schreibt das CMS nicht - das ist eine rechtliche
Aussage über eine Firma. Verlinkt werden beide aus dem Template, nicht aus dem
Fußbereich: ein Pflichtlink, den man löschen kann, ist keiner.

Dazu der Fehler, den Umut im Screenshot gesehen hat: die Wortmarke stand als
Buchstabenkolonne in einem 60 Pixel breiten Kasten. .site-header war noch ein
Flexcontainer aus der Zeit des handgeschriebenen Markups und quetschte die
Sektion zusammen, und design.css band jede Designklasse an #main - wo Kopf-
und Fußbereich seit ADR 026 nicht mehr liegen. Beides behoben.

Neu im Ausbauplan: Statistik (serverseitige, wiedererkennungsfreie Zählung,
die deshalb keine Einwilligung braucht), Inhalte und Ladeverhalten prüfen
(Blättern überall, minimal laden, Ladebudget), Installer (Schritt für Schritt
im Browser, Migration inklusive).

245 PHP-Tests / 976 Assertions unter PHP 8.2.12 und 8.4.22. Der Browsertest
fand zwei Fehler von mir: grauer Text auf dunklem Fußbereich mit 2,24:1, und
das fixierte Banner verdeckte den Einwilligungsknopf am Seitenende.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
kantarciandClaude Opus 5 committed 2026-09-12 19:27:41 +02:00
1 parent 84d471acd9
commit ee446f1ea1
30 files changed
+1298 -154

No files matched your search

+24
View File
@@ -395,3 +395,27 @@ Jetzt rendert `/admin/media` die Bilder selbst, 24 je Seite, neueste zuerst, mit
Hochgeladen wird über eine Ablagefläche, weil Umut das Auswahlfeld des Browsers zu Recht hässlich fand. Darunter liegt weiter ein echtes `input[type=file]`, nur unsichtbar und trotzdem fokussierbar; die Fläche ist dessen `label`, und der Fokusring wird über `:focus-within` auf die Fläche gezeichnet. Ohne JavaScript bleibt genau das übrig: ein Dateifeld mit Beschriftung. Das Skript ergänzt Ziehen, benennt die gewählten Dateien und überspringt alles, was kein JPEG, PNG oder WebP ist – abgeschickt wird nie von selbst, der Knopf bleibt die Stelle, an der etwas passiert. Ein `required` hat das Feld nicht: der Browser kann seine Meldung nicht an etwas Unsichtbares hängen, und der Server weist einen leeren Upload ohnehin mit einem lesbaren Satz ab.
Der Dialog bleibt, was er ist: der Weg, beim Bearbeiten ein Bild auszuwählen. Zwei Wege zum gleichen Bestand mit verschiedenen Aufgaben – `MediaLibrary::list()` bleibt für den Dialog neben `page()` bestehen, weil der Dialog seinen eigenen Filter mitbringt.
## ADR 032 – Einwilligung: das Banner ist der kleinere Teil (Dokumentschema v14, 12.09.2026)
Ein Einwilligungshinweis, der nur fragt und sonst nichts ändert, ist Theater: die Inhalte laden trotzdem, und die Frage war Dekoration. Deshalb besteht diese Umsetzung aus zwei Hälften, und die wichtigere ist die zweite.
**Die Frage.** Serverseitig gerendert, beantwortet durch ein Formular. Kein JavaScript – die öffentlichen Seiten laufen mit `script-src 'none'`, und ein Banner, das ein Skript braucht, wäre das eine, was diese Regel bricht. „Alle akzeptieren“ und „Nur notwendige“ sind dieselbe Art Knopf, nebeneinander, gleich groß; die Auswahl je Gruppe liegt in einem Aufklappfeld. Die Entscheidung steht in **einem** eigenen Cookie: Fassung der Frage, gewährte Gruppen, Zeitpunkt. Keine Kennung, kein Serverabgleich, ein halbes Jahr, dann wird erneut gefragt. Nicht signiert, und zwar absichtlich: das Cookie beschreibt, was dieser Browser gewählt hat, ein gefälschter Wert ändert also nur, was dieser Browser sieht.
**Das Zurückhalten.** Ein neuer Block `embed` (Dokumentschema v14) hält Inhalte anderer Anbieter. Ohne Einwilligung entsteht **keine Anfrage**: kein iframe, keine Adresse im Markup, kein `frame-src` in der Richtlinie. An seiner Stelle steht ein Platzhalter, der den Anbieter nennt und einen Knopf trägt, der genau diese Gruppe gewährt – und die vorher gewährten mitnimmt, statt sie zu überschreiben. Erst nach der Einwilligung wird der Host der Sicherheitsrichtlinie hinzugefügt, und auch dann nur der Host dieses einen Anbieters. Im Editor bleibt es immer ein Platzhalter: die Vorschau soll nichts von außen laden.
Der Server prüft die Adresse beim Speichern, nicht beim Ausliefern: nur HTTPS, ein einfacher Hostname, kein Port, keine Zugangsdaten. Der Grund ist, dass diese Adresse in eine Sicherheitsrichtlinie eingesetzt wird – ein Leerzeichen darin wäre eine zweite erlaubte Quelle.
**Kein CSRF-Token am Einwilligungsformular.** Die Anfrage ändert nichts auf dem Server; sie setzt ein Cookie, das nur den absendenden Browser betrifft. Ein Token bräuchte eine Sitzung, und für jeden Besucher eine Sitzung zu beginnen ist genau das, was ein Einwilligungshinweis vermeiden soll. Geprüft wird stattdessen das Rücksprungziel, damit aus dem Knopf keine Weiterleitung auf fremde Adressen wird.
**Ohne Grund keine Frage.** Ist der Hinweis abgeschaltet, gilt jede Gruppe als gewährt und die Inhalte laden. Sonst würde das Abschalten des Banners stillschweigend jede Seite leeren, die etwas einbettet.
## ADR 033 – Datenschutz und Impressum sind keine Einstellung (12.09.2026)
Umut: „datenschutz und Impressum einbauen sind keine option“. Richtig – in Deutschland sind beide Pflicht und müssen von jeder Seite aus unmittelbar erreichbar sein. Also:
- Fehlt eine der beiden, **warnt die Verwaltung** auf dem Einstellungsbildschirm, und ein Knopf legt die fehlenden Seiten an und verknüpft sie. Vorhandene Seiten werden nie überschrieben; sie tragen womöglich längst den echten Text.
- Die angelegten Seiten sind ein **Gerüst mit markierten Lücken**: jede Pflichtangabe steht als Abschnitt da, mit `NOCH AUSZUFÜLLEN` an der Stelle, wo eine Angabe über ein reales Unternehmen hingehört. Das CMS kann die Struktur vorbereiten, nicht die Erklärung schreiben – das ist eine rechtliche Aussage über eine Firma, und ein erfundener Text wäre schlimmer als eine leere Seite.
- Beide werden **von jeder Seite verlinkt**, aus dem Template und nicht aus dem Fußbereich. Der Fußbereich ist ein frei bearbeitbares Blockdokument; ein Pflichtlink, den man versehentlich löschen kann, ist kein Pflichtlink. Die Links liegen mit dem Link „Einwilligung ändern“ in einem benannten Landmark – loser Text außerhalb aller Landmarks ist auch das, was axe zu Recht beanstandet.
Eine Pfadangabe wird beim Speichern geprüft: zeigt sie auf eine Seite, die es nicht gibt, wird sie abgewiesen.