diff --git a/app/main.py b/app/main.py index e8b82c7..0a86961 100644 --- a/app/main.py +++ b/app/main.py @@ -596,7 +596,7 @@ async def quellen_page(request: Request): @app.get("/api/wahlprogramm-cite") -async def wahlprogramm_cite(pid: str, seite: int, q: str = ""): +async def wahlprogramm_cite(pid: str = "", pdf: str = "", seite: int = 1, q: str = ""): """Render eine Wahlprogramm-Seite mit gelb hervorgehobener Zitat-Stelle. Issue #47: Klick auf eine Zitat-Quelle im Report soll direkt zur @@ -606,11 +606,22 @@ async def wahlprogramm_cite(pid: str, seite: int, q: str = ""): 1-Seiten-PDF mit ``add_highlight_annot``-Annotation auf den per ``page.search_for`` gefundenen Bounding-Boxes. + Akzeptiert ``pid`` (PROGRAMME-Key) ODER ``pdf`` (Dateiname wie + ``spd-grundsatzprogramm.pdf``). Letzterer ermöglicht die retroaktive + Nutzung von Pre-#47-URLs im Frontend, wo nur der statische Pfad + ``/static/referenzen/#page=`` gespeichert ist. + Security: ``pid`` muss ein registrierter PROGRAMME-Key sein — verhindert Path-Traversal und arbiträren File-Read aus dem referenzen-Verzeichnis. ``seite`` wird per Pydantic-Coercion auf int gezwungen. ``q`` ist auf 200 Zeichen begrenzt im Renderer. """ + # Reverse-Lookup: pdf-Filename → programm_id, falls nur pdf angegeben. + if not pid and pdf: + for p, info in PROGRAMME.items(): + if info.get("pdf") == pdf: + pid = p + break if pid not in PROGRAMME: raise HTTPException(status_code=404, detail="Unbekanntes Wahlprogramm") if seite < 1 or seite > 2000: diff --git a/app/templates/index.html b/app/templates/index.html index 79f2f48..9606b7b 100644 --- a/app/templates/index.html +++ b/app/templates/index.html @@ -1498,12 +1498,31 @@ `}).join(''); + // Issue #47: Zitat-URLs zu Cite-Endpoint umschreiben für gelbes + // Highlighting. Funktioniert retroaktiv für Pre-#47-Assessments + // (statische /static/referenzen/X.pdf#page=N) und nativ für + // Post-#47 (die schon /api/wahlprogramm-cite enthalten). + function makeCiteUrl(z) { + if (!z || !z.url) return '#'; + // Schon eine Cite-URL? Durchreichen. + if (z.url.includes('/api/wahlprogramm-cite')) return z.url; + // Statische URL umschreiben: /static/referenzen/X.pdf#page=N + const m = z.url.match(/\/static\/referenzen\/(.+\.pdf)#page=(\d+)/); + if (m && z.text) { + const pdf = m[1]; + const page = m[2]; + const q = encodeURIComponent((z.text || '').substring(0, 200)); + return `/api/wahlprogramm-cite?pdf=${encodeURIComponent(pdf)}&seite=${page}&q=${q}`; + } + return z.url; + } + const wahlprogrammHtml = (item.wahlprogrammScores || []).map(wp => { - // Zitate formatieren mit klickbaren Links + // Zitate formatieren mit klickbaren Links + Highlighting const zitateHtml = (wp.wahlprogramm?.zitate || []).map(z => `
"${z.text}"
- + 📄 ${z.quelle}