Medienakkreditierung

Changelog — Medienakkreditierung BTV

Format orientiert an Keep a Changelog.

Versionierung nach SemVer.

[1.4.0] — 2026-07-15

Neu

Das PDF (BTJ/BTV, 2 Seiten) liegt unter docs/ außerhalb des Webroots und

wird ausschließlich über die Route /safe-sport-merkblatt ausgeliefert (inline,

nosniff).

Session vermerkt; die Kenntnisnahme wird serverseitig nur akzeptiert, wenn

das Merkblatt in dieser Sitzung geöffnet wurde. Ein gesetzter Haken ohne

vorheriges Öffnen wird mit HTTP 422 und einem Hinweis abgewiesen — das greift

auch ohne JavaScript und lässt sich nicht durch manipulierte Formularfelder

umgehen.

wurde; der Status („noch nicht geöffnet" / „✓ geöffnet") ist direkt sichtbar.

[1.3.0] — 2026-07-15

Datenschutz-Anpassungen nach Vorgaben des Datenschutzbeauftragten.

Geändert

Verarbeitung zu" entfällt (die Verarbeitung ist zur Durchführung der

Akkreditierung erforderlich). Stattdessen steht direkt vor dem Absenden ein gut

sichtbarer Hinweis mit Direktlink: „Informationen zum Schutz Ihrer

personenbezogenen Daten finden Sie in unserer Datenschutzerklärung"

(→ btv-turnen.de/der-btv/ueber-uns/datenschutz). Medienrichtlinien- und

SAFE-SPORT-Checkbox bleiben Pflicht (nicht vorbelegt).

werden."

gespeichert (außerhalb Webroot, Deny-all, nur mit Board-Login, Zufallsnamen).

Neu

lassen sich einzeln entfernen (Nachweise/Badge durch Board-Berechtigte, die

vollständige Löschung nur durch die Administration).

entwertet.

Statusverlauf gelöscht, Mails anonymisiert.

wird protokolliert. Fristen und Kill-Switch unter Einstellungen → Aufbewahrung.

[1.2.1] — 2026-07-15

Neu

Veranstaltungen" filtert ein Suchfeld die Tabelle live nach Name, Ort,

Ansprechpartner:in oder Datum. Ohne JavaScript bleibt die komplette Tabelle

sichtbar.

[1.2.0] — 2026-07-15

Neu (Verbesserungsideen aus dem Betrieb)

ein Suchfeld, das während der Eingabe passende Veranstaltungen nach Name, Ort

oder Datum vorschlägt (Tastatur-Navigation inklusive). Ohne JavaScript bleibt das

native Dropdown voll nutzbar; die Auswahl steuert weiterhin dasselbe Feld, sodass

Ort/Zeitraum wie gehabt übernommen werden.

Tabellenkopf einen „Alle auswählen"-Haken. Sobald etwas markiert ist, erscheint

eine Aktionsleiste mit der Anzahl und den Sammelaktionen.

Rutsch genehmigen; die Genehmigungsmails werden gebündelt vorbereitet. Nur noch

offene Anträge werden verarbeitet, bereits entschiedene übersprungen.

automatisch erzeugt (inkl. Verify-QR) und die Badge-Mails mit dem jeweiligen PDF

im Anhang gebündelt versendet.

auch mit einer einzelnen Auswahl — Badge erstellen und versenden, ohne den Antrag

einzeln öffnen zu müssen.

Alle Sammelaktionen führen zunächst auf eine Vorschau: Der Fachbereich sieht

Empfänger:innen und übersprungene Anträge, passt den Text einmal an (Platzhalter

wie {vorname} werden je Empfänger:in eingesetzt) und löst erst per Klick den

gebündelten Versand aus. Damit bleibt die Regel „kein Automatikversand" gewahrt.

Behoben

Der mitgelieferte tFPDF-Font-Cache (*.mtx.php) enthielt den absoluten Pfad vom

Erzeugungsrechner. Er wird jetzt über __DIR__ bestimmt und ist damit portabel —

egal wohin die App deployt oder verschoben wird.

jetzt eine feste Größe (1,35 rem) und die BTV-Akzentfarbe.

Geändert

Name | Ort | von | bis (Ort/Datum optional). Wählt jemand im Formular eine

hinterlegte Veranstaltung, werden Ort und Zeitraum automatisch übernommen und als

Info angezeigt — sie müssen nicht erneut eingegeben werden. Bei „Andere

Veranstaltung" erscheinen wie bisher Freitextfelder. Die Werte einer hinterlegten

Veranstaltung kommen serverseitig aus der Liste, nicht aus dem Formular, sodass

manipulierte Felder keine abweichenden Angaben setzen können.

[1.1.1] — 2026-07-10

Behoben / Geändert (Rückmeldungen aus dem Betrieb)

rechts unten mit „prüfen"-Label; die Textbreite links ist begrenzt.

nicht nur die am Veranstaltungstag; Block heißt „In Bearbeitung / Vor-Ort-Anfragen".

Mail geschickt wurde (/wl/{token}/badge/{id}, Token-geschützt).

Freitextsuche mit kurzer Verzögerung.

ein Einstellungen-Tab; auch der Fachbereich Kommunikation darf sie pflegen.

WL-Infomail), Antragstellende weiterhin mit „Sie".

(bestätigt; die frühere Beobachtung war eine Opcache-Verzögerung nach Deploy).

[1.1.0] — 2026-07-10

Ausbaustufe v1.1 aus Erweiterungskonzept-v1.1.md (E1–E9). Auf frischer

Datenbank (schema + migrate_v1_1 + seed) end-to-end getestet.

Hinzugefügt

Ansprechpartner:in + E-Mail, WL-Token. Pflege in den Einstellungen mit

Vorlage-Schnellanlage und Token-Rotation. Excel-Import/-Export (.xlsx)

dependency-frei über ZipArchive + SimpleXML (app/xlsx.php); Import zweistufig

mit Vorschau (neu/aktualisiert/fehlerhaft), Datum tolerant (ISO/TT.MM.JJJJ/

Excel-Serial). Formular nutzt event_id + Text-Snapshot; Vorauswahl per

?veranstaltung={id}.

Widerrufen, Abgelehnt, Neu eingegangen), Badge-Spalte, 30-s-JSON-Polling,

Vor-Ort-Bestätigung der Badge-Ausgabe. Fremdtoken 404, 7 Tage nach Event-Ende

410, Abgelehnte per Setting abschaltbar.

Renderer für PNG und SVG (app/qr.php).

und Druckliste als Anhang; setzt „WL informiert".

ohne Login, Status zurück auf „In Prüfung", Info-Mail an presse@.

dem Badge, nachträglicher Badge-Download über den Verify-Link.

Doppelschutz über reminded_at; Dubletten-Hinweis im Board.

Reminder erweitert, CSV um Ansprechpartner.

Migration

mails-Erweiterungen. Bestehende Orte werden nicht migriert — die

events-Tabelle startet leer.

[1.0.0] — 2026-07-10

Alle Umsetzungsschritte aus Konzept Abschn. 17 sind fertig. Die 21 prüfbaren

Akzeptanzkriterien (Abschn. 18) laufen lokal grün; offen sind nur Zulieferungen

(O3–O5, O7, O8, O11, O12).

Hinzugefügt

bcrypt, Einladung und Passwort-Reset über zeitlich begrenzte Token. Der Token

liegt nur als SHA-256-Hash in der Datenbank — wer sie liest, kann daraus keinen

gültigen Link bauen. Passwörter werden nie im Klartext verschickt.

antwortet die Anmeldung mit 429 (nutzt dieselbe rate_limit-Tabelle wie das

Formular). Die Antwortzeit ist bei unbekannter E-Mail dieselbe wie bei falschem

Passwort, sodass sich nicht ermitteln lässt, welche Adressen ein Konto haben.

Auswahllisten (Funktionen, Veranstaltungen), alle sieben Mail-Vorlagen,

interner Empfänger, Aufbewahrungsfrist und Benutzerverwaltung. Konten werden

deaktiviert, nie gelöscht; das eigene Konto lässt sich nicht deaktivieren oder

herabstufen. Unvollständige POSTs leeren keine gepflegten Listen.

Inhalt zurückgelesen und geprüft.

an; beide setzen es selbst über „Passwort vergessen".

wäre, anonymisiert das Mail-Protokoll und löscht dann die Anträge. Bewusst

manuell, bis Projekt29 die Frist freigibt (O5).

und QR-Code.

Erst-Admin, Graph, Aufbewahrung, Routenübersicht.

Sicherheit

nutzbar (403), und der Self-Check verlangt mindestens ein anmeldefähiges Konto.

Behoben

freigegeben). Der Zielserver läuft auf 8.4, lokal 8.5.

[0.6.0] — 2026-07-10

Hinzugefügt

und Text kommen aus der Vorlage, sind editierbar und werden erst per Klick

verschickt. Ein Statuswechsel auf Rückfrage, Genehmigung, Ablehnung oder

Widerruf leitet direkt dorthin — automatisch verschickt wird weiterhin nichts.

(LGPL, kein Composer). A6 quer, BTV-Blau, eingebettete Century Gothic.

Enthält Name, Funktion, Redaktion/Verein, Veranstaltung, Ort, Zeitraum und

Logo — nicht Telefon, E-Mail, Nachweis oder interne Bemerkungen (Spec 15.2,

per Textextraktion geprüft). Layout ist ein Platzhalter bis zur

Marketing-Vorlage (O7).

„Badge erstellt" und der Mail-Editor öffnet mit der Badge-Vorlage. Erneutes

Erzeugen überschreibt dieselbe Datei (idempotent, für Tippfehler-Korrekturen).

Sende-Knopf gesperrt und ein direkter POST wird abgewiesen.

Behoben

Hinweis

[0.5.0] — 2026-07-09

Hinzugefügt

Liste mit Statuszählern als Schnellfilter, Filter nach Funktion,

Veranstaltung, Zeitraum und Freitextsuche, sortierbare Spalten.

Nachweis-Download, Statuswechsel mit Kommentar, interne Bemerkung, Haken für

„Wettkampfleitung informiert" und „Badge ausgegeben", vollständiger

Statusverlauf und die Mail-Historie des Antrags.

Rückfrage, Genehmigung, Ablehnung und Widerruf erscheint ein Hinweis, dass

eine Nachricht vorgesehen ist; der vorbefüllte Mail-Editor folgt in Schritt 6.

decided_at wird bei Genehmigung/Ablehnung gesetzt, der Widerruf setzt

zusätzlich revoked.

ausschließlich aus dem Zufallsnamen der Datenbank, Auslieferung immer als

Anhang mit nosniff.

die keinen Systemzugang hat. Mit BOM, damit Excel die Umlaute richtig liest.

den Nachweis vor Ort geprüft hat (im Statusverlauf dokumentiert). Es werden

keine Mails verschickt, und ein bereits vergangener Zeitraum ist zulässig —

anders als im öffentlichen Formular, wo er fast immer ein Tippfehler ist.

als gebundene Parameter in die Abfrage.

[0.4.0] — 2026-07-09

Hinzugefügt

Eingangsbestätigung an die antragstellende Person und interne Information an

presse@btv-turnen.de mit Deeplink zum Antrag im Board. Ein Fehler beim

Versand nimmt den bereits gespeicherten Antrag nicht zurück.

graph (Microsoft Graph sendMail, App-only) und smtp (mail()-Fallback).

base64) — das Referenzprojekt kann das nicht. Nötig für die Badge-Mail.

Ist ein verlangter Anhang unlesbar oder größer als 3 MB, wird die Mail

nicht verschickt und nicht protokolliert, sondern ein Fehler geworfen:

Eine Badge-Mail ohne Badge wäre schlimmer als keine Mail.

Rückfrage, Genehmigung, Ablehnung, Widerruf, Badge) mit Platzhaltern.

ENTWURF — die Spezifikation mit den wörtlichen Texten liegt nicht vor; der

Fachbereich gibt sie frei, Anpassung später ohne Codeänderung über die

Einstellungen.

personenbezogene Daten). Zeigt Art, Empfänger, Betreff, Text, Anhang und ob

tatsächlich versendet wurde.

sind — sonst schlüge jeder Versand still fehl (O11).

Behoben

gemeldet (der Zielserver läuft auf 8.4, lokal 8.5).

[0.3.0] — 2026-07-09

Hinzugefügt

aus Konzept Abschn. 4, serverseitige Validierung mit Re-Render und erhaltenen

Eingaben, Bestätigungsseite /danke.

ausweis.pdf getarnte PHP-Datei und ein als .jpg getarntes GIF werden beide

abgewiesen. Ablage unter Zufallsnamen in storage/uploads (Rechte 0640),

der Originalname existiert nur in der Datenbank.

dieselbe Seite wie ein Mensch), Rate-Limit von 5 Anträgen je IP und Stunde

antwortet mit 429.

URL — sonst ließen sich fremde Nummern durch Hochzählen bestätigen.

erscheint direkt ein Freitextfeld.

Behoben

$_SESSION['csrf'] leer, und hash_equals('', '') ergibt true. Fehlt das

Token in der Session, wird jetzt abgelehnt. (Derselbe Fehler steckt im

Referenzprojekt btv-Digitalisierungsanforderungen.)

und Zustimmungs-Zeitstempel zwei Stunden falsch in der Datenbank, und das

Rate-Limit verglich gegen ein verschobenes Zeitfenster — der Spam-Schutz wäre

im Betrieb wirkungslos gewesen. Die App setzt die Zeitzone jetzt selbst

(timezone in der Konfiguration), Zeitfenster berechnet PHP statt MySQL, und

der Self-Check meldet Abweichungen.

PHP das schließende Anführungszeichen in den Variablennamen (Bytes ≥ 0x80 sind

dort erlaubt). Variable geklammert.

hätte ein Fehler dabei die Bremse stillschweigend übersprungen.

[0.2.0] — 2026-07-09

Hinzugefügt

request_counter, attachments, status_history, mails, settings,

rate_limit, users. Idempotent (CREATE TABLE IF NOT EXISTS).

Vergabe über ein einzelnes atomares INSERT … ON DUPLICATE KEY UPDATE

auf request_counter statt SELECT MAX(seq) … FOR UPDATE — damit keine

Deadlocks bei gleichzeitigen Anträgen. Mit 8 parallelen Prozessen à 25

Nummern geprüft: 200 eindeutige, lückenlose Nummern.

(Mail-Historie je Antrag) und attachment_name (Badge-Mail). Beim Löschen

eines Antrags bleibt das Mail-Protokoll erhalten (ON DELETE SET NULL),

Nachweise und Statusverlauf verschwinden mit (ON DELETE CASCADE).

Statusverlauf. sql/cleanup_testdata.sql zum Zurücksetzen.

Geändert

anmeldefähiges Konto existiert (aktiv, Passwort gesetzt) — die bloße

Existenz der leeren users-Tabelle genügt nicht mehr.

[0.1.0] — 2026-07-09

Hinzugefügt

Front-Controller mit Routentabelle, helpers.php (config, CSRF, Session,

Flash, Changelog-Rendering), PDO-Singleton, settings.php, Zugriffs-Log.

(Medienrichtlinien, SAFE-SPORT-Merkblatt, Datenschutz) werden als Text ohne

Link dargestellt, solange keine URL hinterlegt ist.

app_env=dev gebunden und serverseitig geprüft. Echte Benutzerkonten folgen

in Umsetzungsschritt 9 — vorher kein Livegang.

Lage der Speicherverzeichnisse außerhalb des Webroots, Mail-Modus, offene

Platzhalter-Links. Meldet app_env=prod zusammen mit aktivem Demo-Login als

Fehler.

Harabara und Century Gothic aktiviert.

httponly/secure/SameSite=Lax.

Bekannte Lücken

(Schritt 4), kein Board (Schritt 5).