/** * Die eigenständigen Seiten des Portals. * * Drei rechtliche Pflichtseiten sowie zwei Seiten, die § 4 BITV 2.0 von * öffentlichen Stellen verlangt: Erläuterungen in Leichter Sprache und in * Deutscher Gebärdensprache. */ export const SITE_PAGES = [ "imprint", "privacy", "accessibility", "easyLanguage", "signLanguage", ] as const /** Bezeichner einer Seite. */ export type SitePageKey = (typeof SITE_PAGES)[number] /** Überschrift und URL je Seite – für Fußzeile, Admin und die Seiten selbst. */ export const SITE_PAGE_META: Record< SitePageKey, { title: string; short: string; path: string; hint: string } > = { imprint: { title: "Impressum", short: "Impressum", path: "/impressum", hint: "Anbieterkennzeichnung nach § 5 DDG", }, privacy: { title: "Datenschutzerklärung", short: "Datenschutz", path: "/datenschutz", hint: "Datenverarbeitung durch dieses Portal selbst", }, accessibility: { title: "Erklärung zur Barrierefreiheit", short: "Barrierefreiheit", path: "/barrierefreiheit", hint: "Pflichtangabe öffentlicher Stellen nach § 7 BITV 2.0, jährlich zu überprüfen", }, easyLanguage: { title: "Leichte Sprache", short: "Leichte Sprache", path: "/leichte-sprache", hint: "Pflichtangabe nach § 4 BITV 2.0: wesentliche Inhalte, Navigation und Barrierefreiheitserklärung in Leichter Sprache", }, signLanguage: { title: "Gebärdensprache", short: "Gebärdensprache", path: "/gebaerdensprache", hint: "Pflichtangabe nach § 4 BITV 2.0: dieselben Inhalte in Deutscher Gebärdensprache, üblicherweise als Video", }, } /** * Zusätzliche Angaben, die keine eigene Seite sind, sondern zu einer gehören. * * Das Gebärdensprach-Video steht bewusst in eigenen Feldern und nicht als * HTML im Markdown: Der Markdown-Prozessor filtert HTML weg, und ihn dafür zu * öffnen würde die Angriffsfläche über alle Textfelder ausweiten. */ export const SITE_MEDIA = ["signLanguageVideo", "signLanguageCaptions"] as const export type SiteMediaKey = (typeof SITE_MEDIA)[number] /** Alle Felder, die API und Admin austauschen. */ export type SitePagesContent = Record /** Leere Ausgangsinhalte – solange im Admin nichts gepflegt wurde. */ export const EMPTY_SITE_PAGES: SitePagesContent = { imprint: "", privacy: "", accessibility: "", easyLanguage: "", signLanguage: "", signLanguageVideo: "", signLanguageCaptions: "", } /** * Prüft eine Medienadresse. Zugelassen sind nur eine Adresse auf dem eigenen * Server (mit / beginnend) und https. Damit sind `javascript:` und `data:` * ausgeschlossen, bevor der Wert in ein src-Attribut gelangt. */ export function isSafeMediaUrl(value: string) { const wert = value.trim() if (!wert) return true if (wert.startsWith("/") && !wert.startsWith("//")) return true try { return new URL(wert).protocol === "https:" } catch { return false } }