- The installer only opens with a per-installation key that install.php
creates and shows; the key is deleted with the first account.
- Lock files keep .env, the database, backups and logs out of reach when a
domain points at the project folder instead of public/.
- Linked sections render by their own schema version, so an old section
cannot carry unfiltered HTML into a newer page.
- Editors no longer move, trash or change areas and post data of published
pages; a scheduled publication goes live with the revision that was
scheduled (migration 20261012000100).
- Form stamps are bound to their form, IPv6 senders count per /64, and a
flood per form or overall stops storing with a visible message.
- The consent redirect rejects control characters, update targets must look
like 1.2.3, and own CSS cannot reach outside its block.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Coming-soon page "Kantarci Markt – Neueröffnung" rebuilt from the delivered package with
imprint, privacy and 404 (scripts/reset-kantarci-markt.sh, ports 8100/8101).
- Own CSS accepts a short quoted string for `content`, so ::before/::after work.
- cms:fonts:fetch replaces only the rules of the fetched families in fonts.css.
- A variable font is linked and preloaded once with a weight range instead of once per weight.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Built while setting up the redesign of ebsolar.de as the sixth local
installation (scripts/seed-ebsolar.php, reset-ebsolar.sh, ports 8096/8097).
The rebuild surfaced four things in the core:
- The icon set knows three arrows now: arrow-right, arrow-left,
arrow-up-right. The button block and the post navigation render them as
inline SVG inside the same aria-hidden span as before, so a template that
hides the arrow with "& span{display:none}" keeps working. Until now the
arrow was a text glyph that looked different in every font. Umut: "bei
alle Projekte bzw bei allen möchte ich dieses emoji icon weg haben".
- Own CSS with the same nested selector twice kept only the last rule.
"& .post-card{border:0}" followed by "& .post-card{transform:none}" lost
the border - silently, which brought the core's card frame back on the
project grid. The parser now joins the declarations in source order, as
CSS does; the same for "&{…}" next to the element's own properties.
- The eager image over the fold was not preloaded. The background image of
the first section has had a preload link for a while; a plain <img> was
found only after the stylesheet was parsed, and that round trip went
straight into LCP. PageController::heroImage() now writes the same src,
srcset and sizes the block template writes, and page.html.twig emits the
link - unless a background image is already preloaded.
- Measured on the way: an entrance effect on the LCP element costs about
900 ms of render delay. The EB Solar hero image therefore has none.
Tests: 581 green, the icon test accepts quoted keys, two cases for the CSS
cascade, the preload asserted in the big-picture test.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Umut asked for a block that takes custom HTML, and for an iframe on a new
DEEG offers page. Only one of those was missing.
New core block "Eigenes HTML" (document schema v24): one property, the
markup, up to 20.000 characters, written to the page unchanged. Changing it
is reserved for administrators and compared against what is stored - on
saving, on saving an area, and on restoring an older revision - exactly like
an own script. <script> and <iframe> are refused with the way to the right
place instead of being swallowed by the content security policy: scripts
belong in the script field, frames in the "Externer Inhalt" block, because
only that one asks for consent and puts the host into frame-src (ADR 106).
DEEG: /angebote existed as a draft with placeholder text. It is now built
like the legal pages - small caps line, large light heading, lead on the
tinted band - with the IS24 result list below it and a line pointing to the
portal and to the contact form. Navigation and the offers band on the home
page point there. The consent question is switched on, so the frame loads
only after the visitor agrees, and both the banner and the placeholder carry
the site's own shapes and colours.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
SITE_ORIGIN alone decides whether the site is www or not: CanonicalHostSubscriber
sends the other spelling there with 301, cms:site:origin writes the setting, the
system screen shows it. The roles now match the role help (editor: drafts,
publisher: live content, admin: design, settings, users, system). The installer no
longer reopens on an unreachable database, the session cookie follows ADMIN_PATH,
percent-encoded credentials no longer break the container, consent only accepts
same-site decisions, JSON-LD is HTML-safe, own CSS stays in its block, plus the
editor, updater, download and public-output findings from the full review
(ADR 099, ADR 100).
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Dokumentschema v13. Der neue Reiter "Erweitert" im Editor nimmt zu jedem
Element eigenes CSS und eigenes Skript auf.
Eigenes CSS parst `CustomCode` und schreibt es selbst wieder aus, mit einem
Selektor, den die Klasse setzt und der nie aus der Eingabe stammt. Erlaubt
sind Eigenschaften für das Element und verschachtelte Regeln mit `&`
(`&:hover`, `& > p`, `& .block-text`). Weil neu serialisiert wird, kann eine
überzählige `}` den Block nicht verlassen. Abgewiesen: @-Regeln,
Backslash-Escapes, `expression()`, fremde Selektoren und jede Adresse außer
einem einzelnen `/` oder `https://`. Das darf jeder mit Editorrolle.
Eigenes Skript ist etwas anderes: beliebiges JavaScript auf der
veröffentlichten Website. Deshalb nur für Administratoren, durchgesetzt beim
Schreiben über einen Vergleich mit dem gespeicherten Stand - ein Editor kann
eine Seite mit Skript weiter bearbeiten, das Skript aber nicht anfassen, auch
nicht über eine selbst gebaute Anfrage. Ausgeliefert wird es als
`/assets/seite/{id}.js` und nur für veröffentlichte Seiten, jedes Skript in
einer eigenen Closure mit gesuchtem `element` und `try`/`catch`. Nur eine
Seite mit eigenem Skript sendet `script-src 'self'`, jede andere behält
`script-src 'none'`; inline ist nirgends erlaubt. In der Editorvorschau wird
nichts ausgeführt.
Zwei echte Löcher kamen beim Testen heraus und sind behoben: `& .block-text`
wurde zu `#id.block-text`, weil das führende Leerzeichen wegtrimmt wurde, und
`url(//fremd.example/x.png)` kam durch, weil jede Adresse mit `/` erlaubt war.
201 PHP-Tests / 716 Assertions unter PHP 8.2.12 und 8.4.22, Playwright-
Editortest und öffentliche Browserprüfung grün. Nicht geprüft: der Reiter
"Erweitert" ist nicht von Hand bedient und nicht mit einem Screenreader
getestet.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>