Add an own-HTML block, and build the DEEG offers page on the embed block

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>
This commit is contained in:
kantarciandClaude Opus 5 committed 2026-09-16 08:27:43 +02:00
1 parent 6b1aa50d02
commit 59a40157a4
15 files changed
+465 -8

No files matched your search

+42
View File
@@ -940,6 +940,48 @@ try {
await page.locator('.notice.ok').waitFor();
checks.push({ name: 'plugin switched on, block inserted from the toolbox, schema panel, assets on the public page, switched off keeps the block', passed: true });
// --- Der Baustein „Eigenes HTML“ (ADR 106) -------------------------------------------------
// Markup des Betreibers, unveraendert bis auf die oeffentliche Seite. Geprueft wird der Weg,
// den ein Administrator geht: einfuegen, eintippen, Vorschau, veroeffentlichen - und dass
// <script> und <iframe> mit einer Meldung abgewiesen werden statt still zu verschwinden.
await page.goto(origin + '/admin/pages/test-home');
await treeSelect('headline');
await addFromLibrary('[data-add="html"]');
await page.locator('#properties-title').filter({ hasText: 'Eigenes HTML' }).waitFor();
await page.getByLabel('HTML', { exact: true }).fill('<table class="preise"><tbody><tr><td>Miete</td><td>ab 8 &euro;/m&sup2;</td></tr></tbody></table>');
// Erst der Fokuswechsel schickt den Wert ins Dokument; die Vorschau folgt kurz darauf.
await page.locator('#preview-status').click();
await page.waitForFunction(() => Boolean(document.querySelector('#preview').contentDocument.querySelector('.block-html table.preise')), null, { timeout: 20000 });
await page.locator('#save').click();
await page.locator('#save-status').filter({ hasText: 'Entwurf gespeichert' }).waitFor();
await page.locator('#publish').click(); await page.locator('#confirm-publish').click();
await page.locator('#save-status').filter({ hasText: 'Veröffentlicht' }).waitFor();
{
const html = await (await page.request.get(origin + '/')).text();
assert.match(html, /class="block-html"><table class="preise">/, 'Eigenes HTML unverändert auf der Website');
// Der Baustein selbst bringt kein Skript auf die Seite - die Tabelle steht da, wie sie
// eingegeben wurde, und sonst nichts. (Dass eine Seite ohne Skript weiterhin
// script-src 'none' sendet, prüft CmsTest; hier trägt das Plugin von vorhin schon eines bei.)
assert.equal(html.includes('<table class="preise"><tbody><tr><td>Miete</td>'), true, 'Markup Zeichen für Zeichen');
}
for (const [markup, meldung] of [
['<script>alert(1)</script>', /Eigenes HTML führt keine Skripte aus/],
['<iframe src="https://example.com"></iframe>', /Externer Inhalt/],
]) {
await page.getByLabel('HTML', { exact: true }).fill(markup);
await page.locator('#preview-status').click();
await page.locator('#save').click();
// Abgewiesen wird schon die Vorschau, und das Speichern meldet es in der Leiste über dem
// Editor - die Seite bleibt, wie sie veröffentlicht ist.
await page.locator('#message').filter({ hasText: meldung }).waitFor();
}
await page.getByLabel('HTML', { exact: true }).fill('<p class="notiz">Wieder gut.</p>');
await page.locator('#preview-status').click();
await page.locator('#save').click();
await page.locator('#save-status').filter({ hasText: 'Entwurf gespeichert' }).waitFor();
await page.screenshot({ path: 'reports/editor/own-html.png', fullPage: true });
checks.push({ name: 'own HTML block: inserted, previewed, published unchanged, script and iframe refused', passed: true });
// Every management screen, not just the page list: they carry tables, disclosures and now the
// media grid, and none of that was ever checked automatically before.
for (const [name, path] of [