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>
138 lines
6.2 KiB
PHP
138 lines
6.2 KiB
PHP
<?php
|
||
error_reporting(E_ALL);
|
||
ini_set('display_errors', 0);
|
||
ini_set('log_errors', 1);
|
||
|
||
require_once __DIR__ . '/../config.php';
|
||
require_once __DIR__ . '/../includes/Database.php';
|
||
require_once __DIR__ . '/../includes/Auth.php';
|
||
require_once __DIR__ . '/../includes/UniFiController.php';
|
||
require_once __DIR__ . '/../includes/Notifier.php';
|
||
require_once __DIR__ . '/../includes/I18n.php';
|
||
|
||
$auth = new Auth();
|
||
$auth->requireAdmin();
|
||
I18n::init();
|
||
|
||
$db = Database::getInstance();
|
||
$appTitle = $db->getSetting('app_title', 'UniFi Voucher System');
|
||
$defaultExpire = max(1, (int)$db->getSetting('default_expire_minutes', 480));
|
||
$defaultMaxUses = max(1, (int)$db->getSetting('default_max_uses', 1));
|
||
|
||
$error = '';
|
||
$success = '';
|
||
$results = [];
|
||
|
||
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['do_import'])) {
|
||
if (!$auth->validateCsrfToken($_POST['csrf_token'] ?? '')) {
|
||
$error = __('error_csrf');
|
||
} else {
|
||
try {
|
||
$siteId = (int)($_POST['site_id'] ?? 0);
|
||
$site = $db->fetchOne("SELECT * FROM sites WHERE id=? AND is_active=1", [$siteId]);
|
||
if (!$site) throw new Exception('Site nicht gefunden');
|
||
|
||
// CSV-Quelle: Datei bevorzugt, sonst Textarea
|
||
$raw = '';
|
||
if (!empty($_FILES['csv']['tmp_name'])) {
|
||
$raw = file_get_contents($_FILES['csv']['tmp_name']);
|
||
} else {
|
||
$raw = (string)($_POST['csv_text'] ?? '');
|
||
}
|
||
$lines = preg_split('/\r\n|\r|\n/', trim($raw));
|
||
if (count($lines) > 200) throw new Exception('Maximal 200 Zeilen pro Import.');
|
||
|
||
$controller = new UniFiController(
|
||
$site['unifi_controller_url'], $site['unifi_username'],
|
||
Crypto::decrypt($site['unifi_password']), $site['site_id']
|
||
);
|
||
|
||
$created = 0;
|
||
foreach ($lines as $i => $line) {
|
||
$line = trim($line);
|
||
if ($line === '') continue;
|
||
$cols = str_getcsv($line);
|
||
$name = trim((string)($cols[0] ?? ''));
|
||
if ($name === '' || strtolower($name) === 'name') continue; // Header/leer überspringen
|
||
$maxUses = isset($cols[1]) && $cols[1] !== '' ? max(1, (int)$cols[1]) : $defaultMaxUses;
|
||
$expire = isset($cols[2]) && $cols[2] !== '' ? max(1, (int)$cols[2]) : $defaultExpire;
|
||
try {
|
||
$v = $controller->createVoucher(date('Y-m-d') . '_' . $name, $maxUses, $expire);
|
||
if (!is_array($v) || empty($v['formatted_code'])) throw new Exception('ungültige Antwort');
|
||
$db->execute(
|
||
"INSERT INTO vouchers (site_id, user_id, voucher_code, voucher_name, max_uses, expire_minutes, unifi_voucher_id)
|
||
VALUES (?, ?, ?, ?, ?, ?, ?)",
|
||
[$siteId, $_SESSION['user_id'], $v['code'], date('Y-m-d') . '_' . $name, $maxUses, $expire, $v['unifi_id'] ?? null]
|
||
);
|
||
$results[] = ['name' => $name, 'code' => $v['formatted_code'], 'ok' => true];
|
||
$created++;
|
||
} catch (Exception $e) {
|
||
$results[] = ['name' => $name, 'code' => $e->getMessage(), 'ok' => false];
|
||
}
|
||
}
|
||
if ($created > 0) {
|
||
Notifier::voucherCreated($created, $site['name'], $_SESSION['user_name'] ?? null);
|
||
$auth->writeAuditLog($_SESSION['user_id'], 'voucher_import', 'site', $siteId, "$created Voucher importiert");
|
||
}
|
||
$success = str_replace('{count}', (string)$created, __('import_created'));
|
||
} catch (Exception $e) {
|
||
$error = $e->getMessage();
|
||
}
|
||
}
|
||
}
|
||
|
||
$sites = $db->fetchAll("SELECT * FROM sites WHERE is_active=1 ORDER BY name");
|
||
$csrf = $auth->getCsrfToken();
|
||
$currentPage = 'import';
|
||
$adminBase = '';
|
||
?>
|
||
<!DOCTYPE html>
|
||
<html lang="<?= I18n::getLanguage() ?>">
|
||
<head>
|
||
<meta charset="UTF-8">
|
||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||
<title>CSV-Import – <?= htmlspecialchars($appTitle) ?></title>
|
||
<?php require __DIR__ . '/../includes/admin_nav.php'; ?>
|
||
<div class="page-header">
|
||
<div>
|
||
<h1 class="page-title"><?= __('import_title') ?></h1>
|
||
<p class="page-subtitle"><?= __('import_subtitle') ?></p>
|
||
</div>
|
||
</div>
|
||
|
||
<?php if ($error): ?><div class="alert alert-error"><?= htmlspecialchars($error) ?></div><?php endif; ?>
|
||
<?php if ($success): ?><div class="alert alert-ok"><?= htmlspecialchars($success) ?></div><?php endif; ?>
|
||
|
||
<div class="card">
|
||
<h2><?= __('import_card_title') ?></h2>
|
||
<p class="muted"><?= __('import_format_hint') ?> <code>Name,MaxGeräte,Minuten</code> <?= __('import_format_hint2') ?><br>
|
||
<code>Gast Müller,1,480</code> · <code>Konferenzraum A,5,240</code> · <code>Tagespass</code></p>
|
||
<form method="post" enctype="multipart/form-data">
|
||
<input type="hidden" name="csrf_token" value="<?= htmlspecialchars($csrf) ?>">
|
||
<label><?= __('import_site') ?></label>
|
||
<select class="input" name="site_id" required>
|
||
<?php foreach ($sites as $s): ?><option value="<?= (int)$s['id'] ?>"><?= htmlspecialchars($s['name']) ?></option><?php endforeach; ?>
|
||
</select>
|
||
<label><?= __('import_file') ?></label>
|
||
<input class="input" type="file" name="csv" accept=".csv,text/csv">
|
||
<label><?= __('import_paste') ?></label>
|
||
<textarea name="csv_text" placeholder="Gast Müller,1,480 Konferenzraum A,5,240"></textarea>
|
||
<button class="btn" type="submit" name="do_import" style="margin-top:14px;" onclick="return confirm('<?= __('import_confirm') ?>');"><?= __('import_submit') ?></button>
|
||
</form>
|
||
</div>
|
||
|
||
<?php if (!empty($results)): ?>
|
||
<div class="card">
|
||
<h2><?= __('import_result') ?></h2>
|
||
<table><tr><th><?= __('label_name') ?></th><th><?= __('import_col_code') ?></th><th><?= __('label_status') ?></th></tr>
|
||
<?php foreach ($results as $r): ?>
|
||
<tr><td><?= htmlspecialchars($r['name']) ?></td><td><code><?= htmlspecialchars($r['code']) ?></code></td><td><?= $r['ok'] ? '✅' : '❌' ?></td></tr>
|
||
<?php endforeach; ?>
|
||
</table>
|
||
</div>
|
||
<?php endif; ?>
|
||
|
||
</main>
|
||
<script src="../assets/global.js"></script>
|
||
</body>
|
||
</html>
|