Stadionzeitung, Veranstaltungen, News-Artikel, Lightbox und Anzeigen-Verteilung
Stadionzeitung: Live-Seiten (Tabellen, Ergebnisse, Vorschau, Termine, News,
Kontakt, Historie, Sportheim, Partner, Momente, Impressum), automatisches Cover,
Swipe-Viewer mit Vollbildmodus und die Druckfassung als Falt-Heft. Anzeigen
kommen jetzt aus dem Bestand und werden gleichmäßig verteilt, nie mehr als zwei
hintereinander (vorher vier Blöcke à fünf). Die Doppelseiten-Regel rechnet das
Skript selbst, statt sie von Hand zu prüfen.
Veranstaltungen: Bereich /veranstaltungen mit Detailseite pro Fest, Lebenszyklus
über das Datum (Ankündigung vor dem Fest, Rückblick danach), Galerie mit
Lightbox. veranstaltungen.json ist dritte Termin-Quelle, damit ein Fest nie
doppelt gepflegt wird.
News-Artikel als dritte dynamische Prefix-Route, gemeinsamer detail-head.
Behobene Fehler:
- Wappen-Kontexte setzten Zeilen-Layout auf .crest statt auf die Zeile; das
Wappen wurde zum Grid, Vereinsname und Tore rutschten darunter zusammen
(Ergebnis-Rückblick, Vorschau, Cover).
- --header-h (60px) unterschätzt die Kopfleiste um 32px (Logo 72px + 20px
Versatz): neues abgeleitetes --header-space, sonst klebten Zurück-Links
unter dem Logo.
- base_url in der Produktions-Config ohne www, während .htaccess auf www
umleitet; preflight prüft das jetzt.
- .lightbox brauchte display:none mit (0,2,0), sonst lag das Overlay offen.
Neue Werkzeuge: bin/anzeige-add.php (Anzeigen-Bestand), bin/veranstaltung-bilder.php
(Plakate und Galerien, Alt-Texte folgen der Quelldatei), bin/qr.php (Druck-QR mit
Warnung, wenn base_url nicht auf die echte Domain zeigt).
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NzeVVgMCrzCDJAKeLEdKr7
2026-07-31 14:55:27 +02:00
< ? php
declare ( strict_types = 1 );
/**
* Einzelne Stadionzeitung - Ausgabe : dynamische Route stadionzeitung /< slug > ,
* siehe public / index . php ( Prefix - Match , da app / routes . php nur exakte Slugs
* kennt ) . Ermittelt den Ausgaben - Slug selbst wieder aus der URL , Daten aus
* data / stadionzeitung . json ( maschinenverwaltet von bin / stadionzeitung - add . php ) .
*
* Bewusst OHNE das übliche hero - Component : der Swipe - Viewer soll so viel
* Bildschirmhöhe wie möglich bekommen ( gerade am Handy ) — stattdessen ein
* schmaler Kopfbereich direkt über dem Viewer .
*/
$urlSlug = trim (( string ) parse_url ( $_SERVER [ 'REQUEST_URI' ] ? ? '' , PHP_URL_PATH ), '/' );
$ausgabeSlug = substr ( $urlSlug , strlen ( 'stadionzeitung/' ));
/*
* Reservierter Slug „aktuell " : leitet auf die neueste veröffentlichte Ausgabe
* weiter . Damit zeigt ein GEDRUCKTER QR - Code ( Plakat , Aushang ) dauerhaft auf die
* jeweils aktuelle Ausgabe , ohne dass zu jedem Heimspiel ein neuer Code
* entstehen muss . Kollision ausgeschlossen : Ausgaben - Slugs beginnen immer mit
* dem Datum ( 2026 - 08 - 01 - … ) .
*
* Drei Dinge sind hier load - bearing :
* 1. ** 302 , nie 301. ** Ein 301 wird von Browsern und Crawlern dauerhaft
* gemerkt — das Plakat zeigte dann für immer auf die erste Ausgabe , und
* zwar unreparierbar auf allen Geräten , die ihn einmal gesehen haben .
* 2. `no-store` , damit ein Handy , das im letzten Monat gescannt hat , nicht
* das alte Ziel wiederverwendet .
* 3. Ohne veröffentlichte Ausgabe geht es aufs Archiv statt in einen 404 —
* ein gedruckter Code darf nie ins Leere führen .
*/
if ( $ausgabeSlug === 'aktuell' ) {
$latest = stadionzeitung_latest ();
header ( 'Cache-Control: no-store' );
header (
'Location: ' . ( $latest !== null ? url ( 'stadionzeitung/' . $latest [ 'slug' ]) : url ( 'stadionzeitung' )),
true ,
302
);
// exit, nicht return: nach einem `return` liefe in public/index.php noch
// layout.php und hängte einen kompletten HTML-Body an die Weiterleitung.
// Der Ausgabepuffer aus render_page() ist hier noch leer, es geht also
// nichts verloren.
exit ;
}
$issue = null ;
foreach ( json_load ( 'stadionzeitung' )[ 'issues' ] ? ? [] as $entry ) {
if (( $entry [ 'slug' ] ? ? '' ) === $ausgabeSlug ) {
$issue = $entry ;
break ;
}
}
if ( $issue === 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 ;
}
// Entwurf vs. veröffentlicht (Interview-Beschluss 29.07.2026): Entwürfe sind
// „unsichtbar, aber offen" — sie fehlen im Archiv und in der Sitemap und
// tragen noindex, die direkte URL funktioniert aber (zum Gegenlesen ohne
// Login-System). Die Druckfassung (?druck=1) montiert dieselben Seiten in
// Broschüren-Reihenfolge fürs gefaltete A5-Heft.
$isDraft = ! stadionzeitung_is_published ( $issue );
$isDruck = isset ( $_GET [ 'druck' ]);
// Erste Seite ist seit dem automatischen Cover (Phase 3) eine Live-Seite ohne
// eigenes Bild — og_image gibt es nur, wenn die Ausgabe (noch/wieder) mit
// einem Bild beginnt, sonst greift das globale Default-OG-Bild (meta.php).
$firstImage = $issue [ 'images' ][ 0 ] ? ? null ;
$cover = ( $firstImage !== null && ( $firstImage [ 'type' ] ? ? 'image' ) === 'image' ) ? $firstImage : null ;
$meta = [
2026-09-19 10:53:56 +02:00
'title' => 'Stadionzeitung gegen ' . stadionzeitung_opponent_display ( $issue ) . ( $isDruck ? ' (Druckfassung)' : '' ),
'description' => 'Stadionzeitung zum Heimspiel gegen ' . stadionzeitung_opponent_display ( $issue ) . ' am ' . german_date ( $issue [ 'date' ]) . ' – digital zum Durchblättern.' ,
Stadionzeitung, Veranstaltungen, News-Artikel, Lightbox und Anzeigen-Verteilung
Stadionzeitung: Live-Seiten (Tabellen, Ergebnisse, Vorschau, Termine, News,
Kontakt, Historie, Sportheim, Partner, Momente, Impressum), automatisches Cover,
Swipe-Viewer mit Vollbildmodus und die Druckfassung als Falt-Heft. Anzeigen
kommen jetzt aus dem Bestand und werden gleichmäßig verteilt, nie mehr als zwei
hintereinander (vorher vier Blöcke à fünf). Die Doppelseiten-Regel rechnet das
Skript selbst, statt sie von Hand zu prüfen.
Veranstaltungen: Bereich /veranstaltungen mit Detailseite pro Fest, Lebenszyklus
über das Datum (Ankündigung vor dem Fest, Rückblick danach), Galerie mit
Lightbox. veranstaltungen.json ist dritte Termin-Quelle, damit ein Fest nie
doppelt gepflegt wird.
News-Artikel als dritte dynamische Prefix-Route, gemeinsamer detail-head.
Behobene Fehler:
- Wappen-Kontexte setzten Zeilen-Layout auf .crest statt auf die Zeile; das
Wappen wurde zum Grid, Vereinsname und Tore rutschten darunter zusammen
(Ergebnis-Rückblick, Vorschau, Cover).
- --header-h (60px) unterschätzt die Kopfleiste um 32px (Logo 72px + 20px
Versatz): neues abgeleitetes --header-space, sonst klebten Zurück-Links
unter dem Logo.
- base_url in der Produktions-Config ohne www, während .htaccess auf www
umleitet; preflight prüft das jetzt.
- .lightbox brauchte display:none mit (0,2,0), sonst lag das Overlay offen.
Neue Werkzeuge: bin/anzeige-add.php (Anzeigen-Bestand), bin/veranstaltung-bilder.php
(Plakate und Galerien, Alt-Texte folgen der Quelldatei), bin/qr.php (Druck-QR mit
Warnung, wenn base_url nicht auf die echte Domain zeigt).
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NzeVVgMCrzCDJAKeLEdKr7
2026-07-31 14:55:27 +02:00
'scripts' => [ $isDruck ? 'stadionzeitung-druck.js' : 'stadionzeitung-viewer.js' ],
'schema' => page_schema ([
[ 'name' => 'Startseite' , 'slug' => '' ],
[ 'name' => 'Fußball' , 'slug' => 'fussball' ],
[ 'name' => 'Stadionzeitung' , 'slug' => 'stadionzeitung' ],
2026-09-19 10:53:56 +02:00
[ 'name' => 'Gegen ' . stadionzeitung_opponent_display ( $issue ), 'slug' => 'stadionzeitung/' . $issue [ 'slug' ]],
Stadionzeitung, Veranstaltungen, News-Artikel, Lightbox und Anzeigen-Verteilung
Stadionzeitung: Live-Seiten (Tabellen, Ergebnisse, Vorschau, Termine, News,
Kontakt, Historie, Sportheim, Partner, Momente, Impressum), automatisches Cover,
Swipe-Viewer mit Vollbildmodus und die Druckfassung als Falt-Heft. Anzeigen
kommen jetzt aus dem Bestand und werden gleichmäßig verteilt, nie mehr als zwei
hintereinander (vorher vier Blöcke à fünf). Die Doppelseiten-Regel rechnet das
Skript selbst, statt sie von Hand zu prüfen.
Veranstaltungen: Bereich /veranstaltungen mit Detailseite pro Fest, Lebenszyklus
über das Datum (Ankündigung vor dem Fest, Rückblick danach), Galerie mit
Lightbox. veranstaltungen.json ist dritte Termin-Quelle, damit ein Fest nie
doppelt gepflegt wird.
News-Artikel als dritte dynamische Prefix-Route, gemeinsamer detail-head.
Behobene Fehler:
- Wappen-Kontexte setzten Zeilen-Layout auf .crest statt auf die Zeile; das
Wappen wurde zum Grid, Vereinsname und Tore rutschten darunter zusammen
(Ergebnis-Rückblick, Vorschau, Cover).
- --header-h (60px) unterschätzt die Kopfleiste um 32px (Logo 72px + 20px
Versatz): neues abgeleitetes --header-space, sonst klebten Zurück-Links
unter dem Logo.
- base_url in der Produktions-Config ohne www, während .htaccess auf www
umleitet; preflight prüft das jetzt.
- .lightbox brauchte display:none mit (0,2,0), sonst lag das Overlay offen.
Neue Werkzeuge: bin/anzeige-add.php (Anzeigen-Bestand), bin/veranstaltung-bilder.php
(Plakate und Galerien, Alt-Texte folgen der Quelldatei), bin/qr.php (Druck-QR mit
Warnung, wenn base_url nicht auf die echte Domain zeigt).
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NzeVVgMCrzCDJAKeLEdKr7
2026-07-31 14:55:27 +02:00
]),
];
if ( $cover !== null ) {
$meta [ 'og_image' ] = $cover [ 'base' ] . '-' . max ( $cover [ 'widths' ]) . '.jpg' ;
}
// Entwürfe und die Druckfassung (parametrisierte Zweitansicht derselben
// Inhalte) gehören nicht in den Index.
if ( $isDraft || $isDruck ) {
$meta [ 'noindex' ] = true ;
}
?>
< ? php
// Kopfleiste: im Viewer Titel + Datum, in der Druckfassung stattdessen die
// Blatt-Bilanz und ein Drucken-Knopf griffbereit oben (der ausführliche
// Hinweis + Button am Seitenende bleibt).
$druckSeiten = count ( $issue [ 'images' ] ? ? []);
$druckBlaetter = ( int ) ceil ( $druckSeiten / 4 );
?>
< ? php /* Kopfleiste in drei Zonen und zwei Reihen : oben Rückweg links , Titel mittig ,
Aktion rechts — darunter eine ruhige Statuszeile . Die Badges standen
früher neben dem Titel und machten die Mitte unruhig ; Zustand ( „Entwurf " ,
„Druckfassung " ) und Bilanz sind Information, keine Überschrift. */ ?>
< header class = " stadionzeitung-issue-head<?= $isDruck ? ' stadionzeitung-issue-head--druck' : '' ?> " >
< div class = " container stadionzeitung-issue-head__inner " >
< a class = " stadionzeitung-issue-head__back " href = " <?= e( $isDruck ? url('stadionzeitung/' . $issue['slug'] ) : url('stadionzeitung')) ?> " >
< ? = icon ( 'chevron-left' ) ?> <?= $isDruck ? 'Ausgabe' : 'Archiv' ?>
</ a >
2026-09-19 10:53:56 +02:00
< p class = " stadionzeitung-issue-head__title " > Gegen < ? = e ( stadionzeitung_opponent_display ( $issue )) ?> </p>
Stadionzeitung, Veranstaltungen, News-Artikel, Lightbox und Anzeigen-Verteilung
Stadionzeitung: Live-Seiten (Tabellen, Ergebnisse, Vorschau, Termine, News,
Kontakt, Historie, Sportheim, Partner, Momente, Impressum), automatisches Cover,
Swipe-Viewer mit Vollbildmodus und die Druckfassung als Falt-Heft. Anzeigen
kommen jetzt aus dem Bestand und werden gleichmäßig verteilt, nie mehr als zwei
hintereinander (vorher vier Blöcke à fünf). Die Doppelseiten-Regel rechnet das
Skript selbst, statt sie von Hand zu prüfen.
Veranstaltungen: Bereich /veranstaltungen mit Detailseite pro Fest, Lebenszyklus
über das Datum (Ankündigung vor dem Fest, Rückblick danach), Galerie mit
Lightbox. veranstaltungen.json ist dritte Termin-Quelle, damit ein Fest nie
doppelt gepflegt wird.
News-Artikel als dritte dynamische Prefix-Route, gemeinsamer detail-head.
Behobene Fehler:
- Wappen-Kontexte setzten Zeilen-Layout auf .crest statt auf die Zeile; das
Wappen wurde zum Grid, Vereinsname und Tore rutschten darunter zusammen
(Ergebnis-Rückblick, Vorschau, Cover).
- --header-h (60px) unterschätzt die Kopfleiste um 32px (Logo 72px + 20px
Versatz): neues abgeleitetes --header-space, sonst klebten Zurück-Links
unter dem Logo.
- base_url in der Produktions-Config ohne www, während .htaccess auf www
umleitet; preflight prüft das jetzt.
- .lightbox brauchte display:none mit (0,2,0), sonst lag das Overlay offen.
Neue Werkzeuge: bin/anzeige-add.php (Anzeigen-Bestand), bin/veranstaltung-bilder.php
(Plakate und Galerien, Alt-Texte folgen der Quelldatei), bin/qr.php (Druck-QR mit
Warnung, wenn base_url nicht auf die echte Domain zeigt).
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NzeVVgMCrzCDJAKeLEdKr7
2026-07-31 14:55:27 +02:00
< ? php if ( $isDruck ) : ?>
< p class = " stadionzeitung-issue-head__actions " >
< button class = " btn btn--sm stadionzeitung-issue-head__print " type = " button " data - druck - print hidden >
< ? = icon ( 'printer' ) ?> Drucken
</ button >
</ p >
< ? php else : ?>
< p class = " stadionzeitung-issue-head__date " >
< time datetime = " <?= e( $issue['date'] ) ?> " >< ? = e ( german_date ( $issue [ 'date' ])) ?> </time>
</ p >
< ? php endif ; ?>
< ? php if ( $isDraft || $isDruck ) : ?>
< p class = " stadionzeitung-issue-head__state " >
< ? php if ( $isDraft ) : ?>
< span class = " stadionzeitung-issue-head__badge stadionzeitung-issue-head__badge--entwurf " > Entwurf </ span >
< ? php endif ; ?>
< ? php if ( $isDruck ) : ?>
< span > Druckfassung · < ? = $druckBlaetter ?> Blätter · <?= $druckSeiten ?> Seiten</span>
< ? php endif ; ?>
</ p >
< ? php endif ; ?>
</ div >
</ header >
< ? php component ( $isDruck ? 'stadionzeitung-druck' : 'stadionzeitung-viewer' , [ 'issue' => $issue ]); ?>