Self-Hosting, Branding, i18n, Barrierefreiheit und Werkzeuge #2

Merged
friloo merged 8 commits from feature/self-hosted-assets-branding into main 2026-09-23 06:54:40 +00:00
Owner

Umsetzung der Punkte aus der Nachbesprechung zum Redesign – in acht nachvollziehbaren Commits.

1. Keine externen CDNs mehr

Inter, Font Awesome, Chart.js, qrcodejs und TinyMCE liegen unter assets/vendor/ (2,6 MB) und kommen vom eigenen Server.

  • Datenschutz: bisher ging bei jedem Seitenaufruf die IP der Nutzer an Google Fonts, cdnjs, jsDelivr und Tiny Cloud
  • Funktion: UniFi-Installationen stehen oft in Netzen ohne Weg nach außen – dort fehlten Schrift, Icons, Diagramme und Editor
  • neues includes/Ui.php bindet alles ein und hängt einen Versionsstempel an (?v=filemtime) → kein altes CSS mehr aus dem Browser-Cache nach einem Update
  • TinyMCE-API-Key entfällt, der Editor läuft lokal (GPL-Variante) inklusive deutscher Oberfläche

2. Branding über die Oberfläche

Neuer Tab Einstellungen → Design: Akzentfarbe hell/dunkel, Markenverlauf, Eckenradius mit Live-Vorschau. Abgeleitete Töne berechnet color-mix aus der Grundfarbe – eine Farbe genügt. Gilt systemweit inklusive Login, Installer, Updater und E-Mails.

3. Bild-Upload statt nur URLs

Logo, Favicon, Login-Logo und Login-Hintergrund lassen sich hochladen (includes/Upload.php): Endungs-Whitelist, 3-MB-Grenze, getimagesize-Prüfung, SVG-Sanitizing (Skripte, Event-Handler, javascript:-Verweise), zufällige Dateinamen in uploads/ mit .htaccess-Sperre für PHP.

4. Englische Übersetzungen vervollständigt

API-Schlüssel, Integration & Wartung, Import, Backup, Reporting, 2FA und Audit-Log waren fest auf Deutsch verdrahtet – jetzt rund 200 neue Schlüssel, beide Sprachdateien deckungsgleich, inklusive der JavaScript-Dialoge.

5. Barrierefreiheit und Mobile

Kontrast von 3,1:1 auf 4,9:1 (hell) bzw. 6,4:1 (dunkel), Sprungmarke, aria-label für Icon-Buttons, aria-current, 194 dekorative Icons mit aria-hidden, prefers-reduced-motion. Tabellen werden unter 720 px zu Karten statt horizontal zu scrollen.

6. E-Mails und Druck im neuen Look

Marken-HTML-Rahmen für Mails (Tabellen + Inline-Styles), neuer Platzhalter {VOUCHER_CARD}. Druckvorlage als Ticket mit {QR_CODE} – der QR-Code entsteht lokal im Browser, auch bei Bulk-Erstellung.

7. Sicherheit

.htaccess mit CSP und Sicherheits-Headern; includes/, tools/, tests/, updater/storage und uploads/ mit eigenen Regeln. Docker setzt AllowOverride All und legt ein Volume für uploads/ an. Nginx-Entsprechung steht in der README.

8. Werkzeuge und Tests

tools/demo/build.py baut eine Demo-Instanz ohne Datenbank (Stubs mit festen Beispieldaten), tools/screenshots.py erzeugt daraus alle Bilder in docs/screenshots. Neue Tests für Upload-Sanitizing und die Ui-Helfer (29 Tests, 55 Assertions), PHPStan deckt die neuen Dateien mit ab, CI prüft zusätzlich die Sprachdateien auf Deckungsgleichheit und unbekannte Schlüssel.

Nebenbei behoben

  • global.js überschrieb die Theme-Entscheidung aus dem <head> (kurzes Umspringen, erzwungene Themes wirkungslos)
  • Dark Mode folgt jetzt prefers-color-scheme, solange nichts manuell gewählt wurde
  • Einstellungs-Tabs sind per ?tab=… direkt verlinkbar – auch ohne JavaScript
  • Upload rief __() direkt auf und wäre ohne geladene I18n mit einem Fatal Error abgebrochen (vom neuen Test gefunden)

Geprüft

PHPUnit 29/29, PHPStan ohne Befund, php -l über alle Dateien, jede Seite einmal gerendert (Smoke-Test über die Demo-Instanz), Upload inklusive SVG-Sanitizing manuell durchgespielt. Alle 18 Screenshots neu erzeugt, README auf 2.6.0.

🤖 Generated with Claude Code

Umsetzung der Punkte aus der Nachbesprechung zum Redesign – in acht nachvollziehbaren Commits. ## 1. Keine externen CDNs mehr Inter, Font Awesome, Chart.js, qrcodejs und TinyMCE liegen unter `assets/vendor/` (2,6 MB) und kommen vom eigenen Server. - **Datenschutz:** bisher ging bei jedem Seitenaufruf die IP der Nutzer an Google Fonts, cdnjs, jsDelivr und Tiny Cloud - **Funktion:** UniFi-Installationen stehen oft in Netzen ohne Weg nach außen – dort fehlten Schrift, Icons, Diagramme und Editor - neues `includes/Ui.php` bindet alles ein und hängt einen Versionsstempel an (`?v=filemtime`) → kein altes CSS mehr aus dem Browser-Cache nach einem Update - TinyMCE-API-Key entfällt, der Editor läuft lokal (GPL-Variante) inklusive deutscher Oberfläche ## 2. Branding über die Oberfläche Neuer Tab **Einstellungen → Design**: Akzentfarbe hell/dunkel, Markenverlauf, Eckenradius mit Live-Vorschau. Abgeleitete Töne berechnet `color-mix` aus der Grundfarbe – eine Farbe genügt. Gilt systemweit inklusive Login, Installer, Updater und E-Mails. ## 3. Bild-Upload statt nur URLs Logo, Favicon, Login-Logo und Login-Hintergrund lassen sich hochladen (`includes/Upload.php`): Endungs-Whitelist, 3-MB-Grenze, `getimagesize`-Prüfung, SVG-Sanitizing (Skripte, Event-Handler, `javascript:`-Verweise), zufällige Dateinamen in `uploads/` mit `.htaccess`-Sperre für PHP. ## 4. Englische Übersetzungen vervollständigt API-Schlüssel, Integration & Wartung, Import, Backup, Reporting, 2FA und Audit-Log waren fest auf Deutsch verdrahtet – jetzt rund 200 neue Schlüssel, beide Sprachdateien deckungsgleich, inklusive der JavaScript-Dialoge. ## 5. Barrierefreiheit und Mobile Kontrast von 3,1:1 auf 4,9:1 (hell) bzw. 6,4:1 (dunkel), Sprungmarke, `aria-label` für Icon-Buttons, `aria-current`, 194 dekorative Icons mit `aria-hidden`, `prefers-reduced-motion`. Tabellen werden unter 720 px zu Karten statt horizontal zu scrollen. ## 6. E-Mails und Druck im neuen Look Marken-HTML-Rahmen für Mails (Tabellen + Inline-Styles), neuer Platzhalter `{VOUCHER_CARD}`. Druckvorlage als Ticket mit `{QR_CODE}` – der QR-Code entsteht lokal im Browser, auch bei Bulk-Erstellung. ## 7. Sicherheit `.htaccess` mit CSP und Sicherheits-Headern; `includes/`, `tools/`, `tests/`, `updater/storage` und `uploads/` mit eigenen Regeln. Docker setzt `AllowOverride All` und legt ein Volume für `uploads/` an. Nginx-Entsprechung steht in der README. ## 8. Werkzeuge und Tests `tools/demo/build.py` baut eine Demo-Instanz ohne Datenbank (Stubs mit festen Beispieldaten), `tools/screenshots.py` erzeugt daraus alle Bilder in `docs/screenshots`. Neue Tests für Upload-Sanitizing und die Ui-Helfer (29 Tests, 55 Assertions), PHPStan deckt die neuen Dateien mit ab, CI prüft zusätzlich die Sprachdateien auf Deckungsgleichheit und unbekannte Schlüssel. ## Nebenbei behoben - `global.js` überschrieb die Theme-Entscheidung aus dem `<head>` (kurzes Umspringen, erzwungene Themes wirkungslos) - Dark Mode folgt jetzt `prefers-color-scheme`, solange nichts manuell gewählt wurde - Einstellungs-Tabs sind per `?tab=…` direkt verlinkbar – auch ohne JavaScript - `Upload` rief `__()` direkt auf und wäre ohne geladene I18n mit einem Fatal Error abgebrochen (vom neuen Test gefunden) ## Geprüft PHPUnit 29/29, PHPStan ohne Befund, `php -l` über alle Dateien, jede Seite einmal gerendert (Smoke-Test über die Demo-Instanz), Upload inklusive SVG-Sanitizing manuell durchgespielt. Alle 18 Screenshots neu erzeugt, README auf 2.6.0. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
friloo added 8 commits 2026-09-23 06:54:32 +00:00
Inter, Font Awesome, Chart.js, qrcodejs und TinyMCE liegen jetzt unter
assets/vendor/ und werden vom eigenen Server ausgeliefert.

Warum:
- Datenschutz: bisher ging bei jedem Seitenaufruf die IP der Nutzer an
  Google Fonts, cdnjs, jsDelivr und Tiny Cloud
- Funktion: UniFi-Installationen stehen oft in abgeschotteten Netzen –
  dort fehlten bisher Schrift, Icons, Diagramme und Editor

Neu: includes/Ui.php
- Ui::head()/Ui::script() binden die Assets ein und hängen einen
  Versionsstempel an (?v=filemtime), damit Browser nach einem Update
  nicht das alte CSS aus dem Cache nehmen
- Ui::themeScript() setzt das Theme aus der gespeicherten Auswahl oder
  – wenn keine vorliegt – aus prefers-color-scheme; global.js folgt
  Systemwechseln live, solange nichts manuell gewählt wurde
- <meta name="color-scheme"> ergänzt, damit Formularelemente passen

Der TinyMCE-API-Key entfällt: der Editor läuft immer lokal (GPL-Variante),
inklusive deutscher Oberfläche, wenn die App auf Deutsch steht.

Neu: tools/demo/build.py – baut aus dem Projekt eine Demo-Instanz ohne
Datenbank (Stubs für Database/Auth, feste Beispieldaten). Damit lassen
sich Screenshots reproduzierbar erzeugen und alle Seiten einmal rendern
(Smoke-Test), ohne eine MySQL-Instanz aufzusetzen.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Design-Tab (Administration → Einstellungen → Design):
- Akzentfarbe für Hell- und Dark-Mode, Markenverlauf und Eckenradius
- abgeleitete Töne (Hover, weiche Flächen, Fokusring) werden per
  color-mix aus der Grundfarbe berechnet – eine Farbe genügt
- Live-Vorschau mit Button, Badge, Chip, Logo-Kachel und Link
- Ausgabe als schlanker :root-Override über Ui::brandingStyle(), greift
  auf allen Seiten inklusive Login und Installer

Uploads (includes/Upload.php):
- Logo, Favicon, Login-Logo und Login-Hintergrund lassen sich jetzt
  hochladen; das URL-Feld bleibt als Alternative bestehen
- Whitelist nach Endung, 3-MB-Grenze, getimagesize-Prüfung für Raster,
  SVGs werden von Skripten, Event-Handlern und externen Verweisen befreit
- Zufällige Dateinamen in uploads/, dort sperrt eine .htaccess die
  Ausführung von PHP; beim Ersetzen wird die alte Datei gelöscht

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
API-Schlüssel, Integration & Wartung, Voucher-Import, Backup & Restore,
Reporting, Sicherheit (2FA) und Audit-Log waren fest auf Deutsch
verdrahtet, obwohl in der Kopfzeile ein DE/EN-Umschalter sitzt. Diese
Seiten laufen jetzt komplett über lang/de.php bzw. lang/en.php.

- rund 200 neue Sprachschlüssel, beide Dateien deckungsgleich
- Aktionsnamen im Audit-Log werden übersetzt statt fest ausgegeben
- Bestätigungsdialoge und Statusmeldungen in JavaScript ebenfalls
- admin/security.php initialisiert jetzt I18n und setzt <html lang>
  passend zur Auswahl

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
- Kontrast: gedämpfter Text war mit 3,1:1 unter WCAG AA, jetzt 4,9:1
  (hell) bzw. 6,4:1 (dunkel)
- Sprungmarke „Zum Inhalt springen" in Admin-Shell und Voucher-Seite
- aria-label für alle reinen Icon-Schaltflächen (Theme, Menü, Abmelden,
  Zeilenaktionen), aria-current auf dem aktiven Navigationspunkt,
  role="group" für den Sprachumschalter
- 194 dekorative Icons mit aria-hidden versehen, damit Screenreader sie
  nicht vorlesen
- Toast-Container als aria-live-Bereich ausgezeichnet
- prefers-reduced-motion schaltet Animationen und Übergänge ab

Tabellen (Benutzer, Vouchers, Profile, Audit-Log, Dashboard, API-Keys)
werden unter 720 px zu Karten: die Spaltenüberschrift steht per
data-label vor dem Wert, statt horizontal zu scrollen.

Datums- und Zeitformat in der Voucher-Liste folgen jetzt der gewählten
Sprache statt fest de-DE.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
E-Mail:
- Nachrichten werden in ein markentreues HTML-Gerüst gelegt (Tabellen +
  Inline-Styles, damit Outlook & Co. es zuverlässig darstellen); die
  Kopfleiste nutzt den eingestellten Markenverlauf
- neuer Platzhalter {VOUCHER_CARD} rendert den Code als hervorgehobene
  Karte; bestehende Vorlagen mit {VOUCHER_CODE} funktionieren unverändert
  weiter, sie werden lediglich eingerahmt
- Zeilenumbrüche der Vorlage werden vor dem Einsetzen der Platzhalter
  umgewandelt, Markup aus dem WYSIWYG-Editor bleibt unangetastet

Druck:
- neue Standardvorlage als Ticket mit QR-Code, Code, Gültigkeit und
  Anleitung; zentral in Ui::defaultPrintTemplate()
- neuer Platzhalter {QR_CODE}; der QR-Code wird lokal im Browser erzeugt
  (qrcodejs aus assets/vendor), auch bei Bulk-Erstellung für jede Karte
- die Druckvorlage ist am Bildschirm ausgeblendet und erscheint nur im
  Ausdruck

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Sicherheit:
- .htaccess im Projektstamm mit X-Content-Type-Options, X-Frame-Options,
  Referrer-Policy, Permissions-Policy und einer Content-Security-Policy;
  da alle Assets lokal liegen, erlaubt sie nur noch die eigene Herkunft
  (Ausnahme: hCaptcha, falls aktiviert)
- includes/, tools/, tests/, updater/storage und uploads/ schützen sich
  über eigene .htaccess-Dateien – auch bei Installation im Unterordner
- Docker: AllowOverride All, damit diese Regeln überhaupt greifen, und
  ein Volume für uploads/, damit Logos ein Image-Update überstehen

Werkzeuge:
- tools/screenshots.py erzeugt alle Bilder in docs/screenshots aus der
  Demo-Instanz; tools/README.md beschreibt beides
- Einstellungs-Tabs sind per ?tab=… direkt verlinkbar (serverseitig, also
  auch ohne JavaScript)

Dokumentation: Readme um Markenfarben, Bild-Upload, lokale Assets,
Sicherheits-Header (inkl. Nginx-Entsprechung) und einen Abschnitt
"Entwicklung" ergänzt; Screenshots neu erzeugt, Version 2.6.0.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
- tests/UploadTest.php prüft den SVG-Filter (Skripte, Event-Handler,
  javascript:-Verweise) und Upload::isLocal gegen Pfad-Tricks
- tests/UiTest.php prüft Versionsstempel, Media-Pfade und die
  Branding-Overrides inklusive Abweisung ungültiger Farbwerte
- PHPStan analysiert jetzt auch includes/Ui.php und includes/Upload.php
- CI vergleicht die Sprachdateien (gleiche Schlüsselmenge) und prüft,
  dass jeder im Code verwendete Schlüssel existiert

Dabei aufgefallen und behoben: Upload.php rief __() direkt auf und wäre
außerhalb einer Seite mit geladener I18n mit einem Fatal Error
abgebrochen; jetzt gibt es einen Fallback auf die deutsche Meldung.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Theme-Auswahl aus dem Seitenkopf nicht mehr überschreiben
Some checks are pending
CI / PHP Lint (push) Waiting to run
CI / PHP Lint-1 (push) Waiting to run
CI / Unit Tests & Static Analysis (push) Waiting to run
CI / PHP Lint (pull_request) Waiting to run
CI / PHP Lint-1 (pull_request) Waiting to run
CI / Unit Tests & Static Analysis (pull_request) Waiting to run
2fe7e9522d
global.js hat beim Laden erneut ein Theme gesetzt und dabei die
Entscheidung des Inline-Schnipsels im <head> verworfen – sichtbar als
kurzes Umspringen und daran, dass erzwungene Themes ignoriert wurden.
Jetzt gilt: ausdrückliche Auswahl > bereits gesetztes Attribut > System.

Updater-Seite übernimmt ebenfalls die Markenfarben, Screenshots neu.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
friloo merged commit 7dc60c5bb0 into main 2026-09-23 06:54:40 +00:00
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference: friloo/Unifi-Voucher-Tool#2
No description provided.