Save consent without reloading the page
consent.js posts the consent form in the background, the server answers 204 with the cookie, and the script swaps in the embeds and the legal line from a fresh copy of the page. Embed hosts are in frame-src from the start, the content address still only after consent. Without JavaScript the form keeps working with a reload. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
6390c00876
commit
6fa303f901
8 files changed
+198
-24
No files matched your search
@@ -371,7 +371,13 @@ final class PageController extends AbstractController
|
||||
// Vorlage holt die Liste von hier, statt sie durch jede Einbindung zu reichen.
|
||||
$request->attributes->set('kant_alternates', $alternates);
|
||||
$canonical = null === $adresse ? null : $adresse.(null !== $blaettern && $blaettern['page'] > 1 ? '?seite='.$blaettern['page'] : '');
|
||||
// Steht eine Einwilligung aus - Hinweis offen oder ein eingebetteter Inhalt wartet -, kommt
|
||||
// consent.js dazu: Es speichert die Entscheidung ohne Neuladen. Ohne Skript bleibt das
|
||||
// Formular mit Weiterleitung.
|
||||
$embeds = $this->embedState($request, $page['document'], ...array_values($areas), ...array_values($sections));
|
||||
$needsConsent = $embeds['blocked'] || $this->consent->needsBanner($request);
|
||||
$response = $this->render('page.html.twig', [
|
||||
'needsConsent' => $needsConsent,
|
||||
'page' => $page,
|
||||
'canonical' => $canonical,
|
||||
'alternates' => $alternates,
|
||||
@@ -429,8 +435,8 @@ final class PageController extends AbstractController
|
||||
// Die Pruefung braucht ein Skript von Cloudflare und einen Rahmen dafuer. Beides wird nur
|
||||
// dort erlaubt, wo tatsaechlich ein Formular steht - nicht auf jeder Seite der Website.
|
||||
$mitTurnstile = $this->turnstile->isActive() && (bool) array_filter($documents, fn (array $doc): bool => $this->hasType($doc, 'form'));
|
||||
$wantsScript = ($mitTurnstile || $needsTabs || $needsFormScript || [] !== $pluginAssets['scripts'] || [] !== $areaScripts || $needsSlider || $needsMotion || $needsScrolled || $needsZoom || $needsDocument || [] !== $this->code->scriptsOf($page['document']));
|
||||
$frames = $this->frameHosts($request, $page['document'], ...array_values($areas), ...array_values($sections));
|
||||
$wantsScript = ($mitTurnstile || $needsTabs || $needsFormScript || [] !== $pluginAssets['scripts'] || [] !== $areaScripts || $needsSlider || $needsMotion || $needsScrolled || $needsZoom || $needsDocument || $needsConsent || [] !== $this->code->scriptsOf($page['document']));
|
||||
$frames = $embeds['hosts'];
|
||||
if ($mitTurnstile) {
|
||||
$frames[] = 'challenges.cloudflare.com';
|
||||
}
|
||||
@@ -453,9 +459,9 @@ final class PageController extends AbstractController
|
||||
* The generated stylesheet is inline for speed, so it is allowed by its own hash rather than
|
||||
* by 'unsafe-inline' – any other inline style, injected or not, stays blocked.
|
||||
*
|
||||
* @param list<string> $frameHosts Hosts of embeds that are actually rendered on this response.
|
||||
* Nothing is added to the policy for an embed still waiting
|
||||
* for consent.
|
||||
* @param list<string> $frameHosts Hosts of the embeds on this page, also of those still
|
||||
* waiting for consent (ADR 115: consent.js inserts them
|
||||
* without a reload, and a policy cannot change afterwards)
|
||||
* @param bool $frameSelf whether the page may frame its own PDFs (the viewer)
|
||||
*/
|
||||
private function shell(Response $response, bool $allowScript = false, string $styles = '', array $frameHosts = [], bool $frameSelf = false): Response
|
||||
@@ -1079,23 +1085,27 @@ final class PageController extends AbstractController
|
||||
}
|
||||
|
||||
/**
|
||||
* The hosts of embeds that may load on this request.
|
||||
* The hosts of all embeds on this page, and whether one of them still waits for consent.
|
||||
*
|
||||
* An embed whose group is not granted contributes nothing: its host must not appear in the
|
||||
* policy, because the frame is not rendered either.
|
||||
* Seit ADR 115 steht der Host auch dann in frame-src, wenn die Gruppe noch nicht erlaubt ist:
|
||||
* Die Richtlinie einer Seite steht beim Laden fest, und consent.js setzt den Rahmen nach der
|
||||
* Einwilligung ohne Neuladen ein. Die Erlaubnis lädt nichts; die Adresse des Inhalts steht
|
||||
* weiterhin erst nach der Einwilligung im HTML.
|
||||
*
|
||||
* @return list<string>
|
||||
* @return array{hosts: list<string>, blocked: bool}
|
||||
*/
|
||||
private function frameHosts(Request $request, array ...$documents): array
|
||||
private function embedState(Request $request, array ...$documents): array
|
||||
{
|
||||
$hosts = [];
|
||||
$walk = function (array $blocks) use (&$walk, &$hosts, $request): void {
|
||||
$blocked = false;
|
||||
$walk = function (array $blocks) use (&$walk, &$hosts, &$blocked, $request): void {
|
||||
foreach ($blocks as $block) {
|
||||
if ('embed' === ($block['type'] ?? null) && $this->consent->granted($request, $block['props']['group'] ?? '')) {
|
||||
if ('embed' === ($block['type'] ?? null) && '' !== ($block['props']['url'] ?? '')) {
|
||||
$host = parse_url($block['props']['url'] ?? '', PHP_URL_HOST);
|
||||
if (is_string($host)) {
|
||||
$hosts[$host] = true;
|
||||
}
|
||||
$blocked = $blocked || !$this->consent->granted($request, $block['props']['group'] ?? '');
|
||||
}
|
||||
$walk($block['children'] ?? []);
|
||||
}
|
||||
@@ -1104,7 +1114,7 @@ final class PageController extends AbstractController
|
||||
$walk($document['blocks'] ?? []);
|
||||
}
|
||||
|
||||
return array_keys($hosts);
|
||||
return ['hosts' => array_keys($hosts), 'blocked' => $blocked];
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in new issue
Block a user