1e51ef7def
- 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>
111 lines
5.1 KiB
PHP
111 lines
5.1 KiB
PHP
<?php
|
|
/**
|
|
* API: Einladungstickets (druckbare HTML-Seite)
|
|
* GET /api/tickets?class_id=X → Druckbare Zugangskarten fuer alle Schueler der Klasse
|
|
*/
|
|
|
|
if ($_SERVER['REQUEST_METHOD'] !== 'GET') {
|
|
Response::error('Nur GET erlaubt', 405);
|
|
}
|
|
|
|
$teacherId = Session::requireTeacher();
|
|
$db = Database::get();
|
|
$classId = (int)($_GET['class_id'] ?? 0);
|
|
if (!$classId) Response::error('class_id erforderlich');
|
|
|
|
$class = $db->fetchOne(
|
|
'SELECT id, name, school_year, join_code FROM classes WHERE id = ? AND teacher_id = ?',
|
|
[$classId, $teacherId]
|
|
);
|
|
if (!$class) Response::error('Klasse nicht gefunden', 404);
|
|
|
|
$students = $db->fetchAll(
|
|
'SELECT username, display_name, emoji_avatar, password_plaintext FROM students WHERE class_id = ? AND deleted_at IS NULL ORDER BY username',
|
|
[$classId]
|
|
);
|
|
|
|
// Druckbare HTML-Seite ausgeben (kein JSON)
|
|
header('Content-Type: text/html; charset=utf-8');
|
|
?>
|
|
<!DOCTYPE html>
|
|
<html lang="de">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<title>Zugangskarten — <?= htmlspecialchars($class['name']) ?></title>
|
|
<style>
|
|
@page { size: A4; margin: 10mm; }
|
|
* { margin: 0; padding: 0; box-sizing: border-box; }
|
|
body { font-family: 'Inter', 'Segoe UI', system-ui, sans-serif; background: #fff; color: #1a1a1a; }
|
|
.print-header { text-align: center; padding: 1rem 0 .5rem; border-bottom: 2px solid #4a7c8a; margin-bottom: 1rem; }
|
|
.print-header h1 { font-size: 1.1rem; color: #4a7c8a; }
|
|
.print-header p { font-size: .75rem; color: #8a8a8a; }
|
|
.tickets { display: grid; grid-template-columns: repeat(3, 1fr); gap: 8mm; }
|
|
.ticket {
|
|
border: 2px solid #4a7c8a; border-radius: 10px; padding: 10px;
|
|
page-break-inside: avoid; position: relative; min-height: 140px;
|
|
display: grid; grid-template-columns: 1fr 90px; gap: 8px; align-items: start;
|
|
}
|
|
.ticket-main { min-width: 0; }
|
|
.ticket-qr { width: 90px; height: 90px; }
|
|
.ticket-qr canvas, .ticket-qr svg, .ticket-qr img { width: 100% !important; height: 100% !important; display: block; }
|
|
.ticket-top { margin-bottom: 6px; border-bottom: 1px dashed #dae8ec; padding-bottom: 6px; }
|
|
.ticket-brand { font-size: .6rem; font-weight: 800; color: #4a7c8a; letter-spacing: .02em; }
|
|
.ticket-class { font-size: .55rem; color: #8a8a8a; }
|
|
.ticket-row { display: flex; gap: 4px; margin-bottom: 4px; font-size: .7rem; }
|
|
.ticket-label { color: #8a8a8a; font-weight: 500; min-width: 80px; }
|
|
.ticket-val { font-weight: 700; font-family: 'Courier New', monospace; letter-spacing: .04em; }
|
|
.ticket-hint { font-size: .55rem; color: #aaa; margin-top: 6px; text-align: center; }
|
|
.ticket-url { font-size: .55rem; color: #4a7c8a; text-align: center; margin-top: 4px; font-weight: 600; }
|
|
.no-print { text-align: center; padding: 1rem; }
|
|
.no-print button { padding: .5rem 1.5rem; background: #4a7c8a; color: #fff; border: none; border-radius: 8px; font-size: .85rem; font-weight: 700; cursor: pointer; margin: 0 .3rem; }
|
|
.no-print button:hover { background: #3a6470; }
|
|
@media print {
|
|
.no-print { display: none; }
|
|
body { -webkit-print-color-adjust: exact; print-color-adjust: exact; }
|
|
}
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<div class="no-print">
|
|
<button onclick="window.print()">Drucken / als PDF speichern</button>
|
|
<button onclick="window.close()">Schliessen</button>
|
|
</div>
|
|
<div class="print-header">
|
|
<h1>GeoGraSim — Zugangskarten</h1>
|
|
<p>Klasse <?= htmlspecialchars($class['name']) ?> · <?= htmlspecialchars($class['school_year']) ?> · Code: <?= htmlspecialchars($class['join_code']) ?></p>
|
|
</div>
|
|
<div class="tickets">
|
|
<?php
|
|
// QR-Code-Daten: Login-Tiefenlink mit vorgefülltem Klassencode + Username
|
|
// (login.html liest beide Params und füllt die Felder vor, falls verfügbar).
|
|
$baseUrl = 'https://geograsim.at';
|
|
?>
|
|
<?php foreach ($students as $s):
|
|
$loginUrl = $baseUrl . '/login.html?class=' . urlencode($class['join_code']) . '&user=' . urlencode($s['username']);
|
|
?>
|
|
<div class="ticket">
|
|
<div class="ticket-main">
|
|
<div class="ticket-top">
|
|
<div class="ticket-brand">GeoGraSim</div>
|
|
<div class="ticket-class">Klasse <?= htmlspecialchars($class['name']) ?></div>
|
|
</div>
|
|
<div class="ticket-row"><span class="ticket-label">Klassencode:</span><span class="ticket-val"><?= htmlspecialchars($class['join_code']) ?></span></div>
|
|
<div class="ticket-row"><span class="ticket-label">Benutzer:</span><span class="ticket-val"><?= htmlspecialchars($s['username']) ?></span></div>
|
|
<?php if ($s['password_plaintext']): ?>
|
|
<div class="ticket-row"><span class="ticket-label">PIN:</span><span class="ticket-val"><?= htmlspecialchars($s['password_plaintext']) ?></span></div>
|
|
<?php else: ?>
|
|
<div class="ticket-row"><span class="ticket-label">PIN:</span><span class="ticket-val" style="color:#8a8a8a">selbst gesetzt</span></div>
|
|
<div class="ticket-hint">PIN wurde von der/dem Schüler*in geändert. Lehrkraft kann im Dashboard zurücksetzen.</div>
|
|
<?php endif; ?>
|
|
<div class="ticket-url">geograsim.at</div>
|
|
</div>
|
|
<div class="ticket-qr">
|
|
<img src="https://api.qrserver.com/v1/create-qr-code/?size=180x180&margin=4&data=<?= urlencode($loginUrl) ?>" alt="QR-Code" loading="eager">
|
|
</div>
|
|
</div>
|
|
<?php endforeach; ?>
|
|
</div>
|
|
</body>
|
|
</html>
|
|
<?php exit;
|