fix(tour, csp): media-src für Tour-Audio + Tour global außer Administration
Zwei Bugs:
1) Audio kam nicht durch — die Content-Security-Policy hatte kein
media-src und fiel auf default-src 'self' zurück. data:- (silent-WAV
zum Element-Unlock) und blob:-URLs (ElevenLabs-MP3-Cache) wurden
geblockt. Browser-Fehlermeldung im Console: „Loading media from
‚data:audio/wav;base64,…' violates the following Content Security
Policy directive". Fix: ``media-src 'self' data: blob:;`` ergänzt.
2) Tour war nur auf Startseite + Antrag-Detail eingebunden. User-Wunsch:
auf jeder Page außer Administration. Lösung: Tour-Engine-Include in
v2/base.html, mit ``{% if v2_active_nav not in [admin_*] %}``-Guard.
Pages ohne eigene ``window.GWOE_TOUR_STEPS`` bekommen einen Fallback
mit drei Stationen (Logo+Konzept, Topbar, Sidebar).
Topbar-Tour-Link sichtbar wenn ``window.gwoeTourStart`` existiert
(Engine geladen) — nicht mehr abhängig von Page-eigenen Steps.
Aufräumen: redundante Tour-Includes aus durchsuchen.html und
antrag_detail.html entfernt — die Engine kommt jetzt nur einmal aus
base.html.
This commit is contained in:
parent
57434485ea
commit
4c989ea443
@ -118,12 +118,19 @@ class SecurityHeadersMiddleware(BaseHTTPMiddleware):
|
||||
response.headers["X-XSS-Protection"] = "1; mode=block"
|
||||
response.headers["Referrer-Policy"] = "strict-origin-when-cross-origin"
|
||||
response.headers["Permissions-Policy"] = "geolocation=(), microphone=(), camera=()"
|
||||
# CSP: Allow self, inline styles (for templates), and PDF viewer
|
||||
# CSP: Allow self, inline styles (for templates), and PDF viewer.
|
||||
# media-src: 'self' für die Eigen-Hosting-MP3s (Tour-Audio aus
|
||||
# ElevenLabs-Cache via /api/tour/voice), data: für das 1×1
|
||||
# silent-WAV zum Audio-Element-Unlock im Click-Handler, blob:
|
||||
# für die im Browser zwischengespeicherten ElevenLabs-Blobs
|
||||
# (Object URLs via URL.createObjectURL). Ohne media-src
|
||||
# blockt der Browser jeden Audio-play-Versuch (#185 Phase 2).
|
||||
response.headers["Content-Security-Policy"] = (
|
||||
"default-src 'self'; "
|
||||
"style-src 'self' 'unsafe-inline'; "
|
||||
"script-src 'self' 'unsafe-inline'; "
|
||||
"img-src 'self' data:; "
|
||||
"media-src 'self' data: blob:; "
|
||||
"frame-ancestors 'none';"
|
||||
)
|
||||
return response
|
||||
|
||||
@ -226,6 +226,16 @@
|
||||
|
||||
{% block body_scripts %}{% endblock %}
|
||||
|
||||
{# ── Tour-Engine — auf jeder Seite eingehängt (#185).
|
||||
Pages, die eigene Stationen wollen, setzen ``window.GWOE_TOUR_STEPS``
|
||||
im body_scripts-Block oben. Pages ohne Stationen lassen den Topbar-
|
||||
Tour-Link automatisch ausgeblendet. Administration ist explizit
|
||||
ausgenommen. #}
|
||||
{% if v2_active_nav not in ['admin_stand', 'admin_queue', 'admin_abos',
|
||||
'freischaltungen', 'queue', 'abos'] %}
|
||||
{% include "v3/components/tour.html" ignore missing %}
|
||||
{% endif %}
|
||||
|
||||
{# ── Globaler BL-Selector — Persistenz + Event ───────────────────────────── #}
|
||||
<script>
|
||||
(function () {
|
||||
@ -330,14 +340,14 @@
|
||||
</script>
|
||||
|
||||
<script>
|
||||
/* Tour-Link in der Topbar nur einblenden, wenn die aktuelle Page eine
|
||||
Tour definiert hat (window.GWOE_TOUR_STEPS). Synchron — das Skript
|
||||
steht nach dem body_scripts-Block, dort werden die STEPS gesetzt.
|
||||
addEventListener('DOMContentLoaded') hatte einen Race weil
|
||||
DOMContentLoaded an manchen Stellen schon gefeuert hat. */
|
||||
/* Tour-Link in der Topbar einblenden, wenn die Tour-Engine geladen ist —
|
||||
das ist auf allen Pages außer Administration der Fall (siehe
|
||||
{% include "v3/components/tour.html" %} im body unten). Pages ohne
|
||||
eigene window.GWOE_TOUR_STEPS bekommen eine Fallback-Tour mit
|
||||
Topbar + Sidebar + Logo-Erklärung. */
|
||||
(function () {
|
||||
var link = document.getElementById('v2-topbar-tour');
|
||||
if (link && Array.isArray(window.GWOE_TOUR_STEPS) && window.GWOE_TOUR_STEPS.length > 0) {
|
||||
if (link && typeof window.gwoeTourStart === 'function') {
|
||||
link.hidden = false;
|
||||
}
|
||||
})();
|
||||
|
||||
@ -111,7 +111,6 @@ window.GWOE_TOUR_STEPS = [
|
||||
{% endif %}
|
||||
];
|
||||
</script>
|
||||
{% include "v3/components/tour.html" %}
|
||||
|
||||
{% endblock %}
|
||||
|
||||
|
||||
@ -141,8 +141,27 @@
|
||||
// Jeder Eintrag: {selector, title, text}. Selectoren werden zur Laufzeit
|
||||
// aufgelöst — fehlt das Element (z.B. keine Plenum-Votes auf einer
|
||||
// bestimmten Drucksache), überspringt die Tour den Schritt automatisch.
|
||||
//
|
||||
// Fallback: pages ohne eigene Stationen bekommen eine kurze
|
||||
// Orientierungs-Tour, damit die Tour-Schaltfläche überall einen
|
||||
// sinnvollen Inhalt zeigt.
|
||||
const FALLBACK_STEPS = [
|
||||
{ selector: '.v2-brand-link, .v2-brand',
|
||||
title: 'Willkommen beim GWÖ-Antragsprüfer',
|
||||
text: 'Diese Seite bewertet Anträge aus deutschen Parlamenten nach der Gemeinwohl-Matrix. Was sie auszeichnet: Sie schaut nicht nur, ob ein Antrag „gut klingt", sondern wie sehr er fünf Werten dient — Würde, Solidarität, Nachhaltigkeit, Gerechtigkeit und Demokratie. Klick auf das Logo bringt dich jederzeit zur Übersicht zurück.' },
|
||||
{ selector: '.v2-topbar',
|
||||
title: 'Topbar',
|
||||
text: 'Oben findest du Methodik (wie wir bewerten), Quellen (alle Wahl- und Grundsatzprogramme, semantisch durchsuchbar) und den Bundesland-Filter. Rechts ist der Theme-Toggle und — falls eingeloggt — dein Profil.' },
|
||||
{ selector: '#v2-sidebar nav',
|
||||
title: 'Navigation links',
|
||||
text: 'Links findest du die Hauptbereiche. Die Liste passt sich an: angemeldete Nutzer:innen sehen außerdem Auswertungen, Stimmverhalten und persönliche Funktionen wie eine Merkliste.' },
|
||||
];
|
||||
|
||||
function getSteps() {
|
||||
return Array.isArray(window.GWOE_TOUR_STEPS) ? window.GWOE_TOUR_STEPS : [];
|
||||
if (Array.isArray(window.GWOE_TOUR_STEPS) && window.GWOE_TOUR_STEPS.length > 0) {
|
||||
return window.GWOE_TOUR_STEPS;
|
||||
}
|
||||
return FALLBACK_STEPS;
|
||||
}
|
||||
|
||||
let _tourIdx = 0;
|
||||
|
||||
@ -565,7 +565,7 @@
|
||||
text: 'Hier sehen Sie, wie die Fraktionen tatsächlich abgestimmt haben. Das Warnschild neben einer Fraktion bedeutet Heuchelei: Sie stimmt mit Nein, obwohl der Antrag exakt zu ihrem eigenen Wahlprogramm passt. Das Ausrufezeichen markiert Opportunismus: Ja-Stimme bei einem Antrag, der dem eigenen Programm widerspricht.' },
|
||||
];
|
||||
</script>
|
||||
{% include "v3/components/tour.html" %}
|
||||
{# Tour-Engine wird global in v2/base.html eingebunden #}
|
||||
|
||||
</div>{# .v3-page #}
|
||||
{% endif %}{# antrag #}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user