From 3a95ab9dcd8d4d68eb3fe6dc0899270fa84c3d33 Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 18 Aug 2026 20:45:50 +0200 Subject: [PATCH] Style the change password module Add a template override for Contao's changePassword module with the same card, explanatory copy and corporate design as the lost password module. The card rules keyed off .mod_lostPassword.block, which does not scale to a third auth module. Move them to a .survey-auth-module class that the overrides put on the wrapper themselves, so another auth module needs one line and no new CSS. Co-Authored-By: Claude Opus 5 --- AI_HANDOVER.md | 23 +++- .../frontend/mod_changePassword.html.twig | 34 ++++++ .../frontend/mod_lostPassword.html.twig | 3 + .../templates/frontend/mod_message.html.twig | 3 + public/css/auth-module.css | 108 +++++++++--------- translations/messages.de.yaml | 4 + translations/messages.en.yaml | 4 + 7 files changed, 126 insertions(+), 53 deletions(-) create mode 100644 contao/templates/frontend/mod_changePassword.html.twig diff --git a/AI_HANDOVER.md b/AI_HANDOVER.md index dd180ce..bdf466b 100644 --- a/AI_HANDOVER.md +++ b/AI_HANDOVER.md @@ -467,7 +467,14 @@ Wichtige Settings: ### Gestaltung - Alle Auth-Styles liegen zentral in `public/css/auth-module.css`, global eingebunden über `contao/config/config.php` (`$GLOBALS['TL_CSS']`). -- Das Passwort-vergessen-Modul (`.mod_lostPassword.block`) verwendet bewusst dieselbe zentrierte Karte wie das Login-Modul: gleiche Breite, Radien, Farbverläufe, Schriften und Button-Optik. +- Das Kartenlayout hängt **nicht** an einzelnen Contao-Modultypen, sondern an der Klasse `.survey-auth-module`. Die Template-Overrides setzen sie selbst auf den Modul-Wrapper: + + ```twig + {% set wrapperAttributes = attrs().addClass('survey-auth-module') %} + ``` + + `block_searchable.html.twig` merged das in die Wrapper-Attribute. Ein weiteres Auth-Modul braucht damit nur diese eine Zeile und keine neuen CSS-Regeln. Aktuell nutzen sie `mod_lostPassword`, `mod_changePassword` und die Meldungsansichten über `mod_message`. +- Passwort vergessen und Passwort ändern verwenden bewusst dieselbe zentrierte Karte wie das Login-Modul: gleiche Breite, Radien, Farbverläufe, Schriften und Button-Optik. - Der Link `Passwort vergessen?` im Login ist rechtsbündig und im Corporate Design (Blau, Hover Orange, Chevron `›`). Auf Mobil wird er zentriert. - Fehler- und Bestätigungsmeldungen (auch das Contao-Template `mod_message`, das der Kern bei ungültigem/abgelaufenem Token einsetzt) werden als farbige Hinweisboxen dargestellt. @@ -481,6 +488,19 @@ Wichtige Settings: - mit Token: Texte aus `survey.auth.set_password.*` - Die Texte liegen in `translations/messages.de.yaml` bzw. `messages.en.yaml` unter `survey.auth`, sind also nicht im Template hartkodiert. +### Passwort-Änderung erzwingen + +Die Erweiterung `terminal42/contao-password-validation` bringt das Feld `tl_member.pwChange` und die Startseiten-Einstellung `tl_page.pwChangePage` mit. Ist `pwChange` gesetzt, leitet sie das Mitglied nach dem Login auf die dort hinterlegte Seite. + +Aufbau in dieser Installation: + +- Startseite (tl_page ID 1) → `pwChangePage` = Seite `Passwort ändern` (ID 10, Alias `passwort-aendern`) +- Dort liegt das Contao-Kernmodul `changePassword` (tl_module ID 11) +- Dessen Weiterleitungsseite (`jumpTo`) ist `Passwort wurde geändert` (ID 11, Alias `passwort-wurde-geaendert`) + +- Template-Override: `contao/templates/frontend/mod_changePassword.html.twig`, gleicher Aufbau wie das Passwort-vergessen-Modul. Texte unter `survey.auth.change_password.*`. +- Der Bestätigungstext auf Seite 11 liegt als normales Contao-Textelement mit der Klasse `survey-auth-note` und ist damit redaktionell pflegbar. Der Button führt bewusst **zur Umfragenliste** und nicht zur Anmeldung, weil das Mitglied an dieser Stelle bereits eingeloggt ist. + ### E-Mail-Versand - Der Versand läuft über einen benannten Mailer-Transport `ksb`, konfiguriert in der Projektinstallation (`config/config.yaml`), nicht im Bundle: @@ -583,6 +603,7 @@ Mitglieder-Login / Passwort vergessen: - `public/css/auth-module.css` - `contao/templates/frontend/mod_lostPassword.html.twig` +- `contao/templates/frontend/mod_changePassword.html.twig` - `contao/templates/frontend/mod_message.html.twig` - `src/EventListener/CorporateMailListener.php` - `src/Resources/views/email/corporate.html.twig` diff --git a/contao/templates/frontend/mod_changePassword.html.twig b/contao/templates/frontend/mod_changePassword.html.twig new file mode 100644 index 0000000..a6b209f --- /dev/null +++ b/contao/templates/frontend/mod_changePassword.html.twig @@ -0,0 +1,34 @@ +{% extends '@Contao/block_unsearchable.html.twig' %} +{% trans_default_domain 'messages' %} + +{# Der Modul-Wrapper bekommt die gemeinsame Auth-Klasse, an der das Kartenlayout haengt. #} +{% set wrapperAttributes = attrs().addClass('survey-auth-module') %} + +{% block content %} + +
+ +
+

{{ 'survey.auth.change_password.headline'|trans }}

+

{{ 'survey.auth.change_password.intro'|trans }}

+

{{ 'survey.auth.change_password.hint'|trans }}

+
+ + {% if message|default %} +

{{ message }}

+ {% endif %} + +
+
+ + + {{ fields|raw }} +
+ +
+
+
+ +
+ +{% endblock %} diff --git a/contao/templates/frontend/mod_lostPassword.html.twig b/contao/templates/frontend/mod_lostPassword.html.twig index 77bc851..470bd57 100644 --- a/contao/templates/frontend/mod_lostPassword.html.twig +++ b/contao/templates/frontend/mod_lostPassword.html.twig @@ -1,6 +1,9 @@ {% extends '@Contao/block_unsearchable.html.twig' %} {% trans_default_domain 'messages' %} +{# Der Modul-Wrapper bekommt die gemeinsame Auth-Klasse, an der das Kartenlayout haengt. #} +{% set wrapperAttributes = attrs().addClass('survey-auth-module') %} + {# Schritt 2: Der Nutzer kommt ueber den Link aus der E-Mail und vergibt ein neues Passwort. #} {% set surveyAuthScope = (app.request.query.get('token')|default('')) starts with 'pw-' ? 'set_password' : 'lost_password' %} diff --git a/contao/templates/frontend/mod_message.html.twig b/contao/templates/frontend/mod_message.html.twig index 7c5f5b2..b224771 100644 --- a/contao/templates/frontend/mod_message.html.twig +++ b/contao/templates/frontend/mod_message.html.twig @@ -9,6 +9,9 @@ Sackgasse landet. #} {% set surveyIsAuthMessage = 'mod_lostPassword' in (class|default('')) %} +{% if surveyIsAuthMessage %} + {% set wrapperAttributes = attrs().addClass('survey-auth-module') %} +{% endif %} {% block content %} diff --git a/public/css/auth-module.css b/public/css/auth-module.css index a80f584..f1f2499 100644 --- a/public/css/auth-module.css +++ b/public/css/auth-module.css @@ -268,11 +268,15 @@ nav.mod_customnav.block:has(a[href*="/abmelden"]) .level_1 > li > a:focus { /* ------------------------------------------------------------------ - Passwort-vergessen-Modul (Contao "lostPassword") - Gleiches Kartenlayout wie das Login-Modul, mittig zentriert. + Auth-Module im gleichen Kartenlayout wie das Login-Modul. + + Die Klasse .survey-auth-module setzen die Template-Overrides selbst auf den + Modul-Wrapper (Contao "lostPassword", "changePassword" und die Meldungs- + ansichten via mod_message). So haengen die Kartenregeln nicht an einzelnen + Contao-Modultypen und ein weiteres Auth-Modul braucht nur die Klasse. ------------------------------------------------------------------ */ -.mod_lostPassword.block { +.survey-auth-module { max-width: min(100%, 36rem); margin: clamp(1.5rem, 4vw, 3rem) auto; padding: clamp(1.5rem, 2.6vw, 2.4rem); @@ -288,19 +292,19 @@ nav.mod_customnav.block:has(a[href*="/abmelden"]) .level_1 > li > a:focus { color: var(--survey-auth-ink); } -.mod_lostPassword.block .survey-auth-card { +.survey-auth-module .survey-auth-card { display: grid; gap: 1.35rem; } -.mod_lostPassword.block .survey-auth-intro { +.survey-auth-module .survey-auth-intro { display: grid; gap: 0.55rem; padding-bottom: 1.1rem; border-bottom: 1px solid rgba(162, 168, 180, 0.28); } -.mod_lostPassword.block .survey-auth-intro-headline { +.survey-auth-module .survey-auth-intro-headline { margin: 0; font-family: "Blogger Sans", sans-serif; font-size: clamp(1.35rem, 2.4vw, 1.7rem); @@ -309,14 +313,14 @@ nav.mod_customnav.block:has(a[href*="/abmelden"]) .level_1 > li > a:focus { color: var(--survey-auth-blue); } -.mod_lostPassword.block .survey-auth-intro-text { +.survey-auth-module .survey-auth-intro-text { margin: 0; font-size: 1.02rem; line-height: 1.5; color: var(--survey-auth-ink); } -.mod_lostPassword.block .survey-auth-hint { +.survey-auth-module .survey-auth-hint { margin: 0; padding-left: 0.85rem; border-left: 3px solid var(--survey-auth-green); @@ -325,23 +329,23 @@ nav.mod_customnav.block:has(a[href*="/abmelden"]) .level_1 > li > a:focus { color: var(--survey-auth-muted); } -.mod_lostPassword.block form, -.mod_lostPassword.block .formbody { +.survey-auth-module form, +.survey-auth-module .formbody { margin: 0; } -.mod_lostPassword.block .formbody { +.survey-auth-module .formbody { display: grid; gap: 1rem; } -.mod_lostPassword.block .widget { +.survey-auth-module .widget { display: grid; gap: 0.45rem; margin: 0; } -.mod_lostPassword.block label { +.survey-auth-module label { font-family: "Blogger Sans", sans-serif; font-size: 1rem; font-weight: 500; @@ -349,12 +353,12 @@ nav.mod_customnav.block:has(a[href*="/abmelden"]) .level_1 > li > a:focus { letter-spacing: 0.01em; } -.mod_lostPassword.block label .mandatory { +.survey-auth-module label .mandatory { margin-left: 0.15rem; color: var(--survey-auth-orange); } -.mod_lostPassword.block .invisible { +.survey-auth-module .invisible { position: absolute; width: 1px; height: 1px; @@ -367,12 +371,12 @@ nav.mod_customnav.block:has(a[href*="/abmelden"]) .level_1 > li > a:focus { white-space: nowrap; } -.mod_lostPassword.block input.text, -.mod_lostPassword.block input.password, -.mod_lostPassword.block input.captcha, -.mod_lostPassword.block input[type="text"], -.mod_lostPassword.block input[type="email"], -.mod_lostPassword.block input[type="password"] { +.survey-auth-module input.text, +.survey-auth-module input.password, +.survey-auth-module input.captcha, +.survey-auth-module input[type="text"], +.survey-auth-module input[type="email"], +.survey-auth-module input[type="password"] { width: 100%; min-height: 3.2rem; padding: 0.8rem 0.95rem; @@ -386,12 +390,12 @@ nav.mod_customnav.block:has(a[href*="/abmelden"]) .level_1 > li > a:focus { transition: border-color 0.18s ease, box-shadow 0.18s ease, background-color 0.18s ease; } -.mod_lostPassword.block input.text:focus, -.mod_lostPassword.block input.password:focus, -.mod_lostPassword.block input.captcha:focus, -.mod_lostPassword.block input[type="text"]:focus, -.mod_lostPassword.block input[type="email"]:focus, -.mod_lostPassword.block input[type="password"]:focus { +.survey-auth-module input.text:focus, +.survey-auth-module input.password:focus, +.survey-auth-module input.captcha:focus, +.survey-auth-module input[type="text"]:focus, +.survey-auth-module input[type="email"]:focus, +.survey-auth-module input[type="password"]:focus { outline: none; border-color: rgba(0, 100, 173, 0.72); box-shadow: 0 0 0 4px rgba(0, 100, 173, 0.12); @@ -399,26 +403,26 @@ nav.mod_customnav.block:has(a[href*="/abmelden"]) .level_1 > li > a:focus { } /* Sicherheitsfrage: Rechenaufgabe als Hinweistext unter dem Feld */ -.mod_lostPassword.block .widget-captcha .captcha_text { +.survey-auth-module .widget-captcha .captcha_text { order: 3; font-size: 0.9rem; color: var(--survey-auth-muted); } -.mod_lostPassword.block .widget-captcha input.captcha { +.survey-auth-module .widget-captcha input.captcha { max-width: 9rem; } -/* Zweiter Schritt: Passwortbestaetigung liegt als eigenes Feld direkt darunter */ -.mod_lostPassword.block .widget-password + .widget-password { +/* Passwortbestaetigung liegt als eigenes Feld direkt darunter */ +.survey-auth-module .widget-password + .widget-password { margin-top: -0.15rem; } -.mod_lostPassword.block .widget-submit { +.survey-auth-module .widget-submit { margin-top: 0.3rem; } -.mod_lostPassword.block button.submit { +.survey-auth-module button.submit { display: inline-flex; justify-content: center; align-items: center; @@ -437,8 +441,8 @@ nav.mod_customnav.block:has(a[href*="/abmelden"]) .level_1 > li > a:focus { transition: transform 0.18s ease, box-shadow 0.18s ease, filter 0.18s ease; } -.mod_lostPassword.block button.submit:hover, -.mod_lostPassword.block button.submit:focus { +.survey-auth-module button.submit:hover, +.survey-auth-module button.submit:focus { transform: translateY(-1px); box-shadow: 0 18px 34px rgba(0, 100, 173, 0.28); filter: saturate(1.05); @@ -446,9 +450,9 @@ nav.mod_customnav.block:has(a[href*="/abmelden"]) .level_1 > li > a:focus { } /* Fehler- und Bestaetigungsmeldungen (auch das Contao-Template mod_message) */ -.mod_lostPassword.block p.error, -.mod_lostPassword.block p.confirm, -.mod_lostPassword.block p.message { +.survey-auth-module p.error, +.survey-auth-module p.confirm, +.survey-auth-module p.message { margin: 0; padding: 0.85rem 1rem; border-radius: 1rem; @@ -457,24 +461,24 @@ nav.mod_customnav.block:has(a[href*="/abmelden"]) .level_1 > li > a:focus { line-height: 1.45; } -.mod_lostPassword.block p.error { +.survey-auth-module p.error { border-color: rgba(236, 124, 50, 0.4); background: rgba(236, 124, 50, 0.12); color: #a4501a; } -.mod_lostPassword.block p.confirm { +.survey-auth-module p.confirm { border-color: rgba(0, 174, 151, 0.4); background: rgba(0, 174, 151, 0.12); color: #00705f; } -.mod_lostPassword.block .survey-auth-back { +.survey-auth-module .survey-auth-back { margin: 0; text-align: right; } -.mod_lostPassword.block .survey-auth-back a { +.survey-auth-module .survey-auth-back a { display: inline-flex; align-items: center; gap: 0.3rem; @@ -488,14 +492,14 @@ nav.mod_customnav.block:has(a[href*="/abmelden"]) .level_1 > li > a:focus { transition: color 0.18s ease, border-color 0.18s ease; } -.mod_lostPassword.block .survey-auth-back a::before { +.survey-auth-module .survey-auth-back a::before { content: "\2039"; font-size: 1.05em; line-height: 1; } -.mod_lostPassword.block .survey-auth-back a:hover, -.mod_lostPassword.block .survey-auth-back a:focus { +.survey-auth-module .survey-auth-back a:hover, +.survey-auth-module .survey-auth-back a:focus { color: var(--survey-auth-orange); border-color: rgba(236, 124, 50, 0.55); outline: none; @@ -583,14 +587,14 @@ a.survey-auth-button:focus { outline: none; } -/* Meldungsseiten des Passwort-vergessen-Moduls (Contao-Template mod_message) */ -.mod_lostPassword.block .survey-auth-actions { +/* Meldungsseiten der Auth-Module (Contao-Template mod_message) */ +.survey-auth-module .survey-auth-actions { margin: 0; text-align: center; } -/* Hilfetexte und Feldfehler im Passwort-vergessen-Modul */ -.mod_lostPassword.block p.help { +/* Hilfetexte und Feldfehler in den Auth-Modulen */ +.survey-auth-module p.help { margin: 0; font-size: 0.9rem; line-height: 1.4; @@ -610,12 +614,12 @@ a.survey-auth-button:focus { } @media (max-width: 767px) { - .mod_lostPassword.block { + .survey-auth-module { padding: 1.2rem; border-radius: 1.35rem; } - .mod_lostPassword.block button.submit { + .survey-auth-module button.submit { width: 100%; } @@ -629,7 +633,7 @@ a.survey-auth-button:focus { } .mod_login.block.login .password-reset, - .mod_lostPassword.block .survey-auth-back { + .survey-auth-module .survey-auth-back { text-align: center; } } diff --git a/translations/messages.de.yaml b/translations/messages.de.yaml index 833f3af..4092f8b 100644 --- a/translations/messages.de.yaml +++ b/translations/messages.de.yaml @@ -172,6 +172,10 @@ survey: headline: "Neues Passwort vergeben" intro: "Bitte vergeben Sie jetzt Ihr neues Passwort für die Umfrageplattform. Anschließend können Sie sich damit sofort anmelden." hint: "Wählen Sie ein Passwort, das Sie nicht bereits an anderer Stelle verwenden." + change_password: + headline: "Neues Passwort vergeben" + intro: "Bitte vergeben Sie ein neues Passwort für Ihren Zugang zur Umfrageplattform. Geben Sie dazu zuerst Ihr aktuelles Passwort ein und anschließend zweimal das neue." + hint: "Wählen Sie ein Passwort, das Sie nicht bereits an anderer Stelle verwenden." back_to_login: "Zurück zur Anmeldung" email: cta_password: "Neues Passwort festlegen" diff --git a/translations/messages.en.yaml b/translations/messages.en.yaml index 4c9764a..3355b65 100644 --- a/translations/messages.en.yaml +++ b/translations/messages.en.yaml @@ -172,6 +172,10 @@ survey: headline: "Set a new password" intro: "Please choose your new password for the survey platform. You can log in with it right away." hint: "Please pick a password that you do not already use elsewhere." + change_password: + headline: "Set a new password" + intro: "Please choose a new password for your survey platform account. Enter your current password first, then the new one twice." + hint: "Please pick a password that you do not already use elsewhere." back_to_login: "Back to login" email: cta_password: "Set a new password"