From 2179744ddf593b67a17b80dd4bc60917d77fc12a Mon Sep 17 00:00:00 2001 From: Clemens Creutzburg Date: Thu, 27 Aug 2026 12:51:02 +0200 Subject: [PATCH] Cookie-Einstellungen in Footer aufnehmen --- app/analytics.php | 12 +++++++++- app/legal.php | 1 + assets/css/privacy-settings.css | 35 +++++++++++++++++++++++++++++ assets/js/privacy-settings.js | 27 ++++++++++++---------- email-verifizieren.php | 1 + footer.php | 2 +- landing.php | 1 + login.php | 1 + passwort-vergessen.php | 1 + passwort-zuruecksetzen.php | 1 + register.php | 1 + scripts/check-analytics-consent.php | 25 +++++++++++++++++++++ zwei-faktor.php | 1 + 13 files changed, 95 insertions(+), 14 deletions(-) diff --git a/app/analytics.php b/app/analytics.php index f0b4237..088741f 100644 --- a/app/analytics.php +++ b/app/analytics.php @@ -108,6 +108,16 @@ function app_analytics_head_html(): string return '' . "\n"; } +function app_analytics_footer_settings_button_html(): string +{ + if (app_matomo_configuration() === null) { + return ''; + } + + return ''; +} + function app_analytics_body_html(): string { $configuration = app_matomo_configuration(); @@ -154,7 +164,7 @@ function app_analytics_body_html(): string - + HTML . "\n\n" . '' . "\n"; diff --git a/app/legal.php b/app/legal.php index 3599d85..8ceb7ee 100644 --- a/app/legal.php +++ b/app/legal.php @@ -35,6 +35,7 @@ function app_public_legal_footer(): string . 'Vertrag widerrufen' . 'Preise' . 'Verträge hier kündigen' + . app_analytics_footer_settings_button_html() . ''; } diff --git a/assets/css/privacy-settings.css b/assets/css/privacy-settings.css index df99dd6..d94b4c9 100644 --- a/assets/css/privacy-settings.css +++ b/assets/css/privacy-settings.css @@ -103,6 +103,41 @@ padding: 0.55rem 0.7rem; } +.footer-cookie-settings { + appearance: none; + background: transparent; + border: 0; + border-bottom: dotted 1px; + border-radius: 0; + box-shadow: none; + color: inherit !important; + cursor: pointer; + font: inherit; + font-weight: inherit; + height: auto; + letter-spacing: inherit; + line-height: inherit; + margin: 0; + padding: 0; + text-transform: none; +} + +.footer-cookie-settings:hover { + background: transparent; + border-bottom-color: transparent; + color: #38761d !important; +} + +.footer-cookie-settings:focus-visible { + outline: solid 3px #f0b429; + outline-offset: 3px; +} + +.public-footer-links .footer-cookie-settings { + color: #3d4449 !important; + margin: 0 0.5rem; +} + @media screen and (max-width: 736px) { .analytics-consent { padding: 1rem; diff --git a/assets/js/privacy-settings.js b/assets/js/privacy-settings.js index b8e4ca8..3f3727e 100644 --- a/assets/js/privacy-settings.js +++ b/assets/js/privacy-settings.js @@ -3,9 +3,10 @@ var configurationElement = document.getElementById('analytics-configuration'); var banner = document.getElementById('analytics-consent'); - var settingsButton = document.getElementById('analytics-consent-settings'); + var floatingSettingsButton = document.getElementById('analytics-consent-settings'); + var settingsButtons = document.querySelectorAll('[data-analytics-settings]'); - if (!configurationElement || !banner || !settingsButton) { + if (!configurationElement || !banner || !floatingSettingsButton) { return; } @@ -148,7 +149,7 @@ function closeBanner() { banner.hidden = true; - settingsButton.hidden = false; + floatingSettingsButton.hidden = false; } function choose(decision) { @@ -170,21 +171,23 @@ }); }); - settingsButton.addEventListener('click', function () { - banner.hidden = false; - settingsButton.hidden = true; - var firstButton = banner.querySelector('[data-analytics-consent="denied"]'); - if (firstButton) { - firstButton.focus(); - } + settingsButtons.forEach(function (button) { + button.addEventListener('click', function () { + banner.hidden = false; + floatingSettingsButton.hidden = true; + var firstButton = banner.querySelector('[data-analytics-consent="denied"]'); + if (firstButton) { + firstButton.focus(); + } + }); }); var initialDecision = readDecision(); if (initialDecision === 'granted') { - settingsButton.hidden = false; + floatingSettingsButton.hidden = false; sendPageView(); } else if (initialDecision === 'denied') { - settingsButton.hidden = false; + floatingSettingsButton.hidden = false; removeAnalysisCookies(); } else { banner.hidden = false; diff --git a/email-verifizieren.php b/email-verifizieren.php index b2580a7..777bb3e 100644 --- a/email-verifizieren.php +++ b/email-verifizieren.php @@ -68,6 +68,7 @@ $result = $token !== '' Vertrag widerrufen Preise Verträge hier kündigen + diff --git a/footer.php b/footer.php index 025ac70..24f77ed 100644 --- a/footer.php +++ b/footer.php @@ -133,7 +133,7 @@ if ($saasNavUser !== null) { diff --git a/landing.php b/landing.php index 8b074b6..019ad8b 100644 --- a/landing.php +++ b/landing.php @@ -292,6 +292,7 @@ $jsonFlags = JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE; Vertrag widerrufen Preise Verträge hier kündigen + diff --git a/login.php b/login.php index d9045f0..851271c 100644 --- a/login.php +++ b/login.php @@ -136,6 +136,7 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') { Vertrag widerrufen Preise Verträge hier kündigen + diff --git a/passwort-vergessen.php b/passwort-vergessen.php index 02d719a..09d11d2 100644 --- a/passwort-vergessen.php +++ b/passwort-vergessen.php @@ -116,6 +116,7 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') { Vertrag widerrufen Preise Verträge hier kündigen + diff --git a/passwort-zuruecksetzen.php b/passwort-zuruecksetzen.php index d5889ec..dc1c93d 100644 --- a/passwort-zuruecksetzen.php +++ b/passwort-zuruecksetzen.php @@ -111,6 +111,7 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') { Vertrag widerrufen Preise Verträge hier kündigen + diff --git a/register.php b/register.php index 75cfb27..0ef24c8 100644 --- a/register.php +++ b/register.php @@ -211,6 +211,7 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') { Vertrag widerrufen Preise Verträge hier kündigen + diff --git a/scripts/check-analytics-consent.php b/scripts/check-analytics-consent.php index 1dc2b22..3ea8d73 100644 --- a/scripts/check-analytics-consent.php +++ b/scripts/check-analytics-consent.php @@ -74,6 +74,9 @@ $assert('Matomo selbst ist im HTML nicht vorab eingebunden', && !str_contains($body, '"siteId"')); $assert('Cookie-Einstellungen bleiben nach der Entscheidung erreichbar', str_contains($body, 'analytics-consent-settings')); +$assert('Footer-Schalter wird bei aktiver Analyse ausgegeben', + str_contains(app_analytics_footer_settings_button_html(), 'data-analytics-settings') + && str_contains(app_public_legal_footer(), 'Cookie-Einstellungen')); $assert('Browser nutzt den lokalen First-Party-Endpunkt', str_contains($body, '"trackingEndpoint":"nutzungsanalyse.php"') && str_contains($body, 'assets/js/privacy-settings.js')); @@ -94,6 +97,9 @@ $assert('Keine Kaffeeliste-Benutzerkennung wird gelesen', $assert('Widerruf stoppt Tracking und löscht Analyse-Cookies', str_contains($javascript, 'removeAnalysisCookies()') && str_contains($javascript, "name !== visitorCookieName")); +$assert('Alle Cookie-Einstellungs-Schalter öffnen denselben Dialog', + str_contains($javascript, "querySelectorAll('[data-analytics-settings]')") + && str_contains($javascript, 'settingsButtons.forEach')); $relay = file_get_contents(APP_ROOT . '/app/analytics-relay.php') ?: ''; $assert('Relay übernimmt keine Client-IP', @@ -130,6 +136,25 @@ foreach ($publicTemplates as $template) { $assert('App-Layout bindet Head und Body zentral ein', str_contains(file_get_contents(APP_ROOT . '/header.php') ?: '', 'app_analytics_head_html') && str_contains(file_get_contents(APP_ROOT . '/footer.php') ?: '', 'app_analytics_body_html')); +$appFooter = file_get_contents(APP_ROOT . '/footer.php') ?: ''; +$assert('App-Footer enthält Cookie-Einstellungen ohne Copyright- und Designlink', + str_contains($appFooter, 'app_analytics_footer_settings_button_html') + && !str_contains($appFooter, '© Clemens Creutzburg') + && !str_contains($appFooter, 'html5up.net')); + +foreach ([ + 'email-verifizieren.php', + 'landing.php', + 'login.php', + 'passwort-vergessen.php', + 'passwort-zuruecksetzen.php', + 'register.php', + 'zwei-faktor.php', +] as $manualFooterTemplate) { + $contents = file_get_contents(APP_ROOT . '/' . $manualFooterTemplate) ?: ''; + $assert("{$manualFooterTemplate} bietet Cookie-Einstellungen im Footer an", + str_contains($contents, 'app_analytics_footer_settings_button_html')); +} $privacy = app_legal_document_text('privacy_notice'); $assert('Aktuelle Datenschutzerklärung beschreibt Matomo und Widerruf', diff --git a/zwei-faktor.php b/zwei-faktor.php index 1ec5d20..6f2a1cb 100644 --- a/zwei-faktor.php +++ b/zwei-faktor.php @@ -112,6 +112,7 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') { Datenschutz AVV Widerrufsbelehrung +