Files
geograsim/App/php/api/modules.php
T
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

199 lines
8.3 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?php
/**
* API: Modulfreigabe (Klasse + individuell pro Schüler)
* GET /api/modules?class_id=X → Modulstatus für Klasse
* GET /api/modules?class_id=X&matrix=1 → Matrix: alle Schüler × alle Module
* POST /api/modules {action} → Modul freigeben/sperren
*/
$method = $_SERVER['REQUEST_METHOD'];
$db = Database::get();
$ALL_MODULES = [
['id' => 'sim-05', 'name' => 'Klimawächter 2D', 'desc' => 'Treibhauseffekt-Simulation', 'icon' => '🌡️'],
['id' => 'sim-05-3d', 'name' => 'Klimawächter 3D', 'desc' => '3D-Klimasimulation', 'icon' => '🌊'],
['id' => 'sim-07', 'name' => 'Erdbeben', 'desc' => 'Plattentektonik & Stadtplanung', 'icon' => '🌋'],
['id' => 'sim-09', 'name' => 'Energiemix', 'desc' => 'Energiewende planen', 'icon' => '⚡'],
['id' => 'sim-10', 'name' => 'Lieferketten', 'desc' => 'Globale Lieferketten', 'icon' => '👕'],
['id' => 'sim-11', 'name' => 'Regenwald', 'desc' => 'Regenwald-Expedition', 'icon' => '🌴'],
['id' => 'sim-12', 'name' => 'Flussmanagement', 'desc' => 'Fluss & Hochwasserschutz', 'icon' => '🏞️'],
['id' => 'sim-13', 'name' => 'Stadt & Raumplanung', 'desc' => 'Stadtsimulation mit Kacheln', 'icon' => '🏗️'],
['id' => 'sim-14', 'name' => 'Helikopter-Navigation', 'desc' => 'Rettungshubschrauber steuern', 'icon' => '🚁'],
];
if ($method === 'GET') {
// Schüler*innen-Ansicht: welche Module sind für mich freigeschaltet?
if (isset($_GET['student']) && $_GET['student'] === '1') {
$sessionId = Session::requireStudent();
$session = $db->fetchOne('SELECT class_id FROM student_sessions WHERE id = ?', [$sessionId]);
if (!$session || !$session['class_id']) {
// Kein Klasse = alle Module frei (Autodidakt)
$result = [];
foreach ($ALL_MODULES as $mod) {
$result[] = ['id' => $mod['id'], 'name' => $mod['name'], 'icon' => $mod['icon'], 'mode' => 'free'];
}
Response::ok($result);
}
$classId = $session['class_id'];
$classMap = [];
$settings = $db->fetchAll('SELECT module_id, mode FROM class_modules WHERE class_id = ?', [$classId]);
foreach ($settings as $s) $classMap[$s['module_id']] = $s['mode'];
// Individuelle Overrides für diesen Schüler
// Finde student_id aus session
$studentRow = $db->fetchOne(
'SELECT s.id FROM students s JOIN student_sessions ss ON ss.class_id = s.class_id AND ss.display_name = s.display_name WHERE ss.id = ?',
[$sessionId]
);
$overrides = [];
if ($studentRow) {
$rows = $db->fetchAll('SELECT module_id, mode FROM student_modules WHERE student_id = ?', [$studentRow['id']]);
foreach ($rows as $r) $overrides[$r['module_id']] = $r['mode'];
}
$result = [];
foreach ($ALL_MODULES as $mod) {
$mode = $overrides[$mod['id']] ?? $classMap[$mod['id']] ?? 'locked';
$result[] = ['id' => $mod['id'], 'name' => $mod['name'], 'icon' => $mod['icon'], 'mode' => $mode];
}
Response::ok($result);
}
$teacherId = Session::requireTeacher();
$classId = (int)($_GET['class_id'] ?? 0);
if (!$classId) Response::error('class_id erforderlich');
$class = $db->fetchOne('SELECT id FROM classes WHERE id = ? AND teacher_id = ?', [$classId, $teacherId]);
if (!$class) Response::error('Klasse nicht gefunden', 404);
// Klassen-Defaults laden
$settings = $db->fetchAll('SELECT module_id, mode FROM class_modules WHERE class_id = ?', [$classId]);
$classMap = [];
foreach ($settings as $s) $classMap[$s['module_id']] = $s['mode'];
// Matrix-Modus: alle Schüler × alle Module
if (isset($_GET['matrix']) && $_GET['matrix'] === '1') {
$students = $db->fetchAll(
'SELECT id, username, display_name, emoji_avatar FROM students WHERE class_id = ? ORDER BY username',
[$classId]
);
// Individuelle Overrides laden
$studentIds = array_column($students, 'id');
$overrides = [];
if (!empty($studentIds)) {
$placeholders = implode(',', array_fill(0, count($studentIds), '?'));
$rows = $db->fetchAll(
"SELECT student_id, module_id, mode FROM student_modules WHERE student_id IN ($placeholders)",
$studentIds
);
foreach ($rows as $r) {
$overrides[$r['student_id']][$r['module_id']] = $r['mode'];
}
}
// Matrix aufbauen
$matrix = [];
foreach ($students as $s) {
$row = [
'id' => (int)$s['id'],
'username' => $s['username'],
'displayName' => $s['display_name'] ?: $s['username'],
'emoji' => $s['emoji_avatar'] ?: '🧑‍🎓',
'modules' => [],
];
foreach ($ALL_MODULES as $mod) {
$row['modules'][$mod['id']] = $overrides[$s['id']][$mod['id']] ?? $classMap[$mod['id']] ?? 'locked';
}
$matrix[] = $row;
}
// Modul-Infos mit Klassen-Defaults
$modulesInfo = [];
foreach ($ALL_MODULES as $mod) {
$modulesInfo[] = [
'id' => $mod['id'],
'name' => $mod['name'],
'desc' => $mod['desc'],
'icon' => $mod['icon'],
'classMode' => $classMap[$mod['id']] ?? 'locked',
];
}
Response::ok(['modules' => $modulesInfo, 'students' => $matrix]);
}
// Standard: nur Klassen-Defaults
$result = [];
foreach ($ALL_MODULES as $mod) {
$result[] = [
'id' => $mod['id'],
'name' => $mod['name'],
'desc' => $mod['desc'],
'icon' => $mod['icon'],
'mode' => $classMap[$mod['id']] ?? 'locked',
];
}
Response::ok($result);
}
if ($method === 'POST') {
$teacherId = Session::requireTeacher();
$body = json_decode(file_get_contents('php://input'), true);
$action = $body['action'] ?? '';
$classId = (int)($body['classId'] ?? 0);
$moduleId = $body['moduleId'] ?? '';
if (!$classId || !$moduleId) Response::error('classId und moduleId erforderlich');
$class = $db->fetchOne('SELECT id FROM classes WHERE id = ? AND teacher_id = ?', [$classId, $teacherId]);
if (!$class) Response::error('Klasse nicht gefunden', 404);
// Klassen-Default setzen
if ($action === 'set_mode') {
$mode = $body['mode'] ?? 'locked';
if (!in_array($mode, ['locked', 'free', 'teacher_started'])) Response::error('Ungültiger Modus');
$db->execute(
'INSERT INTO class_modules (class_id, module_id, enabled, mode)
VALUES (?, ?, 1, ?)
ON DUPLICATE KEY UPDATE mode = VALUES(mode), enabled = 1',
[$classId, $moduleId, $mode]
);
Response::ok();
}
// Individuell pro Schüler setzen
if ($action === 'set_student_mode') {
$studentId = (int)($body['studentId'] ?? 0);
$mode = $body['mode'] ?? 'locked';
if (!$studentId) Response::error('studentId erforderlich');
if (!in_array($mode, ['locked', 'free', 'teacher_started'])) Response::error('Ungültiger Modus');
// Prüfen dass Schüler zur Klasse gehört
$student = $db->fetchOne('SELECT id FROM students WHERE id = ? AND class_id = ?', [$studentId, $classId]);
if (!$student) Response::error('Schüler nicht in dieser Klasse');
$db->execute(
'INSERT INTO student_modules (student_id, module_id, mode)
VALUES (?, ?, ?)
ON DUPLICATE KEY UPDATE mode = VALUES(mode)',
[$studentId, $moduleId, $mode]
);
Response::ok();
}
// Klassen-Default für ALLE Schüler übernehmen (reset overrides)
if ($action === 'reset_overrides') {
$db->execute(
'DELETE sm FROM student_modules sm JOIN students s ON s.id = sm.student_id WHERE s.class_id = ? AND sm.module_id = ?',
[$classId, $moduleId]
);
Response::ok();
}
Response::error('Unbekannte Aktion');
}
Response::error('Methode nicht erlaubt', 405);