API-Schlüssel, Integration & Wartung, Voucher-Import, Backup & Restore, Reporting, Sicherheit (2FA) und Audit-Log waren fest auf Deutsch verdrahtet, obwohl in der Kopfzeile ein DE/EN-Umschalter sitzt. Diese Seiten laufen jetzt komplett über lang/de.php bzw. lang/en.php. - rund 200 neue Sprachschlüssel, beide Dateien deckungsgleich - Aktionsnamen im Audit-Log werden übersetzt statt fest ausgegeben - Bestätigungsdialoge und Statusmeldungen in JavaScript ebenfalls - admin/security.php initialisiert jetzt I18n und setzt <html lang> passend zur Auswahl Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
164 lines
12 KiB
PHP
164 lines
12 KiB
PHP
<?php
|
||
/** Demo-Stub: liefert feste Beispieldaten statt echter DB-Zugriffe. */
|
||
class Database {
|
||
private static $instance = null;
|
||
private $settings = [
|
||
'app_title' => 'UniFi Voucher System',
|
||
'logo_url' => '', 'favicon_url' => '',
|
||
'public_access' => '1', 'smtp_enabled' => '1', 'sms_enabled' => '1',
|
||
'm365_enabled' => '1', 'oidc_enabled' => '0', 'oidc_name' => 'Keycloak',
|
||
'default_expire_minutes' => '480', 'default_max_uses' => '2', 'max_uses_limit' => '10',
|
||
'instruction_header' => 'So verbinden Sie sich',
|
||
'instruction_text' => 'WLAN „Gast-WiFi" wählen, Code eingeben und bestätigen. Bei Fragen hilft der Empfang gerne weiter.',
|
||
'last_cron_sync' => '2026-09-22 14:35:00',
|
||
'session_driver' => 'db', 'captcha_mode' => 'math',
|
||
'enforce_2fa_admins' => '1', 'user_daily_voucher_limit' => '25',
|
||
'webhook_enabled' => '1', 'webhook_url' => 'https://hooks.slack.com/services/T024/B0197/XYZ',
|
||
'twilio_sid' => 'AC8f2c1d94e77b40a2', 'twilio_from' => '+4915112345678',
|
||
'trusted_proxy' => '10.0.0.1, 172.18.0.1',
|
||
'cleanup_expired_days' => '90', 'cleanup_audit_days' => '365', 'cleanup_login_days' => '30',
|
||
'last_cleanup' => '2026-09-22 03:00:00',
|
||
'print_template' => '<div style="padding:20px;border:1px dashed #999;"><h2>{APP_TITLE}</h2><p>{SITE_NAME}</p><h1>{VOUCHER_CODE}</h1></div>',
|
||
];
|
||
public function __construct()
|
||
{
|
||
// Screenshot-Varianten: ?brand=custom|gradient|nopanel
|
||
$brand = $_GET['brand'] ?? '';
|
||
if ($brand === 'custom') {
|
||
$this->settings = array_merge($this->settings, [
|
||
'app_title' => 'Hotel Seeblick',
|
||
'login_brand_name' => 'Hotel Seeblick',
|
||
'login_logo_url' => '/demo-assets/logo.svg',
|
||
'login_claim_title' => 'Willkommen im Hotel Seeblick.',
|
||
'login_claim_text' => 'Gäste-WLAN für Zimmer, Tagungsräume und Restaurant – Zugangscodes direkt an der Rezeption erstellen.',
|
||
'login_features' => "Code direkt beim Check-in ausdrucken\nTagungsgäste per E-Mail versorgen\nAuswertung je Haus und Etage",
|
||
'login_footer' => '© 2026 Hotel Seeblick GmbH · Datenschutz · Impressum',
|
||
'login_bg_image' => '/demo-assets/background.svg',
|
||
'login_bg_overlay' => '45',
|
||
'brand_accent' => '#0f766e',
|
||
'brand_accent_dark' => '#2dd4bf',
|
||
'brand_gradient_from' => '#0f766e',
|
||
'brand_gradient_to' => '#0ea5e9',
|
||
]);
|
||
} elseif ($brand === 'nopanel') {
|
||
$this->settings = array_merge($this->settings, [
|
||
'login_panel_enabled' => '0',
|
||
'login_brand_name' => 'Stadtwerke Nordheim',
|
||
]);
|
||
}
|
||
}
|
||
|
||
public static function getInstance() { return self::$instance ??= new self(); }
|
||
public function getConnection() { return null; }
|
||
public function getSetting($key, $default = null) { return $this->settings[$key] ?? $default; }
|
||
public function setSetting($key, $value) { $this->settings[$key] = $value; }
|
||
public function execute($sql, $params = []) { return 1; }
|
||
public function query($sql, $params = []) { return new DemoStmt(); }
|
||
public function fetchOne($sql, $params = []) { $r = $this->fetchAll($sql, $params); return $r[0] ?? false; }
|
||
|
||
public function fetchAll($sql, $params = []) {
|
||
$s = preg_replace('/\s+/', ' ', strtolower($sql));
|
||
|
||
if (str_contains($s, 'count(*) as count from sites')) return [['count'=>4]];
|
||
if (str_contains($s, 'count(*) as count from users')) return [['count'=>12]];
|
||
if (str_contains($s, 'count(*) as count from vouchers where date(created_at)=curdate()')) return [['count'=>18]];
|
||
if (str_contains($s, 'count(*) as count from vouchers where date(created_at)=')) {
|
||
$day = $params[0] ?? '';
|
||
$map = [6,9,5,12,8,4,7,14,11,16,9,6,13,18];
|
||
$idx = abs((int)((strtotime($day) - strtotime('today')) / 86400));
|
||
return [['count' => $map[13 - min($idx, 13)]]];
|
||
}
|
||
if (str_contains($s, 'from vouchers where site_id=?') && str_contains($s, 'count(*) as total')) {
|
||
$per = [1=>['total'=>128,'valid'=>74,'used'=>39,'expired'=>15],
|
||
2=>['total'=>63,'valid'=>41,'used'=>18,'expired'=>4],
|
||
3=>['total'=>22,'valid'=>14,'used'=>6,'expired'=>2],
|
||
4=>['total'=>0,'valid'=>0,'used'=>0,'expired'=>0]];
|
||
return [$per[(int)($params[0] ?? 1)] ?? $per[4]];
|
||
}
|
||
if (str_contains($s, 'count(*) as total') && str_contains($s, 'from vouchers')) {
|
||
return [['total'=>213,'valid'=>129,'used'=>63,'expired'=>21]];
|
||
}
|
||
if (str_contains($s, 'from sites')) {
|
||
return [
|
||
['id'=>1,'name'=>'Hauptstandort Nord','site_id'=>'default','unifi_controller_url'=>'https://unifi.example.com:8443','unifi_username'=>'voucher-api','unifi_password'=>'x','is_active'=>1,'use_ssl'=>1,'created_at'=>'2026-01-12 09:00:00'],
|
||
['id'=>2,'name'=>'Campus West','site_id'=>'campus-west','unifi_controller_url'=>'https://unifi.example.com:8443','unifi_username'=>'voucher-api','unifi_password'=>'x','is_active'=>1,'use_ssl'=>1,'created_at'=>'2026-02-03 11:30:00'],
|
||
['id'=>3,'name'=>'Showroom Berlin','site_id'=>'showroom','unifi_controller_url'=>'https://unifi-b.example.com:8443','unifi_username'=>'voucher-api','unifi_password'=>'x','is_active'=>1,'use_ssl'=>1,'created_at'=>'2026-04-21 15:10:00'],
|
||
['id'=>4,'name'=>'Lager Süd','site_id'=>'lager','unifi_controller_url'=>'https://unifi-s.example.com:8443','unifi_username'=>'voucher-api','unifi_password'=>'x','is_active'=>1,'use_ssl'=>0,'created_at'=>'2026-06-08 08:45:00'],
|
||
];
|
||
}
|
||
if (str_contains($s, 'count(v.id) as voucher_count') || str_contains($s, 'from users u left join vouchers')) {
|
||
return [
|
||
['name'=>'Marie Sander','email'=>'marie.sander@example.com','voucher_count'=>48,'c'=>48],
|
||
['name'=>'Jonas Weber','email'=>'j.weber@example.com','voucher_count'=>31,'c'=>31],
|
||
['name'=>'Alina Brandt','email'=>'alina.brandt@example.com','voucher_count'=>27,'c'=>27],
|
||
['name'=>'Tim Kurz','email'=>'tim.kurz@example.com','voucher_count'=>19,'c'=>19],
|
||
['name'=>'Sara Vogt','email'=>'sara.vogt@example.com','voucher_count'=>12,'c'=>12],
|
||
];
|
||
}
|
||
if (str_contains($s, 'from vouchers')) {
|
||
if (str_contains($s, 'group by date')) {
|
||
$out = []; $vals = [6,9,5,12,8,4,7,14,11,16,9,6,13,18];
|
||
foreach ($vals as $i => $c) { $out[] = ['date'=>date('d.m', strtotime('-'.(13-$i).' days')), 'count'=>$c]; }
|
||
return $out;
|
||
}
|
||
$codes = [['H7K2-M9QF',1,'Hauptstandort Nord','valid'],['P3RT-8ZXC',2,'Campus West','used'],
|
||
['QW4E-7TYU',1,'Hauptstandort Nord','valid'],['LM2N-5BVD',3,'Showroom Berlin','expired'],
|
||
['ZX9C-3PLO',2,'Campus West','valid'],['FG6H-1JKL',1,'Hauptstandort Nord','used']];
|
||
$names = ['Besuch Agentur Nordlicht','Workshop Raum 2','Empfang Tagesgast','Messe-Stand','Handwerker Haustechnik','Bewerbungsgespräch'];
|
||
$out = [];
|
||
foreach ($codes as $i => [$c,$sid,$sname,$st]) {
|
||
$out[] = ['id'=>$i+1,'voucher_code'=>$c,'site_id'=>$sid,'site_name'=>$sname,'user_name'=>'Marie Sander',
|
||
'status'=>$st,'created_at'=>date('Y-m-d H:i:s', strtotime("-{$i} hours")),
|
||
'voucher_name'=>$names[$i],'unifi_voucher_id'=>'65f1a'.$i,
|
||
'expires_at'=>date('Y-m-d H:i:s', strtotime('+'.(8-$i).' hours')),
|
||
'max_uses'=>[2,5,1,3,2,1][$i],'used_count'=>[2,1,0,3,0,1][$i],'expire_minutes'=>480];
|
||
}
|
||
return $out;
|
||
}
|
||
if (str_contains($s, 'from users')) {
|
||
return [
|
||
['id'=>1,'name'=>'Marie Sander','email'=>'marie.sander@example.com','is_admin'=>1,'is_active'=>1,'created_at'=>'2026-01-10 09:00:00','last_login'=>'2026-09-22 08:12:00','totp_enabled'=>1,'auth_source'=>'local','voucher_count'=>48,'password_hash'=>'x'],
|
||
['id'=>2,'name'=>'Jonas Weber','email'=>'j.weber@example.com','is_admin'=>0,'is_active'=>1,'created_at'=>'2026-03-02 13:20:00','last_login'=>'2026-09-21 17:40:00','totp_enabled'=>0,'auth_source'=>'m365','voucher_count'=>31,'password_hash'=>''],
|
||
['id'=>3,'name'=>'Alina Brandt','email'=>'alina.brandt@example.com','is_admin'=>0,'is_active'=>1,'created_at'=>'2026-05-18 10:05:00','last_login'=>'2026-09-20 09:31:00','totp_enabled'=>1,'auth_source'=>'local','voucher_count'=>27,'password_hash'=>'x'],
|
||
];
|
||
}
|
||
if (str_contains($s, 'from api_keys')) {
|
||
return [
|
||
['id'=>1,'name'=>'Buchungssystem','key_prefix'=>'3f9a2c','scope'=>'write','rate_limit'=>60,'is_active'=>1,'last_used_at'=>'2026-09-22 09:12:00','creator'=>'Marie Sander','created_at'=>'2026-06-01 10:00:00'],
|
||
['id'=>2,'name'=>'Terminal Foyer','key_prefix'=>'b71e04','scope'=>'read','rate_limit'=>0,'is_active'=>1,'last_used_at'=>'2026-09-21 17:40:00','creator'=>'Marie Sander','created_at'=>'2026-07-14 16:20:00'],
|
||
['id'=>3,'name'=>'Altes Kassensystem','key_prefix'=>'c0d5f8','scope'=>'write','rate_limit'=>30,'is_active'=>0,'last_used_at'=>null,'creator'=>'Jonas Weber','created_at'=>'2026-02-09 08:05:00'],
|
||
];
|
||
}
|
||
if (str_contains($s, 'from audit_log')) {
|
||
if (str_contains($s, 'count(')) return [['total'=>1284,'c'=>1284,'count'=>1284]];
|
||
if (str_contains($s, 'distinct action')) {
|
||
return [['action'=>'voucher_created'],['action'=>'user_login'],['action'=>'settings_saved'],['action'=>'user_created'],['action'=>'template_updated']];
|
||
}
|
||
$rows = [['2026-09-22 14:32:00','Marie Sander','voucher_created','Hauptstandort Nord · H7K2-M9QF','192.168.10.42'],
|
||
['2026-09-22 13:58:00','Jonas Weber','user_login','Microsoft 365','83.112.4.17'],
|
||
['2026-09-22 11:04:00','Marie Sander','settings_saved','Tab: E-Mail (SMTP)','192.168.10.42'],
|
||
['2026-09-21 17:40:00','Alina Brandt','user_created','tim.kurz@example.com','192.168.10.88'],
|
||
['2026-09-21 09:12:00','Marie Sander','template_updated','Tagesgast','192.168.10.42'],
|
||
['2026-09-20 16:03:00','Jonas Weber','voucher_bulk','5 Vouchers · Campus West','83.112.4.17']];
|
||
$out = [];
|
||
foreach ($rows as $i => [$d,$u,$a,$det,$ip]) {
|
||
$out[] = ['id'=>$i+1,'created_at'=>$d,'user_name'=>$u,'user_email'=>'demo@example.com','action'=>$a,'details'=>$det,'ip_address'=>$ip,'entity_type'=>'voucher','entity_id'=>$i+1];
|
||
}
|
||
return $out;
|
||
}
|
||
if (str_contains($s, 'templates')) {
|
||
return [
|
||
['id'=>1,'name'=>'Tagesgast','description'=>'Standardprofil für Besucher am Empfang','max_uses'=>2,'expire_minutes'=>480,'qos_rate_max_down'=>20000,'qos_rate_max_up'=>5000,'qos_usage_quota'=>0,'is_active'=>1,'is_default'=>1,'created_at'=>'2026-01-15 10:00:00'],
|
||
['id'=>2,'name'=>'Konferenz','description'=>'Mehrtägige Veranstaltungen','max_uses'=>5,'expire_minutes'=>4320,'qos_rate_max_down'=>50000,'qos_rate_max_up'=>10000,'qos_usage_quota'=>0,'is_active'=>1,'is_default'=>0,'created_at'=>'2026-02-20 11:30:00'],
|
||
['id'=>3,'name'=>'Handwerker','description'=>'Kurzzugang für Dienstleister','max_uses'=>1,'expire_minutes'=>240,'qos_rate_max_down'=>10000,'qos_rate_max_up'=>2000,'qos_usage_quota'=>1024,'is_active'=>1,'is_default'=>0,'created_at'=>'2026-03-08 09:15:00'],
|
||
];
|
||
}
|
||
return [];
|
||
}
|
||
}
|
||
class DemoStmt {
|
||
public function fetch($m = null) { return false; }
|
||
public function fetchAll($m = null) { return []; }
|
||
public function rowCount() { return 1; }
|
||
public function execute($p = []) { return true; }
|
||
}
|