diff --git a/App/pages/foto-upload.php b/App/pages/foto-upload.php index 867a414..7261d41 100644 --- a/App/pages/foto-upload.php +++ b/App/pages/foto-upload.php @@ -64,6 +64,52 @@ $MAX_BYTES = 40 * 1024 * 1024; // 40 MB je Datei $MAX_FILES = 12; // je Absenden $ALLOWED_EXT = ['jpg','jpeg','png','heic','heif','webp','tif','tiff']; +/* ---------- Bild-Auslieferung (nur mit PIN; der Ordner selbst bleibt gesperrt) ---------- */ +if (isset($_GET['img']) || isset($_GET['thumb'])) { + $req = $_GET['img'] ?? $_GET['thumb']; + $sim = preg_replace('/[^a-z0-9-]/', '', dirname($req)); + $file = basename($req); + $pfad = "{$UPLOAD_BASE}/{$sim}/{$file}"; + if ($sim === '' || $file === '' || strpos($file, '..') !== false || !is_file($pfad)) { + http_response_code(404); exit('nicht gefunden'); + } + $ext = strtolower(pathinfo($file, PATHINFO_EXTENSION)); + $mime = ['jpg' => 'image/jpeg', 'jpeg' => 'image/jpeg', 'png' => 'image/png', 'webp' => 'image/webp', + 'heic' => 'image/heic', 'heif' => 'image/heif', 'tif' => 'image/tiff', 'tiff' => 'image/tiff'][$ext] ?? 'application/octet-stream'; + if (isset($_GET['thumb'])) { + $tdir = "{$UPLOAD_BASE}/.thumbs"; + if (!is_dir($tdir)) mkdir($tdir, 0775, true); + $tpfad = "{$tdir}/{$sim}__{$file}.jpg"; + if (!is_file($tpfad) || filemtime($tpfad) < filemtime($pfad)) { + $src = @imagecreatefromstring((string)@file_get_contents($pfad)); + if ($src) { + // iPhone/Kamera-Fotos: EXIF-Orientierung berücksichtigen + if (in_array($ext, ['jpg', 'jpeg'], true) && function_exists('exif_read_data')) { + $exif = @exif_read_data($pfad); + $o = (int)($exif['Orientation'] ?? 1); + if ($o === 3) $src = imagerotate($src, 180, 0); + elseif ($o === 6) $src = imagerotate($src, -90, 0); + elseif ($o === 8) $src = imagerotate($src, 90, 0); + } + $w = imagesx($src); $h = imagesy($src); + $f = min(1, 340 / max($w, $h)); + $tw = max(1, (int)round($w * $f)); $th = max(1, (int)round($h * $f)); + $dst = imagecreatetruecolor($tw, $th); + imagecopyresampled($dst, $src, 0, 0, 0, 0, $tw, $th, $w, $h); + imagejpeg($dst, $tpfad, 82); + imagedestroy($src); imagedestroy($dst); + } + } + if (is_file($tpfad)) { $pfad = $tpfad; $mime = 'image/jpeg'; } + // sonst (z. B. HEIC ohne GD-Support): Original ausliefern — Safari/iPad zeigt es nativ + } + header('Content-Type: ' . $mime); + header('Content-Length: ' . (string)filesize($pfad)); + header('Cache-Control: private, max-age=86400'); + readfile($pfad); + exit; +} + /* ---------- Slots aus den Kapiteln parsen ---------- */ $ORDER = ['tourismustal','tourismusregion','kofferdetektiv','busfahrt','logistik', 'weltkueche','farmer','klima','fluss','energiemanager','heli', @@ -105,6 +151,16 @@ function pendingUploads(string $base, string $sim, string $slug): array { } return $out; } +function pendingNotes(string $base, string $sim, string $slug): array { + $dir = "{$base}/{$sim}"; + if (!is_dir($dir)) return []; + $out = []; + foreach (glob($dir . '/' . $slug . '__*.note.txt') as $f) { + $t = trim((string)file_get_contents($f)); + if ($t !== '') $out[] = $t; + } + return $out; +} function safeName(string $s): string { $s = preg_replace('/[^A-Za-z0-9._-]+/', '-', $s); return trim(mb_substr($s, 0, 80), '-.') ?: 'foto'; @@ -192,7 +248,17 @@ foreach ($KAPITEL as $sim => $k) { .badge.offen { background: #eeeadd; color: #6b6659; } .beschr { line-height: 1.55; font-size: 14.5px; margin: 8px 0 10px; } .thumb { max-width: 190px; border-radius: 8px; display: block; margin: 6px 0; } - .pend { font-size: 12.5px; color: #a06a12; margin: 4px 0; } + a.lightbox { cursor: zoom-in; } + .pgrid { display: flex; flex-wrap: wrap; gap: 8px; margin: 8px 0; } + .pgrid .pt img { width: 110px; height: 110px; object-fit: cover; border-radius: 8px; display: block; + border: 2px solid #e8c9a0; background: #eee; } + .pnote { font-size: 13px; color: #6b6659; font-style: italic; margin: 2px 0 6px; } + #ov { position: fixed; inset: 0; background: rgba(20,26,30,.88); display: flex; align-items: center; + justify-content: center; z-index: 99; cursor: zoom-out; } + #ov[hidden] { display: none; } + #ov img { max-width: 94vw; max-height: 92vh; border-radius: 8px; box-shadow: 0 10px 40px rgba(0,0,0,.5); } + #ovx { position: fixed; top: 12px; right: 18px; color: #fff; font-size: 30px; font-weight: 800; cursor: pointer; + text-shadow: 0 1px 6px rgba(0,0,0,.7); } form.up { display: flex; flex-wrap: wrap; gap: 8px; align-items: center; background: #f4f1e6; border-radius: 9px; padding: 10px 12px; } form.up input[type=file] { font-size: 13px; max-width: 100%; } form.up input[type=text] { flex: 1 1 220px; min-width: 180px; border: 1.5px solid #c9c2ae; border-radius: 8px; padding: 8px 10px; font-size: 14px; background: #fff; } @@ -243,8 +309,17 @@ foreach ($KAPITEL as $sim => $k) { = $bild ? '✅ Foto eingebaut' : ($pend ? '📥 Upload wartet auf Einbau' : '📷 Foto gesucht') ?>
= htmlspecialchars($vorschlag) ?>
-