17 Commits

Author SHA1 Message Date
Adminator ff889865e1 Lizenz-Gate: Schueler ohne Lizenz gesperrt + Lehrer-Cockpit-Warnung
Teil 1 — Schueler-Lizenz-Gate (gilt fuer alle Klassen-Schueler ohne gueltige
Lizenz = zugewiesen, nicht storniert, nicht abgelaufen):
- Server-Gate zentral im Front-Controller (index.php + php/lib/license_gate.php):
  eingeloggte Klassen-Schueler ohne Lizenz sehen bei JEDER Sim-Route statt der
  Simulation eine Hinweis-Seite "Deine Lehrperson muss dir noch eine gueltige
  Lizenz zuweisen". Schnell-Ausstieg fuer Nicht-Schueler/Nicht-Sim (kein Cookie
  -> keine DB-Abfrage). Autodidakt ohne Klasse: nicht lizenzpflichtig.
- modules.php?student=1 liefert 'licensed'; Schueler-Cockpit (schueler.html)
  zeigt oben die Meldung und sperrt die Start-Buttons.

Teil 2 — Lehrer-Cockpit:
- Rote Warnleiste ganz oben, wenn Schueler:innen ohne gueltige Lizenz in der
  Klasse sind (+ Link "Jetzt zuweisen"). licenses?class_id=X liefert dazu die
  Liste 'unlicensed'.
- Lizenzen-Lasche: neue Sektion "Ohne gueltige Lizenz" mit Einzel- und
  Sammel-Zuweisung (auto_assign) — sonst fuehrt die Warnung ins Leere.

Kunden-Vereinfachung (Feedback): keine Auto-Erkennung/Dedup mehr (Ansprech-
partner koennen wechseln). Kunde = Freitext "Name/Verlag/Schule" + Mailadresse,
je Bestellung gespeichert. Editierbares Kundennummer-Feld + Autocomplete entfernt.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-26 16:15:16 +02:00
Adminator 50c72fa8d8 Lizenz-Vertrieb Feinschliff: Zugabe +10%, sichere Datei-Einloesung, Mail-Config
Umsetzung des Thomas-Feedbacks:
- Reihenfolge: freie Lizenzen beim Lehrer jetzt ORDER BY id (= Reihenfolge der
  reinkopierten Liste); stornierte ausgeblendet.
- Gratis-Zugabe: Checkbox (Default an) "+10 % aufgerundet" -> geliefert =
  ceil(bestellt*1.1) (14 -> 16). Batch speichert qty_ordered (verrechnet) und
  qty (geliefert); Live-Vorschau im Formular.
- Einloeselink/QR ENTFERNT (Sicherheitsrisiko: falscher eingeloggter Account).
  einloesen.html + pages/einloesen.php geloescht, Sammellink aus UI/Mail raus.
- CSV: Gueltig-bis + Metadaten nur in der KOPFZEILE, danach reine Code-Zeilen
  (per Copy-Paste einloesbar). Dateiname: <ExtBestellnr|Datum>_geograsim_<intNr>.
- Interne Bestellnummer = batch.id (laufend, nicht editierbar, storno-stabil).
- Lehrer-Einloesen: sichere .txt/.csv-Ablage (mehrstufig: Endung+MIME, Groessen-
  limit, nur Text lesen, Whitelist-Regex extrahiert nur Code-Tokens, Server
  validiert) — kein Upload, keine Ausfuehrung/Rendering.
- Mailsystem: zentrale Config in neuer app_settings (Provider server|aws,
  Absender-Name/-Adresse, oeffentliche URL), pflegbar in Admin + Test-Versand.
  Mailer liest Absender aus Config (SMTP-Zugang bleibt in .env). Mailtext nutzt
  kanonische geograsim.at (nicht v3/localhost).

Migrationen: -2 (qty_ordered), -3 (app_settings). Beide additiv, auf v3 angewandt.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-26 11:15:53 +02:00
Adminator 96a73e22b2 Lizenz-Vertrieb: Chargen-Vergabe, Storno, Verlaengerung, Mail, Einloeselink
Erweitert das Lizenzmodul um eine Vertriebs-/Vergabe-Schicht ueber dem
bestehenden Einloese-Flow (non-breaking, alle Alt-Codes + generate/redeem
/assign unveraendert).

Schema (Migration 2026-08-26-lizenz-vertrieb.sql, additiv):
- license_customers (Besteller: Kundennummer/Email/Name)
- license_batches (Bestellung/Charge: externe Bestellnummer, Typ, Menge,
  Gueltigkeit, Sammel-Einloesetoken, Storno)
- licenses += batch_id, issued_at, valid_from/until (pro Code -> Verlaengerung),
  canceled_at/reason; code auf VARCHAR(32) (laengere Codes)
- license_mails (Mail-Archiv, unabh. vom IMAP), license_redeem_attempts
  (Brute-Force-Schutz), license_events (Audit-Log)

Backend (php/api/licenses.php):
- issue_batch: N frische 100-Bit-Codes in Transaktion, sofort vergeben,
  an Kunde/Bestellung/Gueltigkeit gebunden; sticky Default Gueltig-bis
- Uebersicht (view=batches mit Aggregat ausgegeben/storniert/netto/eingeloest
  /abgelaufen), batch_detail, Kunden-Autocomplete, sales_default
- cancel_batch/cancel_license, extend_batch/extend_license
- send_batch_mail (Server-Treiber via Mailer, archiviert, Anhang bei >40)
- redeem_charge (Sammel-Einloeselink)
- Brute-Force-Bremse + Audit + Storno-Ablehnung im redeem/redeem_batch

UI (admin-licenses.html): Ausgabe-Formular (Bestaetigung bei Grossmengen),
Ergebnis mit Copy/Download(txt+csv)/Mail-Composer/Sammellink, Bestellungs-
Uebersicht mit Filtern + Detail-Modal (Storno/Verlaengern/Verlauf/Mails).

Einloese-Landingpage (einloesen.html + pages/einloesen.php): ?charge=Token
(alle auf einmal) und ?code=CODE, mit Lehrer-Login-Gate.

Lib: Database Transaktionen; Mailer sendWithAttachments + renderPlainBody.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-26 01:39:14 +02:00
Adminator 182a87978e Auth-Umschalter: Session.php verzweigt auf AUTH_BACKEND (jwt = JWT-Access-Cookie + widerrufbare Redis-Session)
- teacher/admin Login/Read/Logout branchen auf AUTH_BACKEND; session-Pfad unveraendert (Live-Default, prod-sicher).
- jwt: loginTeacher/loginAdmin legen Redis-Session (SessionStore) an + setzen kurzlebiges JWT-Cookie (ggs_t/ggs_a) + langes Sid-Cookie (ggs_ts/ggs_as).
- Read: Fast-Path via signiertem JWT (zustandslos), Refresh-Path via Sid->Redis + frisches JWT; Widerruf = Redis DEL bei Logout.
- Teacher/Admin getrennte Cookies (koexistieren), typ-Check gegen Rollenverwechslung. Lazy-require von Jwt/SessionStore (jeder Einstiegspfad).
- Schueler unveraendert (UUID-Cookie + DB, schon pod-unabhaengig).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-20 00:15:22 +02:00
Adminator 37f6238d05 Auth Phase 2: Session-Zugriffe kanalisieren (verhaltensneutral)
Vorbereitung für den JWT+Redis-Umschalter: alle verstreuten
$_SESSION['teacher_id'|admin_id']-Zugriffe laufen jetzt durch Session::-Helper,
damit der Backend-Tausch später EINE Datei (Session.php) ist statt 18 Stellen.

- Session.php: neue Admin-Helper adminId()/loginAdmin()/logoutAdmin()/
  requireAdmin() (mirror der Teacher-Helper). teacherId()/adminId() starten
  die Session jetzt lazy (Aufrufer müssen Session::start() nicht mehr).
- 18 Fundstellen kanalisiert (admin.php, admin-accounts, admin-modules,
  licenses, levels, waypoints, foto-upload, admin_gate, save_map).
- Country.php + app.php cockpit_href: über Session:: mit class_exists-Fallback
  (Bootstrap/Lib-Kontext, Session evtl. nicht geladen).

Verhaltensneutral: 14/14 Lint OK, Helper-Round-Trip 6/6 PASS, Live-Smoke
(admin status/modules/accounts/licenses) weist unauth. Requests wie bisher
mit 401/403 ab. AUTH_BACKEND bleibt 'session' — Live unverändert.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-18 16:33:14 +02:00
Adminator 5d94572569 Auth-Fundament: JWT + Redis (Phase 1, non-breaking)
Vorbereitung für pod-unabhängige Sessions (k8s-Ziel). Ändert am Live-System
NICHTS: AUTH_BACKEND='session' bleibt Default, die neuen Libs sind noch
nirgends verdrahtet.

- composer eingeführt: firebase/php-jwt ^6.11 + predis/predis ^2.4 (pure PHP,
  keine Redis-Extension nötig → läuft auf XAMPP wie im Prod-Image). vendor/
  committed (kein Composer auf dem Server).
- Jwt.php: HS256-Access-Token (issue/verify), decode() IMMER mit explizitem
  Algorithmus (mitigiert Algorithmus-Confusion). 8/8 Tests grün.
- SessionStore.php: predis-gestützter Session/Refresh-Store (sess:<sid>,
  Sliding-TTL, revoke=DEL) — die widerrufbare Session hinter dem kurzen JWT.
- app.php: vendor/autoload (guarded), Konstanten AUTH_BACKEND (Default session),
  REDIS_URL, JWT_TTL, JWT_REFRESH_TTL. JWT_SECRET bewusst ohne Default.
- docker-compose.dev.yml: Redis (+ redis-commander UI) für lokale Dev-Parität.
- .env.example um Auth-Block ergänzt.

Nächste Phasen: $_SESSION-Zugriffe (293) durch Session:: kanalisieren →
Backend-Umschalter in Session.php → doppelgleisiger Cutover → Reporting auf
student_id-FK ziehen.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-18 16:21:27 +02:00
Adminator b9bcbf9198 Backend-Editoren admin-geschuetzt (admin_gate) + Admin-Hub auf Admin-Seite; save_map admin-oder-lehrer; foto-upload admin-bypass (PIN bleibt)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-18 11:54:08 +02:00
Adminator 8b1088a070 Balancing: sonnensystem (Erst-Treffer-Bewertung, Loesung nicht aufdecken, Level-Badge) + energiemanager (echter Blackout an Netzfrequenz, Schwierigkeitsrampe)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-15 21:59:32 +02:00
Adminator fb44ec7afe student_id-FK-Migration Stufe 1+2: Name-Joins -> student_sessions.student_id
- student_sessions: neue Spalte student_id (+Index), backfilled aus (class_id,display_name)
- Session::createStudent setzt student_id (per Lookup, falls nicht uebergeben)
- alle Joins students<->student_sessions von display_name auf student_id umgestellt:
  Benchmark.php, live.php (5x), results.php (4x + runs), modules.php, profile.php, progress.php (answers+runs)
- verlustfrei verifiziert (alt-Join == neu-Join, class 99/102)

Robuster gegen Umbenennung/Doppelnamen, indexierbare Joins.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-15 19:16:41 +02:00
Adminator 8047778a5b Tourismusregion: Komplett-Integration (zweite neue Sim, beta)
Wellenbasierte Routen-Sim (DefensegeograSimMac) nach dem Tourismustal-
Playbook integriert: Sim nach App/sims/tourismusregion (game.html,
Sprites 47MB->9.5MB, ohne Eigen-Backend/Editor/Dashboard/Keys),
Plattform-Hooks (GGS_LIVE_STATE mit Welle/Budget/Umwelt/Sterne/Lenkung,
Submit bei Partie-Ende mit fertigem 0-100-Score, GGS_TUTORIAL, Home-
Button, Musik leise). Wrapper + Modul-Detailseite. SQL: module_info
(beta, Karte), Glossar-Kernbegriff Besucherlenkung (+Bild, Leichte
Sprache) + 12 Zuordnungen, Lehrplan-Mapping auf tourismus-
raumentwicklung (primaer) + verkehrserziehung/systemisches-denken.
LEHRPLAN.md mit Schwerpunkt Besucherlenkung & Mobilitaet. Lehrer-
Backend: Benchmark (Sim-Score direkt), Live-Cockpit-Spalten,
needsHelp, Highlights.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-12 01:14:12 +02:00
Adminator 0a2ebf46b4 Tourismustal: Komplett-Integration (Sim + Wrapper + SQL + Lehrer-Backend)
Sim nach App/sims/tourismustal umgezogen (Source of Truth), game.html mit
Injection-Marker. Neu in der Sim: 5 Sommergebaeude (Bikepark mit Flowtrails,
Hochseilgarten, Fahrradverleih als Verstaerker, Aussichtsplattform,
Erlebnisspielplatz), Rettungsstation mit Notarzthubschrauber, Ausbau-
Bauanimation, Event-Pacing, Schulden-Notbremse (Bank-Zwangsverkauf),
Berater-Tipps (Ruecklagen), Sommer-Trend-Mechanik (Serfaus-Effekt) und
Wissens-Bausteine mit echten DACH-Tourismusdaten (QUELLEN.md).

Plattform: PHP-Wrapper mit base-Tag + Session-Mode, Modul-Detailseite,
module_info (beta) + Glossar (14 Begriffe inkl. Leichter Sprache + Quellen)
+ Lehrplan-Anker AT/DE/CH an neuer Kompetenz tourismus-raumentwicklung.
Lehrer-Backend: Benchmark-Formel, Live-Cockpit-Spalten, needsHelp-Heuristik,
Modul-Highlights. Strategie-Testharness (tests/strategien.mjs) belegt:
Panzer-Rush wird liquidiert, Ruecklagen zahlen sich aus.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-11 02:45:08 +02:00
Adminator 1e51ef7def Nachtrag: alle bisher untracked Ordner + hängende Änderungen mit-committen
- Konzept/, didaktik_geografie/, didaktik_simulation/, v2-modules/, v2-platform/
- 12 code-workspace-Files
- STATUS-*.md
- viele M/D/R-Änderungen an bereits getrackten Files
- .gitignore verstärkt: **/.humaninput/, **/secret_keys.txt

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-07-08 02:27:02 +02:00
Adminator 9a61f55cb1 Atlas: Infrastruktur + Team-Konventionen + Sprachregel Lernarbeit
- Design-System (assets/css/design-system.css) mit 21 Komponenten,
  iPad-Responsive-Breakpoints, Touch-Ziele 36px, Music-Player,
  Glossar-Tooltips
- Templates (sims/template.html + student/teacher-Dashboard)
- Docs: module-interface.md (inkl. 4a Sprachregel, 4b Leichte Sprache,
  4c iPad), content-architecture.md, crash-recovery.md, music-registry.md
- Admin-Infrastruktur: admin-modules.html + api/admin-modules.php
  (Titel, Emoji, Bild, Status, Dauer, Alter pro Modul)
- Inbox-System: _inbox/README.md + _status.md fuer Atlas + Briefings
  an Klima, Glossar, Lehrplan, Fluss
- Zentrale SFX-Pipeline (scripts/generate-sounds.py)
- DALL-E-Bilder: 8 Badges + 5 Glossar-Repraesentationsbilder (Querformat)
- Logo + Inter-Font lokal
- PHP-APIs: admin, glossar, levels, licenses, progress, waypoints,
  assignments, profile, tickets
- Spielsprache entfernt (admin-modules, admin-levels, schueler)
- Landing-Page-Bearbeitungen (Boote sichtbarer, Button-Hintergrund)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-19 11:51:14 +02:00
Adminator 7deec2a26d Lehrplan: Country-Helper + Landeinstellung live
- App/php/lib/Country.php: Kaskade session → classes → teachers →
  cookie → 'AT', Helper current()/flag()/label()/allCountries()
- App/php/api/country.php: GET + POST /api/country
- country-Spalten in teachers, classes, student_sessions (idempotent)
- Native-<select>-Picker im Header aller öffentlichen Seiten
  (modul-*.php, lehrplan.php, simulationen.php)
- Footer-Links "Andere Länder: 🇨🇭 🇩🇪 🇱🇮"
- lehrplan.php liest Default-Filter jetzt aus Country::current()
- iPad-freundlich: Native Select gibt Wheel-Picker auf iOS,
  Touch-Ziel >=40px

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-19 00:09:43 +02:00
Adminator db9ed43407 Lehrplan: 4 Modul-Detailseiten + lehrplan.php + simulationen.php + 78 Anker
- Gemeinsamer Partial App/pages/_partials/modul_detail.php
- modul-klima/fluss/heli/stadt als 3-Zeilen-Wrapper
- lehrplan.php: Filter Land/Thema/Typ, TOC, URL-Persistenz
- simulationen.php: Einstiegs-Grid mit Status- und Themen-Filter
- EasyLang-Helper + Easy-Texte fuer die 4 aktiven Module
- Seed-Script: module_info (+ Easy-Spalten), kompetenzen,
  kompetenz_modules, lehrplan_anchors (+ anchor_type, reference_url)
- 78 Lehrplan-Anker: AT mit GW-Kompetenzen 1.9/2.1/4.2 etc.,
  13 uebergreifende Themen, andere Faecher (Bio, Chemie, Physik,
  Mathematik, GSP, Bewegung+Sport); DE 10 Bundeslaender-Quellen +
  DGfG; CH Lehrplan 21 (RZG, NT, MA, MI, WAH, ERG, BNE); LI LiLe
- iPad-First: Touch-Ziele >=40px, :active-Feedback, Breakpoint 900px
- Inbox: Konventionen (Instanz-Name, iPad, Leichte Sprache,
  _status.md, Git-Commits) umgesetzt

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-19 00:05:12 +02:00
Adminator 32ed869f23 Simplify: PHP page dedup, secure UUID, input validation, remove redundant headers
- pages/*.php: 11 files reduced to 1-liners via shared renderPage() helper
- Session: UUID generation uses random_bytes() instead of mt_rand()
- Session: logout cookie uses same security options as create
- API saves: size limits on key (100) and data (500KB)
- API sessions: displayName capped at 64 chars
- API: removed redundant Content-Type headers (Response::json handles it)
- API dashboard: replaced SELECT * with explicit columns

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 16:54:43 +02:00
Adminator f22c5ebbfe Stand 2026-04-13: PHP/MySQL Infrastruktur, Flussmanagement, Stadt-Prototyp
- PHP/MySQL Backend (XAMPP + Produktionsserver)
- Front-Controller, API-Endpunkte, Session-Management
- Flussmanagement-Simulation (Echtzeit, Punkt-basierter Fluss)
- Stadt & Raumplanung (Prototyp, Top-Down Kachelsystem)
- Klimawaechter 3D: Deiche kleiner, Baeume kippen, Budget angepasst
- persistence.ts: Dualer Speicher (localStorage + Server-API)
- 6 Unit-Test-Dateien fuer bestehende Simulationen

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 16:43:42 +02:00