, siehe * public/index.php (Prefix-Match, da app/routes.php nur exakte Slugs kennt). * Ermittelt den Slug selbst wieder aus der URL, Daten aus * data/veranstaltungen.json — Texte chat-gepflegt, `plakat` und `galerie` * schreibt bin/veranstaltung-bilder.php. * * Geladen wird hier bewusst UNGEFILTERT (veranstaltung_find(), nicht * veranstaltungen_load()): ein Entwurf muss unter seiner direkten URL lesbar * bleiben, damit er gegengelesen werden kann. Sichtbar wird er dadurch nirgends sonst, und er * trägt noindex. * * Bewusst OHNE das übliche hero-Component (wie die Stadionzeitungs-Ausgabe): * ein Archiv-Eintrag hat kein eigenes Titelbild, und das einzige vorhandene * Bild ist ein Plakat im Hochformat, das als cover-beschnittener * Hero-Hintergrund zerstört würde. Stattdessen ein schmaler Kopf mit Rückweg. * Die Übersichtsseite /veranstaltungen behält den regulären Unterseiten-Hero. */ $urlSlug = trim((string) parse_url($_SERVER['REQUEST_URI'] ?? '', PHP_URL_PATH), '/'); $veranstaltungSlug = substr($urlSlug, strlen('veranstaltungen/')); $v = veranstaltung_find($veranstaltungSlug); if ($v === null) { // http_response_code(404) ist globaler Engine-State (kein Scope-Problem wie // $meta/$current) — public/index.php liest ihn nach render_page() wieder aus, // um $current auf '404' zu setzen (noindex + kein Self-Canonical in meta.php). http_response_code(404); require APP_PATH . '/pages/404.php'; return; } $isDraft = !veranstaltung_is_published($v); // Der Lebenszyklus läuft über das Datum, nicht über ein Feld von Hand: // vor dem Fest wirbt die Seite, danach erzählt sie. Maßgeblich ist der LETZTE // Tag (termin_is_upcoming()), damit ein dreitägiges Fest am Samstag nicht schon // zum Rückblick wird. $isVorbei = !termin_is_upcoming($v, date('Y-m-d')); $plakate = $v['plakate'] ?? []; $galerie = $v['galerie'] ?? []; $dateText = german_date_range((string) $v['date_start'], $v['date_end'] ?? null); $meta = [ 'title' => $v['title'], 'description' => (string) ($v['text'] ?? $v['title']), 'schema' => array_merge( page_schema([ ['name' => 'Startseite', 'slug' => ''], ['name' => 'Der Verein', 'slug' => 'verein'], ['name' => 'Veranstaltungen', 'slug' => 'veranstaltungen'], ['name' => $v['title'], 'slug' => 'veranstaltungen/' . $v['slug']], ]), array_values(array_filter([event_schema($v, 'veranstaltungen/' . $v['slug'])])) ), ]; // Die Lightbox kommt nur dazu, wenn es auch etwas zu vergrößern gibt. if ($plakate !== [] || $galerie !== []) { $meta['scripts'] = ['lightbox.js']; } $ogBild = $plakate[0] ?? $galerie[0] ?? null; if ($ogBild !== null && !empty($ogBild['widths'])) { $meta['og_image'] = $ogBild['base'] . '-' . max($ogBild['widths']) . '.jpg'; } if ($isDraft) { $meta['noindex'] = true; } // Einleitung: vor dem Fest die Ankündigung, danach der Rückblick. Fehlt der // jeweilige Text noch, trägt der Teaser aus `text` die Sektion — die Seite darf // nie leer sein, sie startet ja mit nichts als dem Plakat. // Fällt der jeweilige Text noch, trägt der Teaser aus `text` die Sektion — die // Seite darf nie leer sein, sie startet ja mit nichts als dem Plakat. $introText = $isVorbei ? ($v['rueckblick'] ?? null) : ($v['ankuendigung'] ?? null); $introSection = !empty($introText) ? [ 'id' => 'ueberblick', 'title' => $isVorbei ? 'Unser Rückblick' : 'Das erwartet dich', // Ohne Bild läuft die Sektion einspaltig auf Lesebreite (split--plain). 'text' => $introText, ] : null; ?> ['href' => url('veranstaltungen'), 'label' => 'Alle Veranstaltungen'], 'title' => $v['title'], 'badge' => $isDraft ? 'Entwurf' : '', 'date_iso' => (string) $v['date_start'], 'date_text' => $dateText, 'location' => (string) ($v['location'] ?? ''), ]); ?> $introSection]); ?> $galerie, 'title' => 'Bilder']); ?> $plakate, 'title' => 'Plakate von damals', 'compact' => true]); ?> $plakate, 'title' => count($plakate) > 1 ? 'Plakat und Programm' : 'Plakat']); ?> $galerie, 'title' => 'Bilder']); ?> $dateText, 'date_iso' => (string) $v['date_start'], 'time' => (string) ($v['time'] ?? ''), 'location' => (string) ($v['location'] ?? ''), 'karte' => (string) ($v['karte'] ?? ''), 'anfahrt' => $v['anfahrt'] ?? [], ]); ?> $abschnitt]); ?> $v['programm'] ?? [], 'title' => $isVorbei ? 'So lief unser Programm' : 'Programm', 'compact' => $isVorbei, ]); ?> $v['helfer'], 'anchor' => 'helfen']); ?> $v['partner'], 'anchor' => 'fest-partner']); ?>