requireAdmin(); I18n::init(); $db = Database::getInstance(); $appTitle = $db->getSetting('app_title', 'UniFi Voucher System'); $error = ''; $success = ''; /** Formularwerte einsammeln – für Anlegen und Bearbeiten identisch. */ function kioskInput(): array { return [ 'site_id' => (int)($_POST['site_id'] ?? 0), 'template_id' => (int)($_POST['template_id'] ?? 0) ?: null, 'name' => trim((string)($_POST['name'] ?? '')), 'headline' => trim((string)($_POST['headline'] ?? '')), 'subline' => trim((string)($_POST['subline'] ?? '')), 'daily_limit' => max(0, (int)($_POST['daily_limit'] ?? Kiosk::DEFAULT_DAILY_LIMIT)), 'cooldown_seconds' => max(0, min(3600, (int)($_POST['cooldown_seconds'] ?? Kiosk::DEFAULT_COOLDOWN))), 'display_seconds' => max(10, min(600, (int)($_POST['display_seconds'] ?? Kiosk::DEFAULT_DISPLAY_SECONDS))), 'is_active' => isset($_POST['is_active']) ? 1 : 0, 'bg_overlay' => max(0, min(90, (int)($_POST['bg_overlay'] ?? 45))), 'accent_color' => self_accent($_POST['accent_color'] ?? ''), 'card_style' => ($_POST['card_style'] ?? 'light') === 'dark' ? 'dark' : 'light', ]; } /** Nur echte Hex-Farben durchlassen – der Wert landet in einem style-Attribut. */ function self_accent($value): ?string { $value = strtolower(trim((string)$value)); return preg_match('/^#[0-9a-f]{6}$/', $value) ? $value : null; } if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['add_kiosk'])) { if (!$auth->validateCsrfToken($_POST['csrf_token'] ?? '')) { $error = __('error_csrf'); } else { try { $in = kioskInput(); if ($in['name'] === '') throw new Exception(__('error_name_req')); if ($in['site_id'] <= 0) throw new Exception(__('error_site_req')); $logo = Upload::resolveField('logo_url', '', 'image'); $bg = Upload::resolveField('background_url', '', 'image'); $db->execute( "INSERT INTO kiosks (site_id, template_id, name, token, headline, subline, logo_url, background_url, bg_overlay, accent_color, card_style, daily_limit, cooldown_seconds, display_seconds, is_active, created_by) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 1, ?)", [$in['site_id'], $in['template_id'], $in['name'], Kiosk::newToken(), $in['headline'], $in['subline'], $logo, $bg, $in['bg_overlay'], $in['accent_color'], $in['card_style'], $in['daily_limit'], $in['cooldown_seconds'], $in['display_seconds'], $_SESSION['user_id']] ); $auth->writeAuditLog($_SESSION['user_id'], 'kiosk_created', 'kiosk', null, $in['name']); $success = __('kiosks_added'); } catch (Exception $e) { $error = $e->getMessage(); } } } if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['edit_kiosk'])) { if (!$auth->validateCsrfToken($_POST['csrf_token'] ?? '')) { $error = __('error_csrf'); } else { try { $id = (int)($_POST['kiosk_id'] ?? 0); $in = kioskInput(); if ($in['name'] === '') throw new Exception(__('error_name_req')); if ($in['site_id'] <= 0) throw new Exception(__('error_site_req')); $current = $db->fetchOne("SELECT logo_url, background_url FROM kiosks WHERE id = ?", [$id]) ?: []; $logo = Upload::resolveField('logo_url', (string)($current['logo_url'] ?? ''), 'image'); $bg = Upload::resolveField('background_url', (string)($current['background_url'] ?? ''), 'image'); $db->execute( "UPDATE kiosks SET site_id=?, template_id=?, name=?, headline=?, subline=?, logo_url=?, background_url=?, bg_overlay=?, accent_color=?, card_style=?, daily_limit=?, cooldown_seconds=?, display_seconds=?, is_active=? WHERE id=?", [$in['site_id'], $in['template_id'], $in['name'], $in['headline'], $in['subline'], $logo, $bg, $in['bg_overlay'], $in['accent_color'], $in['card_style'], $in['daily_limit'], $in['cooldown_seconds'], $in['display_seconds'], $in['is_active'], $id] ); $auth->writeAuditLog($_SESSION['user_id'], 'kiosk_updated', 'kiosk', $id, $in['name']); $success = __('kiosks_updated'); } catch (Exception $e) { $error = $e->getMessage(); } } } // Neuen Link erzeugen – der alte gilt damit sofort nicht mehr. if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['renew_token'])) { if (!$auth->validateCsrfToken($_POST['csrf_token'] ?? '')) { $error = __('error_csrf'); } else { $id = (int)($_POST['kiosk_id'] ?? 0); $db->execute("UPDATE kiosks SET token = ? WHERE id = ?", [Kiosk::newToken(), $id]); $auth->writeAuditLog($_SESSION['user_id'], 'kiosk_updated', 'kiosk', $id, 'Link erneuert'); $success = __('kiosks_token_renewed'); } } if (isset($_GET['delete'], $_GET['token'])) { if ($auth->validateCsrfToken($_GET['token'])) { $old = $db->fetchOne("SELECT logo_url, background_url FROM kiosks WHERE id = ?", [(int)$_GET['delete']]); if ($old) { Upload::delete((string)($old['logo_url'] ?? '')); Upload::delete((string)($old['background_url'] ?? '')); } $db->execute("DELETE FROM kiosks WHERE id = ?", [(int)$_GET['delete']]); $auth->writeAuditLog($_SESSION['user_id'], 'kiosk_deleted', 'kiosk', (int)$_GET['delete'], ''); $success = __('kiosks_deleted'); } else { $error = __('error_csrf'); } } $sites = $db->fetchAll("SELECT id, name FROM sites WHERE is_active = 1 ORDER BY name"); $templates = $db->fetchAll("SELECT id, name, max_uses, expire_minutes FROM voucher_templates WHERE is_active = 1 ORDER BY name"); $kiosks = $db->fetchAll( "SELECT k.*, s.name AS site_name, t.name AS template_name, (SELECT COUNT(*) FROM vouchers v WHERE v.kiosk_id = k.id) AS total_vouchers, (SELECT COUNT(*) FROM vouchers v WHERE v.kiosk_id = k.id AND DATE(v.created_at) = CURDATE()) AS today_vouchers FROM kiosks k INNER JOIN sites s ON s.id = k.site_id LEFT JOIN voucher_templates t ON t.id = k.template_id ORDER BY k.is_active DESC, k.name" ); $csrf = $auth->getCsrfToken(); $currentPage = 'kiosks'; $adminBase = ''; ?>
= __('kiosks_subtitle') ?>
= __('kiosks_no_sites') ?>
= __('sites_add') ?>= __('kiosks_empty') ?>