Ein DGS-Video liess sich bisher gar nicht einbinden: Der Markdown- Prozessor laeuft mit Sanitizing, und remark reicht rohes HTML dann von vornherein nicht durch - <video> kam als leeres <p> heraus. Eine erweiterte Freigabeliste allein half deshalb nicht. Statt rohes HTML fuer alle Textfelder zu oeffnen - auch fuer die 121 Erklaerungen - bekommt die Gebaerdensprach-Seite zwei eigene Felder fuer Video- und Untertiteladresse. Ausgegeben wird ein natives <video controls> mit <track kind="captions"> und Bildunterschrift. Adressen werden zweimal geprueft, beim Speichern und vor der Ausgabe: zugelassen sind nur der eigene Server und https. javascript:, data:, protokollrelative und einfache http-Adressen werden abgewiesen. iframe bleibt bewusst gesperrt - eine YouTube-Einbindung wuerde Besucherdaten an Dritte uebertragen. Erklaerung zur Barrierefreiheit entsprechend gekuerzt: Die beiden Luecken nach § 4 BITV 2.0 entfallen, es bleibt die ungepruefte Druckansicht. Geprueft: Video wird mit Untertitelspur ausgeliefert und ist per Tastatur erreichbar, axe-core meldet auf beiden neuen Seiten in hell und dunkel keine Verstoesse, Adresspruefung weist alle acht Testfaelle korrekt zu. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
88 lines
3.0 KiB
TypeScript
88 lines
3.0 KiB
TypeScript
import { notFound } from "next/navigation"
|
||
|
||
import { PublicHeader } from "@/components/public-header"
|
||
import { SiteFooter } from "@/components/site-footer"
|
||
import { markdownToHtml } from "@/lib/markdown"
|
||
import { SITE_PAGE_META, SitePageKey, isSafeMediaUrl } from "@/lib/site-pages"
|
||
import { readSitePages } from "@/lib/site-pages.server"
|
||
|
||
/**
|
||
* Rendert eine der rechtlichen Seiten aus dem im Admin gepflegten Markdown.
|
||
*
|
||
* Serverseitig gerendert: Der Text steht ohne JavaScript im Dokument und ist
|
||
* damit auch für Screenreader, Suchmaschinen und Archivierung erreichbar.
|
||
* Ist nichts gepflegt, gibt es die Seite nicht – ein leeres Impressum wäre
|
||
* irreführender als ein 404.
|
||
*/
|
||
export async function LegalPage({ page }: { page: SitePageKey }) {
|
||
const pages = await readSitePages()
|
||
const content = pages[page].trim()
|
||
|
||
if (!content) notFound()
|
||
|
||
const html = await markdownToHtml(content)
|
||
const meta = SITE_PAGE_META[page]
|
||
|
||
// Zweite Prüfung vor der Ausgabe: Was in der Datenbank steht, könnte aus
|
||
// einer Zeit vor der Prüfung in der API stammen.
|
||
const video = isSafeMediaUrl(pages.signLanguageVideo)
|
||
? pages.signLanguageVideo.trim()
|
||
: ""
|
||
const captions = isSafeMediaUrl(pages.signLanguageCaptions)
|
||
? pages.signLanguageCaptions.trim()
|
||
: ""
|
||
|
||
return (
|
||
<div className="flex min-h-screen flex-col bg-background">
|
||
<PublicHeader subtitle={meta.title} />
|
||
|
||
<main id="inhalt" tabIndex={-1} className="mx-auto w-full max-w-3xl flex-1 px-4 py-6 sm:px-6 sm:py-8">
|
||
<h1 className="mb-6 text-2xl font-semibold tracking-tight">
|
||
{meta.title}
|
||
</h1>
|
||
|
||
{/* Erläuterungen in Deutscher Gebärdensprache nach § 4 BITV 2.0.
|
||
Der native Player ist mit der Tastatur bedienbar; Untertitel
|
||
werden eingebunden, sobald eine WebVTT-Datei hinterlegt ist. */}
|
||
{page === "signLanguage" && video && (
|
||
<figure className="mb-8">
|
||
<video
|
||
controls
|
||
preload="metadata"
|
||
className="w-full rounded-lg border bg-muted"
|
||
>
|
||
<source src={video} />
|
||
{captions && (
|
||
<track
|
||
src={captions}
|
||
kind="captions"
|
||
srcLang="de"
|
||
label="Deutsch"
|
||
default
|
||
/>
|
||
)}
|
||
Ihr Browser kann dieses Video nicht abspielen.{" "}
|
||
<a href={video}>Video herunterladen</a>
|
||
</video>
|
||
<figcaption className="mt-2 text-sm text-muted-foreground">
|
||
Erläuterungen zu diesem Portal in Deutscher Gebärdensprache
|
||
</figcaption>
|
||
</figure>
|
||
)}
|
||
|
||
<div
|
||
className="markdown-content"
|
||
dangerouslySetInnerHTML={{ __html: html }}
|
||
/>
|
||
</main>
|
||
|
||
<SiteFooter />
|
||
</div>
|
||
)
|
||
}
|
||
|
||
/** Seitentitel für den Browser-Tab. */
|
||
export function legalPageMetadata(page: SitePageKey) {
|
||
return { title: SITE_PAGE_META[page].title }
|
||
}
|