Sportfest-Ausgabe der Stadionzeitung zum Heimspiel gegen ATS Wartenfels
Neue Live-Seiten, geschaltet vom chat-gepflegten Feld `veranstaltung` (Slug aus data/veranstaltungen.json) — Programm und Turnierpläne kommen damit aus derselben Quelle wie die Website, nicht als Kopie ins Heft: - Programm-Seite (programm[], rendert zugleich auf /veranstaltungen/sportfest-2026) - vier Turnierplan-Seiten (turniere[]) mit leerer Ergebnisspalte und leerer Abschlusstabelle zum Eintragen am Platz - Gegnerseite je Mannschaft mit Heimspiel am Fest statt nur der tragenden: stadionzeitung_gegner_fixtures() ist die einzige Stelle, die das entscheidet, Seitenplanung und Snapshot lesen beide dort. `gegner` ist deshalb eine Map je Team-Key; die Vorschau überspringt jedes Spiel mit eigener Gegnerseite - Danke-Seite mit Partner-Logo-Wand aus data/partners.json und QR auf /partner-werden, dazu das Anzeigen-Feld `platzierung` für Motive mit festem Platz auf einer Live-Seite (Emons über der Logo-Wand). Vorher klebte der Dank als leere Fläche unten an einem Anzeigen-Bild - Cover einer Sonderausgabe: das Fest als Titelgeschichte statt einer der drei Begegnungen, dazu das Hauptplakat schräg gelegt an Stelle des Spielerfotos Ausgabe 2026-09-04-ats-wartenfels als ENTWURF angelegt, 48 Seiten (12 Bögen), Tabellen auf gerader Seite, längster Anzeigenblock zwei. Veröffentlicht wird auf dem Server, dort liegt der frische Matchcenter-Stand. Nebenbei behoben: bin/anzeige-add.php räumte beim Anlegen alle Motive weg, deren Slug mit demselben Text beginnt (`emons` löschte `emons-partnerdank` und `emons-stuebinger`). Muster jetzt auf die Breiten-Endung eingeschränkt. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01KubsajzuV6N6QsSd3xKNjC
This commit is contained in:
138
app/helpers.php
138
app/helpers.php
@@ -568,6 +568,27 @@ function veranstaltungen_load(): array
|
||||
return $items;
|
||||
}
|
||||
|
||||
/**
|
||||
* Eine Veranstaltung per Slug, UNGEFILTERT (auch Entwürfe) — für die
|
||||
* Detailseite (app/pages/veranstaltung.php) und die Stadionzeitungs-Live-Seiten
|
||||
* der Sportfest-Ausgabe (Programm, Turnierpläne, Gegnerseiten je Heimspiel).
|
||||
* Ungefiltert aus demselben Grund wie die Detailseite: ein Entwurf muss sich
|
||||
* gegenlesen lassen, und eine Entwurfs-Ausgabe der Stadionzeitung darf auf
|
||||
* einen Entwurfs-Termin zeigen. null, wenn es den Slug nicht gibt.
|
||||
*/
|
||||
function veranstaltung_find(string $slug): ?array
|
||||
{
|
||||
if ($slug === '') {
|
||||
return null;
|
||||
}
|
||||
foreach (json_load('veranstaltungen')['veranstaltungen'] ?? [] as $entry) {
|
||||
if (($entry['slug'] ?? '') === $slug) {
|
||||
return $entry;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Steht ein Termin zum Bezugsdatum noch an? Maßgeblich ist das ENDE, nicht der
|
||||
* Beginn: ein mehrtägiges Fest (Sportfest, Freitag bis Sonntag) gehört am
|
||||
@@ -955,6 +976,86 @@ function stadionzeitung_is_published(array $issue): bool
|
||||
return ($issue['status'] ?? 'veroeffentlicht') !== 'entwurf';
|
||||
}
|
||||
|
||||
/**
|
||||
* Coverzeile „Im Heft" einer Ausgabe — die EINE Quelle für das Cover
|
||||
* (stadionzeitung-cover.php) und die Archivkarte (stadionzeitung-cards.php),
|
||||
* damit beide nie Verschiedenes sagen. Eine Sonderausgabe (Sportfest) setzt
|
||||
* `cover.teaser` chat-gepflegt; sonst trägt die Coverzeile des Interviews
|
||||
* (`interview.teaser`). Leer, wenn es weder das eine noch das andere gibt.
|
||||
*/
|
||||
function stadionzeitung_teaser(array $issue): string
|
||||
{
|
||||
$teaser = trim((string) ($issue['cover']['teaser'] ?? ''));
|
||||
if ($teaser !== '') {
|
||||
return $teaser;
|
||||
}
|
||||
return trim((string) ($issue['interview']['teaser'] ?? ''));
|
||||
}
|
||||
|
||||
/**
|
||||
* Welche Mannschaften bekommen in einer Ausgabe eine Gegnerseite — die EINE
|
||||
* Logik dafür, benutzt von bin/stadionzeitung-add.php (Seitenplanung) UND
|
||||
* stadionzeitung_build_snapshot() (Daten), damit nie eine Seite ohne Daten
|
||||
* oder Daten ohne Seite entstehen.
|
||||
*
|
||||
* Immer: die tragende Mannschaft (Gegner = `opponent`, Tag = `date`).
|
||||
* Zusätzlich, wenn die Ausgabe eine `veranstaltung` (Slug) mit Zeitraum trägt
|
||||
* (Sportfest-Ausgabe): jede weitere Mannschaft aus matchcenter.json mit einem
|
||||
* HEIMspiel in diesem Zeitraum — drei Heimspiele am Fest, drei Gegnerseiten.
|
||||
* Der Gegner ist dann der andere Verein der Begegnung. Reihenfolge wie in
|
||||
* matchcenter.json (= config 'matchcenter.teams').
|
||||
*
|
||||
* Rückgabe: [team_key => ['opponent' => string, 'date' => 'YYYY-MM-DD']]
|
||||
*/
|
||||
function stadionzeitung_gegner_fixtures(array $issue): array
|
||||
{
|
||||
$date = (string) ($issue['date'] ?? '');
|
||||
$issueTeam = (string) ($issue['team_key'] ?? '');
|
||||
$issueOpponent = trim((string) ($issue['opponent'] ?? ''));
|
||||
|
||||
$von = '';
|
||||
$bis = '';
|
||||
$veranstaltung = veranstaltung_find((string) ($issue['veranstaltung'] ?? ''));
|
||||
if ($veranstaltung !== null) {
|
||||
$von = (string) ($veranstaltung['date_start'] ?? '');
|
||||
$bis = (string) ($veranstaltung['date_end'] ?? $von);
|
||||
}
|
||||
|
||||
$fixtures = [];
|
||||
foreach (json_load('matchcenter')['teams'] ?? [] as $team) {
|
||||
$key = (string) ($team['key'] ?? '');
|
||||
if ($key === '') {
|
||||
continue;
|
||||
}
|
||||
if ($key === $issueTeam) {
|
||||
if ($issueOpponent !== '') {
|
||||
$fixtures[$key] = ['opponent' => $issueOpponent, 'date' => $date];
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if ($von === '') {
|
||||
continue;
|
||||
}
|
||||
foreach ($team['next_matches'] ?? [] as $match) {
|
||||
if (empty($match['is_home'])) {
|
||||
continue;
|
||||
}
|
||||
$matchDate = substr((string) ($match['kickoff'] ?? ''), 0, 10);
|
||||
if ($matchDate < $von || $matchDate > $bis) {
|
||||
continue;
|
||||
}
|
||||
$opponent = trim((string) ($match['away'] ?? ''));
|
||||
if ($opponent === '' || $opponent === 'SPIELFREI') {
|
||||
continue;
|
||||
}
|
||||
$fixtures[$key] = ['opponent' => $opponent, 'date' => $matchDate];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return $fixtures;
|
||||
}
|
||||
|
||||
/**
|
||||
* Die neueste VERÖFFENTLICHTE Ausgabe (oder null, wenn es keine gibt) — für die
|
||||
* Kurz-URL /stadionzeitung/aktuell, auf die gedruckte QR-Codes zeigen. Sortiert
|
||||
@@ -988,12 +1089,15 @@ function stadionzeitung_build_snapshot(array $issue): array
|
||||
{
|
||||
$date = (string) ($issue['date'] ?? '');
|
||||
$issueTeam = (string) ($issue['team_key'] ?? '');
|
||||
$issueOpponent = trim((string) ($issue['opponent'] ?? ''));
|
||||
|
||||
$tabellen = [];
|
||||
$ergebnisse = [];
|
||||
$vorschau = [];
|
||||
$gegner = null;
|
||||
// Gegnerseiten als Map je Team-Key (wie `tabellen`): welche Mannschaften
|
||||
// eine bekommen, entscheidet stadionzeitung_gegner_fixtures() — dieselbe
|
||||
// Funktion, mit der bin/stadionzeitung-add.php die Seiten einplant.
|
||||
$gegnerFixtures = stadionzeitung_gegner_fixtures($issue);
|
||||
$gegner = [];
|
||||
foreach (json_load('matchcenter')['teams'] ?? [] as $team) {
|
||||
$key = (string) ($team['key'] ?? '');
|
||||
if ($key === '') {
|
||||
@@ -1006,11 +1110,14 @@ function stadionzeitung_build_snapshot(array $issue): array
|
||||
'table' => $team['table'] ?? [],
|
||||
];
|
||||
|
||||
// Gegnerseite der tragenden Mannschaft: ausschließlich aus demselben
|
||||
// Matchcenter-Datensatz wie die Tabelle ableiten. So gibt es keinen
|
||||
// zweiten BFV-Abruf und der Stand wird beim Veröffentlichen zusammen
|
||||
// mit den übrigen Live-Daten eingefroren.
|
||||
if ($key === $issueTeam && $issueOpponent !== '') {
|
||||
// Gegnerseite(n): ausschließlich aus demselben Matchcenter-Datensatz
|
||||
// wie die Tabelle ableiten. So gibt es keinen zweiten BFV-Abruf und der
|
||||
// Stand wird beim Veröffentlichen zusammen mit den übrigen Live-Daten
|
||||
// eingefroren. Normal nur die tragende Mannschaft; in der Sportfest-
|
||||
// Ausgabe jede Mannschaft mit Heimspiel am Fest (s. gegner_fixtures).
|
||||
if (isset($gegnerFixtures[$key])) {
|
||||
$gegnerName = $gegnerFixtures[$key]['opponent'];
|
||||
$gegnerDate = $gegnerFixtures[$key]['date'];
|
||||
$table = is_array($team['table'] ?? null) ? $team['table'] : [];
|
||||
$ownRow = null;
|
||||
$opponentRow = null;
|
||||
@@ -1018,7 +1125,7 @@ function stadionzeitung_build_snapshot(array $issue): array
|
||||
if (!empty($row['is_own_club'])) {
|
||||
$ownRow = $row;
|
||||
}
|
||||
if (strcasecmp(trim((string) ($row['club'] ?? '')), $issueOpponent) === 0) {
|
||||
if (strcasecmp(trim((string) ($row['club'] ?? '')), $gegnerName) === 0) {
|
||||
$opponentRow = $row;
|
||||
}
|
||||
}
|
||||
@@ -1027,15 +1134,15 @@ function stadionzeitung_build_snapshot(array $issue): array
|
||||
foreach ($team['next_matches'] ?? [] as $match) {
|
||||
$matchDate = substr((string) ($match['kickoff'] ?? ''), 0, 10);
|
||||
$clubs = [(string) ($match['home'] ?? ''), (string) ($match['away'] ?? '')];
|
||||
if ($matchDate === $date && in_array($issueOpponent, $clubs, true)) {
|
||||
if ($matchDate === $gegnerDate && in_array($gegnerName, $clubs, true)) {
|
||||
$fixture = $match;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
$gegner = [
|
||||
$gegner[$key] = [
|
||||
'team_name' => (string) ($team['name'] ?? ''),
|
||||
'opponent' => $issueOpponent,
|
||||
'opponent' => $gegnerName,
|
||||
'league' => (string) ($team['league'] ?? ''),
|
||||
'season' => (string) ($team['season'] ?? ''),
|
||||
'fixture' => $fixture,
|
||||
@@ -1064,7 +1171,11 @@ function stadionzeitung_build_snapshot(array $issue): array
|
||||
|
||||
// Vorschau: das nächste Spiel dieser Mannschaft NACH dem Spieltag der
|
||||
// Ausgabe. Das Heft-Spiel selbst fliegt raus (das Cover zeigt es
|
||||
// schon); ein Spiel einer ANDEREN Mannschaft am selben Tag bleibt drin.
|
||||
// schon), und ebenso jedes Spiel, das eine eigene Gegnerseite hat
|
||||
// (Sportfest-Ausgabe: Zweite und Frauen stünden sonst doppelt im Heft).
|
||||
// Ein Spiel einer ANDEREN Mannschaft am selben Tag ohne Gegnerseite
|
||||
// bleibt drin.
|
||||
$gegnerDate = $gegnerFixtures[$key]['date'] ?? null;
|
||||
foreach ($team['next_matches'] ?? [] as $match) {
|
||||
// Spielfreie Runden führt der BFV als Pseudo-Begegnung gegen
|
||||
// "SPIELFREI" — kein echtes Spiel, im Heft überspringen.
|
||||
@@ -1078,6 +1189,9 @@ function stadionzeitung_build_snapshot(array $issue): array
|
||||
if ($matchDate === $date && $key === $issueTeam) {
|
||||
continue;
|
||||
}
|
||||
if ($gegnerDate !== null && $matchDate === $gegnerDate) {
|
||||
continue;
|
||||
}
|
||||
$vorschau[] = [
|
||||
'team_key' => $key,
|
||||
'team_name' => (string) ($team['name'] ?? ''),
|
||||
|
||||
Reference in New Issue
Block a user