feat(footer): hideable legal links + socials + promo banner (#441 + #440)

Combined footer overhaul:

- Per-CMS-page show_in_footer toggle (#441) — admins can hide
  Impressum / Datenschutz from the gallery footer when an external
  privacy / imprint URL is enough.
- Five social-media URL fields in branding settings (#441) — Facebook,
  Instagram, WhatsApp, X/Twitter, YouTube. Empty string hides each
  icon individually; the row is omitted when none are set.
- Promotional banner slot above or below the gallery footer (#440) —
  global default authored as markdown in branding settings, plus a
  three-way per-event override on the Edit Event form
  (inherit / custom / off). Backend nulls promo_markdown automatically
  when mode != 'custom' so stale text never persists.

Sanitization: marked with gfm/breaks → DOMPurify with a tight
allowlist (no img, no tables, no inline html). Post-process forces
target=_blank rel="noopener noreferrer nofollow" on every link so
admin-set URLs can't tab-nap the gallery context.

i18n covers all six locales (en/de/nl/pt/ru/fr).

Targets the beta branch.
This commit is contained in:
Paul Nothaft
2026-05-10 21:46:20 +02:00
parent c52c1c8419
commit 3a731e7c95
25 changed files with 778 additions and 93 deletions
+3
View File
@@ -10,6 +10,8 @@ export interface CMSPage {
logo_url: string | null;
use_external_url: boolean;
external_url: string | null;
// Footer visibility (#441). True = link rendered in the gallery footer.
show_in_footer?: boolean;
updated_at: string;
}
@@ -20,6 +22,7 @@ export interface PublicCMSPage {
logo_url: string | null;
use_external_url: boolean;
external_url: string | null;
show_in_footer?: boolean;
updated_at: string;
}
@@ -25,6 +25,14 @@ export interface PublicSettings {
* AdminDarkModeContext + ThemeContext both honor this.
*/
branding_force_color_mode?: 'dark' | 'light' | null;
// Footer overhaul (#441 + #440). Empty strings mean "hide".
branding_facebook_url?: string;
branding_instagram_url?: string;
branding_whatsapp_url?: string;
branding_twitter_url?: string;
branding_youtube_url?: string;
branding_promo_markdown?: string;
branding_promo_position?: 'above_footer' | 'below_footer';
theme_config: any;
default_language: string;
enable_analytics: boolean;
+10
View File
@@ -25,6 +25,16 @@ export interface BrandingSettings {
* `colorMode` override is ignored. `null` means no force (default behavior).
*/
force_color_mode?: 'dark' | 'light' | null;
// Footer overhaul (#441 + #440). Empty strings hide each social
// icon individually; promo_markdown empty hides the slot for events
// in 'inherit' mode. Position controls global default placement.
facebook_url?: string;
instagram_url?: string;
whatsapp_url?: string;
twitter_url?: string;
youtube_url?: string;
promo_markdown?: string;
promo_position?: 'above_footer' | 'below_footer';
}
export interface ThemeSettings {