v1.7.2
28
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
0920e3cb83 | Ergänzt Prozentregler für die responsive Bildbreite | ||
|
|
41d87f62a4 | Ergänzt AJAX-Livesuche für Seiten, Beiträge und Mediathek | ||
|
|
495b26896e | Fassung 1.7.0: verlässliche Installation, übersichtliche Verwaltung und bedienbare Bewegung | ||
|
|
6a044e59e9 | Fassung 1.6.2: sichtbare leere Layouts und direktes Befuellen im Editor | ||
|
|
b783d9d1e5 | Fassung 1.6.1: flexible Editorflaeche und passende Blockeigenschaften | ||
|
|
d1ac9d760b |
Fassung 1.5.5: die Verwaltungsnavigation war am Schreibtisch unerreichbar
In der ausgelieferten Fassung fehlte in der Verwaltung am Schreibtisch die gesamte linke Navigation - Marke oben, Fusszeile unten, dazwischen nichts. Die Navigation steckt in einem <details>: auf dem Telefon ein Klappmenue, am Schreibtisch die stehende Spalte. Am Schreibtisch wurde der Schalter mit display:none ausgeblendet - aber das Element trug kein open, und ein geschlossenes details versteckt seinen Inhalt selbst. Schalter weg und Inhalt weg. Behoben mit ::details-content, das den Inhalt am Schreibtisch zurueckholt, waehrend der Schalter ausgeblendet bleibt. Kennt ein Browser die Regel nicht, faellt ueber @supports die ganze Gruppe weg: dann steht der Schalter da und die Liste ist einen Klick entfernt statt gar nicht da. Der Code dafuer lag bereits im Arbeitsverzeichnis - eine parallel laufende Sitzung hatte ihn geschrieben, aber nicht committet, und damit war er in keinem Release. Uebernommen ist hier nur diese Korrektur, nicht die uebrige Arbeit jener Sitzung. Wie das durchrutschen konnte: beim vorigen Bericht wurde die Navigation fuer in Ordnung erklaert, nachdem eine Nachbildung der Regel in einer leeren Seite geprueft worden war. Die Nachbildung enthielt die Korrektur, die ausgelieferte Datei nicht - der Befund war wertlos. Jetzt prueft der Browsertest die echte Verwaltungsseite, auf beiden Groessen. Gegenprobe gemacht: gegen die ausgelieferte admin.css schlaegt er fehl. Geprueft: npm run test:editor vollstaendig, npm test 28/28, phpunit 452/452. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
7c3c8671a7 |
Fassung 1.5.4: der Werkzeugkasten bekommt Bilder und eine Suche
Die Elementwahl war eine Liste aus Schriftzeichen - "▣" fuer Sektion, "☰" fuer Zeile. Jetzt traegt jeder der 34 Eintraege eine Strichzeichnung, die zeigt, was er baut: die Sektion ein gefuelltes Band, "Zeile, 3 Spalten" tatsaechlich drei Kaesten, 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 Flaeche an, auf der sie liegen - gesperrte Eintraege werden damit von selbst blass - und sie bleiben auf jedem Bildschirm scharf. Die Zeilenvorschau wird gerechnet: zwei, drei oder vier Kaesten aus derselben Funktion. Suche. Ein Feld ueber der Liste filtert ueber alle Gruppen, gesucht wird ueber Namen, internen Typ und Gruppennamen - wer "slider" eingibt, muss nicht wissen, dass er unter "Gruppen" steht. Gruppen ohne Treffer verschwinden, darunter steht wie viele von wie vielen uebrig sind. Dabei bemerkt: es gab zwei Suchfelder namens "Element suchen" auf einem Bildschirm, fuer eine Sprachausgabe nicht unterscheidbar. Sie heissen jetzt nach dem, was sie durchsuchen. Geprueft: der Browsertest zaehlt die Vorschaubilder gegen die Eintraege und sucht nach Name, Gruppenname und etwas Nichtvorhandenem. npm test 28/28, phpunit 452/452, npm run test:editor vollstaendig ohne axe-Verstoss. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
9726af9254 |
Fassung 1.5.3: der Editor blieb nach dem Update schwarz
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 fuer jede .js-Datei Cache-Control: immutable, max-age=31536000 - ein Jahr. Die Fassungskennung haengt aber nur an den Einstiegsdateien, die die Vorlage schreibt. Die fuenf Module, die der Editor ueber import './...js' laedt, 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, und editor.js importiert canvas-overlay.js in Zeile 5; bricht der Import, laeuft keine Zeile danach und die Elementliste bleibt leer. Das gemeldete fehlende Menue war der Werkzeugkasten, nicht die Verwaltungsnavigation - die wurde geprueft und ist in Chrome 152 in Ordnung. Behebung: eine Importkarte in base.html.twig haengt die Fassung an die aufgeloeste Adresse jedes Moduls, ohne dass eine Importzeile sie kennen muss. Das Zwischenspeichern ueber ein Jahr bleibt - 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; csp_nonce() gibt ihn der Karte mit. Der Nonce erlaubt genau dieses eine Skript, 'self' gilt unveraendert fuer alles andere. Geprueft: der Browsertest verlangt an jeder angeforderten .js-Adresse eine Fassungskennung (vorher fehlten fuenf, jetzt tragen alle acht), dazu ein Test, der jeden Modulimport gegen die Importkarte haelt - Gegenprobe gemacht. npm test 28/28, phpunit 452/452, npm run test:editor vollstaendig. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
7225ae052e |
Fassung 1.5.2: die Optionen selbst
Drei Bedienungen, die Flatsome an jeder Option hat und die hier fehlten. Geraeteschalter je Option. Bisher entschied ein einziger Schalter ueber der Vorschau, welches Geraet das ganze Bedienfeld bearbeitet; "ist die Ueberschrift auf dem Telefon kleiner" liess sich nur durch Umschalten und Hinsehen beantworten. Jetzt traegt jede Option drei Chips (D, T, M): sie zeigen, wo ein eigener Wert liegt, und ein Klick wechselt dorthin. Bedienfeld und Vorschau wechseln gemeinsam - einen Wert fuer ein Geraet zu setzen, das man nicht ansieht, ist blindes Aendern. Abstand als Rahmen. Vier Felder untereinander, beschriftet "Oben, Rechts, Unten, Links", zwangen zum Lesen. 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 frueheren Knopf "Alle Seiten gleich setzen", und das war eine bewusste Handlung. Seiten mit unterschiedlichen Einheiten gelten als "gemischt" und bleiben unangetastet - ein gespeichertes 2rem wird nicht still zu 2px. Ziehfelder. Zahlenfelder lassen sich seitwaerts ziehen, Umschalt grob, Alt fein. Tippen und Pfeiltasten bleiben unveraendert. Nur fuer Maus und Stift: auf einem Tippgeraet ist ein seitliches Ziehen ueber ein Feld die Art, wie das Bedienfeld gescrollt wird. Dabei behoben: der Zug war nach einem Pixel zu Ende, weil jede Aenderung das Bedienfeld neu zeichnete und damit das Feld ersetzte, an dem der Zeiger hing (keepPanel). Der Kettenschalter verlor aus demselben Grund seinen Zustand. Und das Gleichziehen nahm den Wert des ersten Feldes, auch wenn es leer war - ein Druck auf die Kette loeschte damit die Seiten, die einen Wert hatten. Geprueft: npm test 27/27, phpunit 452/452, npm run test:editor vollstaendig mit neuem Abschnitt und ohne axe-Verstoss. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
0e2334d30f |
Fassung 1.5.1: der Werkzeugkasten kennt den Zusammenhang
Umuts Einwand: Zeile und Spalte standen oben in der Auswahl, als waeren sie Seitenanfaenge. Sie gehoeren in den Inhalt - erst eine Sektion, dann eine Zeile darin. Das war nicht nur eine Anzeigefrage: eine Zeile nahm bisher jedes Kind an, eine Ueberschrift konnte neben den Spalten landen, und eine Spalte durfte ueberall stehen. Die Struktur, die der Editor in seinem eigenen Hinweistext beschreibt, galt fuer die Schaltflaechen nicht. Uebernommen ist Flatsomes Regelmodell (Elements.php): ALLOW - eine Zeile nimmt nur Spalten; REQUIRE - eine Spalte lebt nur in einer Zeile. Dazu ein Zusatz, den Flatsome nicht hat: ROOT_NEEDS_CONTAINER - eine Zeile ist kein Seitenanfang. In jedem Behaelter bleibt sie erlaubt, auch in einem Tab. Daraus die Bedienung: - Werkzeugkasten nach Aufbau gruppiert statt flacher Liste aus 31 Typen. - Was nicht hingehoert, ist gesperrt und sagt beim Ueberfahren warum. Eine Regel, die man erklaert bekommt, lernt man; eine Schaltflaeche, die nur nicht mehr reagiert, drueckt man weiter. - Was moeglich ist, sagt wohin es kommt. - Die Zeile wird nach Spaltenzahl angeboten und bringt ihre Spalten mit; der separate Zeilenbereich ist entfallen. - insertionTarget beantwortet "wohin kaeme das?", ohne etwas einzufuegen. Knopf, Einfuegepunkt, Ziehen und die Sperrlogik nutzen dieselbe Funktion - vorher entschied insertBlock allein, und insertRow hatte eine zweite Kopie derselben Platzierungslogik. Ein Element, das der gewaehlte Behaelter nicht nimmt, wird nicht gesperrt, sondern steigt auf, bis ein Elternteil es annimmt. Nebenbei behoben: postnav fehlte seit Schema v18 in labels und icons, der Strukturbaum zeigte dafuer "Unbekannt" und ein Fragezeichen. Geprueft: npm test 27/27, phpunit 446/446, npm run test:editor vollstaendig mit neuem Abschnitt und ohne axe-Verstoss. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
22e4ada830 |
Fassung 1.5.0: die Vorschau wird zur Arbeitsflaeche
Abgleich mit dem Flatsome UX Builder (docs/FLATSOME-ABGLEICH.md): die Gestaltung ist Flatsome voraus, die Luecken liegen beim Elementkatalog und bei der Bedienung. Umuts Entscheidung: Bedienung zuerst, Shop-Elemente ausgelassen. Erste Haelfte der Bedienung: - Werkzeugleiste am Element in der Vorschau. Sie nennt das Element, auf das sie wirkt - die Seitenleiste hat dieselben fuenf Aktionen, und zwei gleichnamige Bedienelemente auf einem Bildschirm sind fuer eine Sprachausgabe nicht unterscheidbar. Beide Wege rufen dieselben Funktionen auf. - Auswahl- und Ueberfahrrahmen mit Beschriftung. - Einfuegepunkte an Ober- und Unterkante: das naechste Element landet dort statt am Ende. - Ziehen aus dem Werkzeugkasten in die Vorschau, mit Ablegemarke zwischen den Elementen und Rahmen um einen aufnehmenden Behaelter. Der Strukturbaum zieht in dieselbe Flaeche. Die Ebene liegt ueber dem Rahmen, nicht darin: die Vorschau wird bei jeder Aenderung ersetzt, eingezeichnete Bedienelemente waeren weg und stuenden in der Auszeichnung, die die Vorschau wahrheitsgetreu zeigen soll. Leiste und Pluszeichen werden gegenskaliert und bleiben in der Mobilvorschau fingergross. Im Modell neu: insertBlockAt, moveBlockTo, dropList. Dabei behoben: die Ebene war aria-hidden und enthielt fokussierbare Schaltflaechen; canContain zerbrach an einem fehlenden Block beim Ziehen aus dem Werkzeugkasten; beim Verschieben ans Listenende wurde der Index begrenzt, bevor die Entnahme verrechnet war. Geprueft: npm test 22/22, phpunit 434/434, npm run test:editor vollstaendig mit neuem Abschnitt und ohne axe-Verstoss. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
c517173579 |
Fassung 1.4.1: dem Releasearchiv fehlten migrations.php und cli-config.php
Auf dem Hosting brach die Installation im dritten Schritt ab - "The configuration file does not exist", genau dort, wo die Tabellen entstehen sollten. Beide Dateien standen weder in der Mitnahme- noch in der Ausschlussliste des Releaseskripts. Ein Test ordnet jetzt jeden Eintrag im Projektstamm einer der beiden Listen zu: was neu dazukommt und unbedacht bleibt, laesst ihn fehlschlagen. Der Installationsweg wurde danach vollstaendig durchgespielt - 3504 Dateien entpackt, 14 Migrationen ausgefuehrt, 15 Tabellen angelegt. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
de1e66350b |
Knopf nach oben, Bildbetrachter, Rechtszeile als Baustein (Schema v19)
Der Knopf "nach oben" ist ein Anker, kein Skript: er springt an den Seitenanfang, eingeblendet ueber eine Scroll-Zeitachse. Abschaltbar und vollstaendig einstellbar - Seite, Form, Groesse, Farben, Schwelle, Beschriftung. Bilder lassen sich vergroessern, je Bild waehlbar (nichts / vergroessern / Adresse), Galerien mit einem Schalter. Grundlage ist ein gewoehnlicher Verweis auf die grosse Fassung: ohne JavaScript oeffnet er das Bild, mit JavaScript einen dialog-Betrachter mit Blaettern, Escape und Pfeiltasten. Das Skript laedt nur, wo es etwas zu tun hat. Die Rechtszeile wird ein Baustein und steht damit im Fussbereich statt als heller Streifen darunter. Die eingebaute Leiste erscheint nur noch, wenn die Seite den Baustein nicht enthaelt. Dabei gefunden: zwei Stylesheet-Regeln griffen seit drei Veroeffentlichungen nicht mehr. Beim Entfernen alter Regeln waren Selektorreste stehen geblieben, und CSS nimmt dann die naechste Regel mit - betroffen waren der Einwilligungsdialog und die Rechtszeile. Ein Test prueft jetzt jede CSS-Datei auf abgebrochene Selektoren. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
6c477a52be |
Vorschau zeigt nie eine Seite ohne ihre Gestaltung
Gemessen: die Farben in der Vorschau und auf der veroeffentlichten Seite waren immer identisch. Der Aufbau war das Problem - die erzeugten Stile wurden im load-Ereignis des Rahmens angewandt, und das feuert erst, wenn das letzte Bild geladen ist. Auf der nachgebauten Startseite stand die Vorschau dadurch 1440 ms lang ohne eine einzige Farbe da. Jetzt wird angewandt, sobald das Dokument geparst ist; der Rahmen bleibt bis dahin verdeckt; und gestaltet wird nur das neue Dokument - der erste Anlauf gestaltete das alte und schaltete zu frueh sichtbar. Die Editorkontrolle prueft dagegen das Verhalten statt der Zeit: der Rahmen muss beim Aufbau verdeckt werden und danach wieder sichtbar. Eine Zeitmessung haette auf der kleinen Testseite nichts gefunden. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
a2dfd0f796 |
Schriften ins Repository, Anleitung fuer neue Websites, Schutzkopfzeilen
Die Lato-Dateien liegen jetzt unter public/assets/fonts/ samt OFL-Text und einer Lizenznotiz: auf einem frischen Server fehlt nichts, und es wird nie versehentlich von Google geladen. docs/NEUE-WEBSITE.md beschreibt, wie eine neue Kundenwebsite entsteht - Reihenfolge, Dokumentformat, Bilder, SEO, Pruefungen - und nennt am Ende die fuenf Fehler, die hier immer wieder passiert sind. Permissions-Policy ergaenzt (Kamera, Mikrofon, Standort, Sensoren aus), X-Powered-By entfernt: der Server nannte in jeder Antwort seine genaue PHP-Fassung. Die Browserpruefung misst die Kopfzeilen jetzt. Behoben: der Editor kannte Schema v17, der Validator v18 - die Beitragsnavigation liess sich in keinem aktuellen Dokument einfuegen. Ein Test bindet die Zahl des Editors an die des Validators. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
074ef0e7e5 |
Eigener Suchtitel, Schriften lokal, Bilder in Anzeigegroesse
Getrennter Titel fuer Suchergebnisse je Seite und Beitrag (Migration 20260913000100): auf der Seite steht "Leistungen", im Suchergebnis darf der lange Titel stehen. Leer heisst weiterhin: der Titel der Seite. Schriften kommen jetzt vom eigenen Server. Ein Knopf im Designbereich oder cms:fonts:fetch holt die gewaehlten Google-Schnitte als WOFF2 nach public/assets/fonts. Das spart zwei fremde Verbindungen nacheinander (gemessen 820 ms) und die IP-Weitergabe an Google. Ohne Dateien bleibt es beim bisherigen Verweis - eine Seite ohne Schrift waere schlimmer. Ein Stylesheet statt vier, mit Pruefsumme im Namen und ohne Kommentare, dazu eine .htaccess mit Kompression, Cache und Schutz interner Dateien. Der Entwicklungsserver sendet dieselben Kopfzeilen, sonst misst man lokal etwas, das es im Betrieb nicht gibt. Bilder in der Groesse, in der sie erscheinen: ein nur fuer Desktop gesetztes Hintergrundbild gilt auf dem Telefon in Telefongroesse (189 KB -> 40 KB), img nennt seine feste Breite oder sizes="auto", und fetchpriority liegt auf dem ersten Bild des Inhalts statt auf dem Logo. Neu: npm run test:links prueft jeden internen Verweis auf seine Antwort. Lighthouse gegen den Nachbau: Barrierefreiheit, Best Practices und SEO 100 auf allen vier geprueften Adressen, Performance 100 ausser auf der bilderreichen Startseite (98 mobil, 96 Desktop). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
f2ef2a4901 |
Beitraege optional, Unterstriche nur im Text, eigenes Symbol
Eine Website ohne Beitraege redet nirgends von Beitraegen: Archiv und Feed antworten mit 404, der Feedverweis faellt aus dem Kopf, und der Hinweiskasten der Beitragsliste bleibt im Editor statt oeffentlich zu erscheinen. Das gilt auch je Kategorie, sobald deren letzter Beitrag weg ist. Dabei gefunden: die Liste fragte je Beitrag nach Bild und Begriffen - jetzt je eine Abfrage fuer die ganze Liste (22 -> 16 Abfragen auf der Startseite des Nachbaus). Unterstrichene Links gibt es nur noch im Fliesstext, dort mit einem Schalter im Designbereich; Karten, Menue, Kontaktzeilen und der Ansehen-Streifen haben keinen mehr. Die aktuelle Seite im Menue wird ueber das Schriftgewicht kenntlich - die Akzentfarbe fiel im dunklen Fussmenue durch die Kontrastpruefung. Neu: das Symbol der Website laesst sich festlegen. Aus einem Bild der Mediathek entstehen beim Speichern quadratische PNG-Dateien in 32 und 180 px; schlaegt das fehl, wird die Einstellung nicht gespeichert. Die Browserpruefung laeuft jetzt gegen jede Installation statt nur gegen die Demo: geprueft wird, was in der Sitemap steht, und das Ladebudget trennt Bilder vom Beiwerk. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
e34b4dca67 |
Installer: sechs Bildschirme, und eine Datei davor
/install fuehrt von einer leeren Installation zu einer Website: Systempruefung, Datenbank (SQLite oder MySQL, Verbindung sofort geprueft), Migration im Prozess ohne Konsole, Website mit Startseite, Bereichen, Menue sowie Impressum und Datenschutzgeruest, erstes Konto, Abschlussliste. Jeder Schritt schreibt sein Ergebnis sofort, damit ein Neuladen nichts verliert. Gesperrt wird ueber den Zustand der Datenbank - gibt es ein Konto, antwortet /install mit 404. Eine Datei koennte jemand loeschen. Solange nichts installiert ist, fuehrt jede Adresse zum Installer statt zu einem 500 aus einer fehlenden Tabelle. Und weil ohne APP_SECRET nicht einmal die Seite bootet, die das erklaeren wuerde, erzeugt der erste Start das Geheimnis selbst. scripts/install.php ist die einzelne Datei davor: Adresse und SHA-256 stehen darin (vom Buildskript eingetragen), ohne passende Pruefsumme wird nicht entpackt, jeder Archiveintrag wird gegen das Zielverzeichnis geprueft, und danach loescht sie sich selbst. Geprueft: 379 Tests unter PHP 8.2 und 8.4, darunter der ganze Ablauf durch die HTTP-Schicht, neun ZIP-Slip-Varianten, axe ohne Verstoss auf allen Installationsbildschirmen, und das entpackte Releasearchiv startet in den Installer. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
c3bff7d936 |
Bilder in vier Breiten, Releasearchiv fuer Hosting ohne Composer
Uploads werden weiterhin nach WebP umgewandelt, jetzt in vier statt drei Breiten (480/960/1600/2400) und ohne Hochskalieren. Hintergrundbilder waehlen ihre Variante je Geraet, weil CSS kein srcset kennt. Dabei kam ein Fehler heraus, den erst der Browsertest zeigte: der Dokumentpruefer erlaubte drei sources je Bild, also war mit der vierten Variante jedes Dokument mit einem Bild ungueltig - ohne sichtbare Meldung. Die Grenze haengt jetzt an MediaLibrary::WIDTHS. Geteiltes Hosting hat meistens keinen Composer. scripts/build-release.php baut deshalb auf einem Build-Rechner ein Archiv mit fertigem vendor/ ohne Entwicklungspakete, samt SHA-256. Der Updater bricht ab, wenn ein Update composer.lock aendert und Composer auf dem Server nicht aufrufbar ist, statt mit einem unpassenden vendor/ weiterzulaufen. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
269fb223af |
Messen statt vermuten: 18 Abfragen auf 8, Ladebudget, Blaettern ueberall
Arbeitspaket M sagte "Abfragen je Seite zaehlen. Messen, dann zusammenfassen." Messen braucht ein Messgeraet, also gibt es jetzt eines: ein Zaehler als Doctrine-Middleware, dessen Zahl ausserhalb der Produktion im Kopf X-Kant-Queries steht. Keine Profiler-Maschinerie, eine Zahl. Die erste Messung: die Startseite brauchte 18 Abfragen. Nicht wegen einer komplizierten Seite, sondern weil SiteSettings::load() und Theme::load() je Aufrufer dieselbe Zeile erneut holten - Renderer, strukturierte Daten, Einwilligungsschalter, Vorlagen. Beide merken sich ihren Stand jetzt fuer die Dauer einer Anfrage: 18 auf 8. Dabei ist mir die klassische Falle des Merkens selbst passiert, und ein bestehender Test hat sie sofort gezeigt: ich leerte den Merker am Anfang des Speicherns. Das Speichern liest den Stand aber noch einmal und fuellte ihn damit erneut - der Rest der Anfrage bekam den alten Stand. Geleert wird jetzt nach dem Schreiben. Das Ladebudget wird gemessen statt behauptet: die Browserpruefung liest je Seite HTML-Groesse, CSS-Groesse, Anzahl Anfragen, Anzahl Skripte und Abfragezahl und prueft Obergrenzen. Startseite: 7,4 KB HTML, 22,7 KB CSS, 2 Anfragen, 8 Abfragen, 0 Skripte. Die Null ist die wichtigste Zahl - sie haelt als Test fest, was bisher nur Absicht war. Das erste Bild einer Seite ist nicht mehr lazy. Es ist meistens das groesste Ding ueber der Falz, und es zu verzoegern verzoegert genau das Pixel, auf das jemand wartet. Der Renderer bestimmt es in Renderreihenfolge, Kopfbereich zuerst. Eine feste Obergrenze ist kein Blaettern: der Fassungsverlauf hoerte bei dreissig auf, die Einsendungen bei fuenfzig, die Weiterleitungen zeigten bis zu zweitausend auf einmal. Das Erste sieht aus wie "es gibt dreissig" und ist das Einzige, was eine Historie nicht tun darf. Alle drei blaettern jetzt, mit derselben Rechnung und demselben Pager wie Beitragsarchiv und Mediathek. 347 PHP-Tests / 1422 Assertions unter PHP 8.2.12 und 8.4.22. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
52f0ab3807 |
Updater: neue Fassung über Git, mit Vorprüfung und Rückweg
Aus dem Plan in docs/UPDATE.md ist Code geworden: cms:update, cms:health, der Bildschirm "System" und der Wartungszustand. Die Vorprüfung ist die halbe Arbeit. Sie fragt nach proc_open, Git, sauberer Arbeitskopie, Herkunft, Schreibrechten und Platz - und danach, ob Datenbank und Medienordner wirklich von Git ausgenommen sind. Das ist der Punkt, an dem ein Update Inhalte zerstören könnte, also wird er geprüft und nicht angenommen. Jede Prüfung sagt, was zu tun ist, nicht bloß "fehlgeschlagen". Der Rückweg ist Teil des Wegs: scheitert ein Schritt, wird auf den vorherigen Commit zurückgesetzt. Die Datenbank nur dann zurückgeschrieben, wenn die Migration schon begonnen hatte - sonst verlöre der Rückweg Inhalte, die während des Updates entstanden sind. Antwortet der alte Stand im Rauchtest, geht die Website wieder online; antwortet er nicht, bleibt der Wartungszustand, weil eine kaputte Seite schlechter ist als eine ehrliche Meldung. Der Rauchtest läuft als eigener Prozess, denn nur ein frischer Prozess führt den neuen Code aus. Ein Test im laufenden Prozess hätte den alten geprüft und "alles in Ordnung" gemeldet. Kein symfony/process: der Runner sind dreißig Zeilen über proc_open mit dem Befehl als Array - kein Shell, nichts zu maskieren, und "proc_open ist abgeschaltet" wird die erste Antwort statt der Überraschung im dritten Schritt. 285 PHP-Tests / 1244 Assertions. Sechs davon fahren den Updater gegen echte Git-Repositorys in einem Temp-Verzeichnis, mit Platzhalterskripten für die zwei Befehle - inklusive des Fehlschlags, nach dem Code und Datenbank zurückgesetzt sein müssen. Nicht gegen dieses Projekt: ein Test, der die Arbeitskopie auscheckt, in der er läuft, ist ein Weg, Arbeit zu verlieren. Offen bleibt die Sicherung einer MySQL-Datenbank per mysqldump - dafür fehlt die Installation; der Updater sagt das im Schritt "Sicherung". Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
99bad61179 |
Statistik: Zähler statt Protokoll, deshalb ohne Einwilligung
"Wieviel wann wo wie die Seite benutzt" - naheliegend wäre ein Analysewerkzeug gewesen, das eine Einwilligung braucht und damit genau die Besucher verliert, die ablehnen. Also andersherum: eine Messung, die niemanden wiedererkennt. 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, denn der Hash einer IP-Adresse bleibt ein Personenbezug. Ein Test liest die Spaltenliste der Tabelle und vergleicht sie mit dieser Zusage, damit sie beim nächsten Ausbau nicht unbemerkt fällt. Vom Verweis bleibt nur der Host: die volle Adresse kann einen Suchbegriff oder einen Namen tragen. Gezählt wird auf kernel.terminate, nach der Antwort - die Messung darf keine Anfrage verlangsamen, und ein Fehler beim Zählen kostet einen Zähler, nicht eine Seite. Nur GET auf HTML mit 200 oder 404; Verwaltung, Assets, Feed, Sitemap und Einwilligungsseite sind ausgenommen, erkennbare Maschinen über ihre Kennung. Das ist eine Heuristik, deshalb heißt die Zahl auf dem Bildschirm nicht "Besucher". Erfundene Crawler-Adressen laufen gesammelt unter (sonstige), sonst wächst die Tabelle mit jedem geprobten Pfad. Die Diagramme zeichnet der Server als SVG mit Klassen statt Stilattributen: kein Diagrammwerkzeug, kein Skript, und die Richtlinie bleibt streng. Die Zahlen stehen zusätzlich in einer Tabelle, weil ein Balken für einen Screenreader nichts ist. Was die Zahlen nicht sagen, steht auf dem Bildschirm: Aufrufe statt Besucher, keine Verweildauer, keine Absprungrate, kein Klickweg, kein Land. Das ist die Folge der Entscheidung, nicht eine Lücke. Aufbewahrung: nach 60 Tagen werden die Stunden eines Tages verschmolzen, nach 425 Tagen gelöscht - gut ein Jahr, damit der Vorjahresmonat vergleichbar bleibt. cms:stats:prune macht beides und gehört in einen Cronjob. 279 PHP-Tests / 1119 Assertions unter PHP 8.2.12 und 8.4.22, axe auf zwölf Verwaltungsbildschirmen ohne Verstöße. Dabei kam eine PHP-8.4-Verwarnung zu fputcsv() heraus, jetzt mit ausdrücklichem Escape-Parameter. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
ee446f1ea1 |
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> |
||
|
|
68380c6295 |
Mediathek mit Ablagefläche, Updateverfahren geplant
Das Auswahlfeld des Browsers sah aus wie das Auswahlfeld des Browsers. Hochgeladen wird jetzt über eine Ablagefläche: Bilder hineinziehen oder klicken, darunter steht, welche Dateien gewählt sind und welche übersprungen wurden. Darunter liegt weiter ein echtes input[type=file] - unsichtbar, aber fokussierbar, mit der Fläche als label und dem Fokusring über :focus-within. Ohne JavaScript bleibt genau das übrig: ein Dateifeld mit Beschriftung. Das Skript ergänzt Ziehen und benennt die Auswahl; abgeschickt wird nie von selbst. Kein "required", weil der Browser seine Meldung nicht an etwas Unsichtbares hängen kann - der Server weist einen leeren Upload mit einem lesbaren Satz ab. docs/UPDATE.md plant das Update über einen Git-Tag: Vorprüfung, Sicherung, Wartungszustand mit 503 für die Website und offenem /admin, Checkout, composer, Migration, Cache, Rauchtest, Protokoll - und den Rückweg in derselben Reihenfolge rückwärts. Migrationen bleiben nicht umkehrbar, der Rückweg ist die Sicherung. Kein Updater, der eine Adresse aus dem Formular zieht, und kein automatisches Update im Hintergrund. Zwei Entscheidungen hängen am Hosting: Composer auf dem Server oder vendor/ im Tag, und ob es Shell-Zugriff gibt. Gebaut ist davon nichts. 219 PHP-Tests / 881 Assertions unter PHP 8.4.22, darunter Upload über das Formular mit zwei Dateien, CSRF-Pflicht, Versionskonflikt, Archivfilter und Blättern mit 26 Bildern. Playwright prüft die Ablagefläche am echten Dateifeld; axe bleibt auf elf Bildschirmen ohne Verstöße. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
14031a5880 |
Benutzerverwaltung in der Oberfläche, Mediathek als eigene Seite
Konten gab es bisher nur über die Konsole. Unter "Benutzer" legt ein Administrator jetzt Konten an, ändert Rollen, setzt Passwörter und löscht Konten; unter "Mein Konto" ändert jeder sein eigenes Passwort, aber nur mit dem aktuellen in der Hand. Eine Rolle je Konto: Administrator, Veröffentlicher, Redakteur. Ein Passwort oder eine Rolle zu ändern meldet jede offene Sitzung dieses Kontos ab - dafür war nichts zu bauen, CmsUser::isEqualTo vergleicht Hash und Rollen mit der Datenbank. Genau eine Sitzung überlebt: die, die ihr eigenes Passwort geändert hat. "Jemanden sofort aussperren" ist damit dasselbe wie "Passwort setzen". Es bleibt immer ein Administrator übrig, nicht über eine Zählung, sondern weil niemand sein eigenes Konto löschen oder herabstufen kann und jede Änderung die Administratorrolle voraussetzt. Die zusätzliche Prüfung auf einen zweiten Administrator war damit unerreichbar und ist wieder heraus. Mediathek (Umut: "das sollte direkt laden in pagination"): /admin/media zeigt die Bilder jetzt selbst, 24 je Seite, neueste zuerst, mit Blättern und einem Schalter für archivierte. Hochladen und Bildangaben laufen über gewöhnliche Formulare ohne JavaScript; der Dialog bleibt der Weg, beim Bearbeiten ein Bild auszuwählen. Die Bild-ID steht in jeder Karte, weil Beitragsbild und Teilen-Vorschau genau diese ID erwarten. Die Migrationsliste der Testfixtures stand dreimal da und steht jetzt einmal in tests/TestSchema.php. 217 PHP-Tests / 845 Assertions unter PHP 8.2.12 und 8.4.22. axe deckt jetzt elf Bildschirme ab statt drei - alle Verwaltungsseiten, nicht nur Anmeldung, Seitenliste und Editor -, null Verstöße. Nicht geprüft: kein Screenreader; Blättern und Upload sind mit zwei Bildern geprüft, nicht mit einer großen Bibliothek. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
33e4ac0c54 |
Frei wählbare Startseite; Standarddateien waren lokal unerreichbar
Eine Quelle entscheidet jetzt, welche Seite unter / antwortet: SiteSettings::homePath() und publicPath(). Das Literal home kommt im Anwendungscode nicht mehr vor - öffentliche Routen, Canonical, Sitemap, llms.txt, Menüauflösung, Beitragslisten und der Formularrücksprung benutzen dieselben zwei Methoden. Die gewählte Seite muss veröffentlicht und nicht im Papierkorb sein, ihre eigene Adresse leitet dauerhaft auf /, und als Wurzel ist sie gegen Umbenennen und Papierkorb gesperrt. Zweiter echter Fehler, den nur ein HTTP-Aufruf zeigt: /sitemap.xml, /robots.txt, /llms.txt und /feed.xml waren lokal nie erreichbar. Der eingebaute PHP-Server antwortet für fehlende Pfade mit Dateiendung selbst mit 404, ohne den Frontcontroller zu fragen; alle Kernel-Tests bestanden trotzdem, weil sie die Anwendung direkt aufrufen. bin/dev-router.php bildet die Rewrite-Regel des Hostings nach, Start- und CI-Befehl nutzen es, und die Browserprüfung ruft die vier Adressen jetzt über HTTP auf. Barrierefreiheitsbefunde werden nach Ursache getrennt: ein Kontrastfehler aus Theme oder Stylesheet bleibt ein Defekt und bricht den Lauf ab, einer aus einer im Editor gesetzten Blockfarbe wird bei jedem Lauf mit Seite, Farbe und Verhältnis genannt und in reports/browser.json geschrieben, färbt aber keinen Codewechsel rot. Geprüft: 162 PHP-Tests / 636 Assertions unter PHP 8.2.12 und 8.4.22, 15 JavaScript-Modelltests, vollständiger Editortest mit axe, wieder grüne öffentliche Browserprüfung und Lighthouse mit je drei Läufen mobil und Desktop, alle fünf Kategorien 100. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
ba56add678 |
Browsertests wiederhergestellt; drei echte Fehler gefunden
Der Playwright-Editortest war durch den Editorumbau kaputt: er steuerte die abgeschafften Auswahllisten. Umgeschrieben auf die freien Bedienelemente - Einheitenfelder, Vererbung über drei Geräte, einzelnes Zurücksetzen, Palettenfarbe, Abstand je Seite, Hover-Regel, freie Rasterspalten und die weiter vorhandenen Bewegungseffekte. Dabei kamen drei Fehler heraus, die ohne Browserlauf unsichtbar sind: 1. Die frei gestalteten Werte wirkten in der Editorvorschau gar nicht. Ein srcdoc-Iframe erbt die Richtlinie des einbettenden Dokuments, und die Verwaltung verbietet Inline-Styles - das erzeugte style-Element wurde stillschweigend verworfen. Ausgeliefert wurde es korrekt, nur die Vorschau log. Jetzt als inertes template plus adoptedStyleSheets. 2. Die Einrückung der Unterseiten in der Seitenliste hat nie funktioniert; sie lief über ein style-Attribut, das dieselbe Richtlinie verwirft. Jetzt über Klassen. 3. Das Theme setzte alle Links auf die Akzentfarbe und überschrieb die kontextabhängigen Farben: 3,08:1 auf dunklem Grund. Die Regel gilt jetzt nur für Links in Textblöcken, die voreingestellte Linkfarbe liegt auf allen hellen Tönen über 4,5:1. Geprüft: 155 PHP-Tests / 579 Assertions unter PHP 8.2.12 und 8.4.22, 15 JavaScript-Modelltests, vollständiger Editortest mit axe (null Verstöße) und Lighthouse 13.4.1 mit je drei Läufen mobil und Desktop, alle fünf Kategorien 100. Offen: npm run test:browser schlägt wegen eines Inhalts fehl - /grundlagen trägt einen im Editor gesetzten Textblock mit 2,75:1. Nicht verändert, weil es eine Nutzerentscheidung ist. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
82b07820bc |
Kant CMS: Grundgerüst, visueller Editor, Mediathek
Symfony 7.4 / Twig / Doctrine DBAL auf herkömmlichem PHP-Webhosting. Versionierte Blockdokumente (v1-v3) mit unveränderlichen Revisionen, getrennten Entwürfen/Veröffentlichungen und optimistischer Konflikterkennung. Visueller Editor mit Strukturbaum, Live-Vorschau, responsiven Gerätewerten, Abschnittsvorlagen, Bildmediathek, Galerien und Effekten. Öffentliche Ausgabe ohne Editor-JavaScript. Geprüft: 41 PHP-Tests / 155 Assertions, 10 JavaScript-Modelltests. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |