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

202 lines
8.4 KiB
PHP

<?php
/**
* API: Lizenzverwaltung
* GET /api/licenses?class_id=X → Lizenzen der Klasse (Lehrer)
* GET /api/licenses?admin=1 → Alle Lizenzen (Super-Admin)
* POST /api/licenses {action} → Lizenz einlösen/zuweisen/generieren
*/
$method = $_SERVER['REQUEST_METHOD'];
$db = Database::get();
// === GET ===
if ($method === 'GET') {
// Super-Admin: alle Lizenzen
if (isset($_GET['admin']) && $_GET['admin'] === '1') {
Session::start();
$adminId = $_SESSION['admin_id'] ?? null;
$teacherId = Session::teacherId();
if (!$adminId && !$teacherId) {
Response::error('Nicht autorisiert', 401);
}
$year = $_GET['year'] ?? null;
$status = $_GET['status'] ?? null; // 'free', 'used'
$where = '1=1';
$params = [];
if ($year) { $where .= ' AND l.school_year = ?'; $params[] = $year; }
if ($status === 'free') { $where .= ' AND l.student_id IS NULL'; }
if ($status === 'used') { $where .= ' AND l.student_id IS NOT NULL'; }
$total = $db->fetchOne("SELECT COUNT(*) as c FROM licenses l WHERE $where", $params);
$free = $db->fetchOne("SELECT COUNT(*) as c FROM licenses l WHERE student_id IS NULL" . ($year ? " AND school_year = ?" : ""), $year ? [$year] : []);
$used = $db->fetchOne("SELECT COUNT(*) as c FROM licenses l WHERE student_id IS NOT NULL" . ($year ? " AND school_year = ?" : ""), $year ? [$year] : []);
$limit = (int)($_GET['limit'] ?? 100);
$offset = (int)($_GET['offset'] ?? 0);
$licenses = $db->fetchAll(
"SELECT l.id, l.code, l.school_year, l.student_id, l.teacher_id, l.redeemed_at, l.created_at,
s.username as student_name, t.display_name as teacher_name
FROM licenses l
LEFT JOIN students s ON s.id = l.student_id
LEFT JOIN teachers t ON t.id = l.teacher_id
WHERE $where
ORDER BY l.id DESC
LIMIT $limit OFFSET $offset",
$params
);
Response::ok([
'total' => (int)$total['c'],
'free' => (int)$free['c'],
'used' => (int)$used['c'],
'licenses' => $licenses
]);
}
// Lehrer: Lizenzen einer Klasse
$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);
$licenses = $db->fetchAll(
'SELECT l.id, l.code, l.school_year, l.student_id, l.redeemed_at,
s.username as student_name, s.display_name as student_display
FROM licenses l
JOIN students s ON s.id = l.student_id
WHERE s.class_id = ?
ORDER BY s.username',
[$classId]
);
// Auch freie Lizenzen des Lehrers (noch nicht zugewiesen)
$freeLicenses = $db->fetchAll(
'SELECT id, code, school_year FROM licenses WHERE teacher_id = ? AND student_id IS NULL ORDER BY code',
[$teacherId]
);
Response::ok(['assigned' => $licenses, 'free' => $freeLicenses]);
}
// === POST ===
if ($method === 'POST') {
$body = json_decode(file_get_contents('php://input'), true);
$action = $body['action'] ?? '';
// Lizenz einlösen (Lehrer gibt Code ein → wird ihm zugeordnet)
if ($action === 'redeem') {
$teacherId = Session::requireTeacher();
$code = strtoupper(trim($body['code'] ?? ''));
if (!$code) Response::error('Lizenzcode erforderlich');
$license = $db->fetchOne('SELECT id, student_id, teacher_id FROM licenses WHERE code = ?', [$code]);
if (!$license) Response::error('Lizenzcode ungültig');
if ($license['student_id']) Response::error('Diese Lizenz ist bereits bereits zugewiesen');
if ($license['teacher_id'] && $license['teacher_id'] != $teacherId) Response::error('Diese Lizenz gehört einer anderen Lehrperson');
$db->execute('UPDATE licenses SET teacher_id = ?, redeemed_at = NOW() WHERE id = ?', [$teacherId, $license['id']]);
Response::ok(['licenseId' => (int)$license['id']]);
}
// Mehrere Lizenzen auf einmal einlösen
if ($action === 'redeem_batch') {
$teacherId = Session::requireTeacher();
$codes = $body['codes'] ?? [];
if (!is_array($codes) || empty($codes)) Response::error('Lizenzcodes erforderlich');
$redeemed = 0;
$errors = [];
foreach ($codes as $code) {
$code = strtoupper(trim($code));
if (!$code) continue;
$license = $db->fetchOne('SELECT id, student_id, teacher_id FROM licenses WHERE code = ?', [$code]);
if (!$license) { $errors[] = "$code: ungültig"; continue; }
if ($license['student_id']) { $errors[] = "$code: bereits zugewiesen"; continue; }
if ($license['teacher_id'] && $license['teacher_id'] != $teacherId) { $errors[] = "$code: gehört anderer Lehrperson"; continue; }
$db->execute('UPDATE licenses SET teacher_id = ?, redeemed_at = NOW() WHERE id = ?', [$teacherId, $license['id']]);
$redeemed++;
}
Response::ok(['redeemed' => $redeemed, 'errors' => $errors]);
}
// Lizenz einem Schüler zuweisen
if ($action === 'assign') {
$teacherId = Session::requireTeacher();
$licenseId = (int)($body['licenseId'] ?? 0);
$studentId = (int)($body['studentId'] ?? 0);
if (!$licenseId || !$studentId) Response::error('licenseId und studentId erforderlich');
$license = $db->fetchOne('SELECT id, teacher_id FROM licenses WHERE id = ? AND student_id IS NULL', [$licenseId]);
if (!$license) Response::error('Lizenz nicht gefunden oder bereits zugewiesen');
if ($license['teacher_id'] && $license['teacher_id'] != $teacherId) Response::error('Lizenz gehört anderer Lehrperson');
// Prüfen dass Schüler dem Lehrer gehört
$student = $db->fetchOne(
'SELECT s.id FROM students s JOIN classes c ON c.id = s.class_id WHERE s.id = ? AND c.teacher_id = ?',
[$studentId, $teacherId]
);
if (!$student) Response::error('Schüler*in nicht gefunden');
$db->execute('UPDATE licenses SET student_id = ?, teacher_id = ? WHERE id = ?', [$studentId, $teacherId, $licenseId]);
Response::ok();
}
// Auto-Assign: nächste freie Lizenz einem Schüler zuweisen
if ($action === 'auto_assign') {
$teacherId = Session::requireTeacher();
$studentId = (int)($body['studentId'] ?? 0);
if (!$studentId) Response::error('studentId erforderlich');
$student = $db->fetchOne(
'SELECT s.id FROM students s JOIN classes c ON c.id = s.class_id WHERE s.id = ? AND c.teacher_id = ?',
[$studentId, $teacherId]
);
if (!$student) Response::error('Schüler*in nicht gefunden');
// Nächste freie Lizenz des Lehrers
$license = $db->fetchOne(
'SELECT id FROM licenses WHERE teacher_id = ? AND student_id IS NULL ORDER BY id LIMIT 1',
[$teacherId]
);
if (!$license) Response::error('Keine freien Lizenzen verfügbar');
$db->execute('UPDATE licenses SET student_id = ? WHERE id = ?', [$studentId, $license['id']]);
Response::ok(['licenseId' => (int)$license['id']]);
}
// Super-Admin: neue Lizenzen generieren
if ($action === 'generate') {
$teacherId = Session::requireTeacher();
$count = min(1000, max(1, (int)($body['count'] ?? 100)));
$year = (int)($body['year'] ?? date('Y'));
$schoolYear = ($year % 100) . '/' . (($year % 100) + 1);
$chars = 'ABCDEFGHJKLMNPQRSTUVWXYZ23456789';
$generated = 0;
for ($i = 0; $i < $count; $i++) {
$code = '';
for ($j = 0; $j < 3; $j++) {
if ($j > 0) $code .= '-';
for ($k = 0; $k < 4; $k++) $code .= $chars[random_int(0, strlen($chars) - 1)];
}
$code .= '-' . $year;
try {
$db->execute('INSERT INTO licenses (code, school_year) VALUES (?, ?)', [$code, $schoolYear]);
$generated++;
} catch (\Exception $e) {
$i--; // Collision, retry
}
}
Response::ok(['generated' => $generated]);
}
Response::error('Unbekannte Aktion');
}
Response::error('Methode nicht erlaubt', 405);