175 lines
8.5 KiB
PHP
175 lines
8.5 KiB
PHP
<?php
|
||
require_once __DIR__ . '/../includes/bootstrap.php';
|
||
$staff = require_admin_or_permission('manage_banlist');
|
||
$pdo = get_db();
|
||
|
||
$errors = [];
|
||
|
||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||
verify_csrf();
|
||
$action = $_POST['action'] ?? '';
|
||
|
||
if ($action === 'create') {
|
||
$matchType = ($_POST['match_type'] ?? 'email') === 'domain' ? 'domain' : 'email';
|
||
$value = trim(strtolower($_POST['value'] ?? ''));
|
||
$reason = trim($_POST['reason'] ?? '');
|
||
if (!$value) { $errors[] = 'Bitte einen Wert angeben.'; }
|
||
elseif ($matchType === 'email' && !filter_var($value, FILTER_VALIDATE_EMAIL)) { $errors[] = 'Ungültige E-Mail-Adresse.'; }
|
||
elseif ($matchType === 'domain' && (!str_contains($value, '.') || str_contains($value, '@'))) { $errors[] = 'Nur Domain angeben, z.B. spam.de (ohne @).'; }
|
||
if (!$errors) {
|
||
$chk = $pdo->prepare('SELECT id FROM banlist WHERE value=?');
|
||
$chk->execute([$value]);
|
||
if (!$chk->fetchColumn()) {
|
||
$pdo->prepare('INSERT INTO banlist (match_type,value,reason,created_by,created_at) VALUES (?,?,?,?,?)')
|
||
->execute([$matchType, $value, $reason ?: null, $staff['id'], db_now()]);
|
||
// Tickets schließen
|
||
$col = $matchType === 'email' ? 'requester_email' : "" . sql_email_domain("requester_email") . "";
|
||
$priorStmt = $pdo->prepare("SELECT id,status FROM tickets WHERE $col=? AND status NOT IN ('closed')");
|
||
$priorStmt->execute([$value]); $priorTickets = $priorStmt->fetchAll();
|
||
$pdo->prepare("UPDATE tickets SET status='closed',resolution_code='spam',resolution_note='Im Rahmen einer Sperre geschlossen',closed_at=?,updated_at=? WHERE $col=? AND status NOT IN ('closed')")
|
||
->execute([db_now(), db_now(), $value]);
|
||
ticket_sla_transition_rows($pdo, $priorTickets, 'closed', (int) $staff['id']);
|
||
audit_log($staff['id'], "Sperrliste: $value hinzugefügt ($matchType)");
|
||
}
|
||
flash('success', "$value gesperrt.");
|
||
redirect('/staff/banlist.php');
|
||
}
|
||
}
|
||
|
||
if ($action === 'bulk_add') {
|
||
$raw = trim($_POST['bulk_values'] ?? '');
|
||
$reason = trim($_POST['bulk_reason'] ?? '');
|
||
$lines = array_filter(array_map('trim', preg_split('/[\r\n,;]+/', $raw)));
|
||
$added = 0; $skipped = 0;
|
||
foreach ($lines as $val) {
|
||
$val = strtolower($val);
|
||
$type = str_contains($val, '@') ? 'email' : 'domain';
|
||
if ($type === 'email' && !filter_var($val, FILTER_VALIDATE_EMAIL)) { $skipped++; continue; }
|
||
if ($type === 'domain' && !str_contains($val, '.')) { $skipped++; continue; }
|
||
$chk = $pdo->prepare('SELECT id FROM banlist WHERE value=?');
|
||
$chk->execute([$val]);
|
||
if ($chk->fetchColumn()) { $skipped++; continue; }
|
||
$pdo->prepare('INSERT INTO banlist (match_type,value,reason,created_by,created_at) VALUES (?,?,?,?,?)')
|
||
->execute([$type, $val, $reason ?: null, $staff['id'], db_now()]);
|
||
// Tickets schließen
|
||
$col = $type === 'email' ? 'requester_email' : "" . sql_email_domain("requester_email") . "";
|
||
$priorStmt = $pdo->prepare("SELECT id,status FROM tickets WHERE $col=? AND status NOT IN ('closed')");
|
||
$priorStmt->execute([$val]); $priorTickets = $priorStmt->fetchAll();
|
||
$pdo->prepare("UPDATE tickets SET status='closed',resolution_code='spam',resolution_note='Im Rahmen einer Sperre geschlossen',closed_at=?,updated_at=? WHERE $col=? AND status NOT IN ('closed')")
|
||
->execute([db_now(), db_now(), $val]);
|
||
ticket_sla_transition_rows($pdo, $priorTickets, 'closed', (int) $staff['id']);
|
||
$added++;
|
||
}
|
||
audit_log($staff['id'], "Sperrliste Massen-Import: $added hinzugefügt, $skipped übersprungen");
|
||
flash('success', "$added Einträge gesperrt, $skipped übersprungen.");
|
||
redirect('/staff/banlist.php');
|
||
}
|
||
|
||
if ($action === 'delete') {
|
||
$id = (int)$_POST['id'];
|
||
$val = $pdo->prepare('SELECT value FROM banlist WHERE id=?');
|
||
$val->execute([$id]);
|
||
$v = $val->fetchColumn();
|
||
$pdo->prepare('DELETE FROM banlist WHERE id=?')->execute([$id]);
|
||
audit_log($staff['id'], "Sperrliste: $v entfernt");
|
||
flash('success', "$v entsperrt.");
|
||
redirect('/staff/banlist.php');
|
||
}
|
||
}
|
||
|
||
$entries = $pdo->query(
|
||
'SELECT b.*, s.name AS added_by_name FROM banlist b LEFT JOIN staff s ON s.id = b.created_by ORDER BY b.created_at DESC'
|
||
)->fetchAll();
|
||
|
||
$pageTitle = 'Sperrliste';
|
||
$activePage = 'banlist';
|
||
require __DIR__ . '/_header.php';
|
||
?>
|
||
|
||
<div class="topbar">
|
||
<h1>Sperrliste</h1>
|
||
</div>
|
||
|
||
<p class="muted" style="margin-bottom:20px">E-Mail-Adressen oder Domains die keine Tickets mehr erstellen dürfen. Beim Sperren werden alle offenen Tickets automatisch geschlossen.</p>
|
||
|
||
<?php foreach ($errors as $err): ?>
|
||
<div class="flash error"><?= e($err) ?></div>
|
||
<?php endforeach; ?>
|
||
|
||
<div style="display:flex; gap:20px; align-items:flex-start; flex-wrap:wrap">
|
||
|
||
<!-- Liste -->
|
||
<div style="flex:2; min-width:400px">
|
||
<div class="card">
|
||
<h3>Gesperrte Einträge (<?= count($entries) ?>)</h3>
|
||
<table>
|
||
<thead><tr><th>Typ</th><th>Wert</th><th>Grund</th><th>Gesperrt am</th><th></th></tr></thead>
|
||
<tbody>
|
||
<?php foreach ($entries as $b): ?>
|
||
<tr>
|
||
<td><span class="badge" style="background:<?= $b['match_type']==='domain'?'#a855f7':'#64748b' ?>"><?= $b['match_type']==='domain'?'Domain':'E-Mail' ?></span></td>
|
||
<td style="font-weight:600"><?= e($b['value']) ?></td>
|
||
<td class="muted" style="font-size:.82rem"><?= e($b['reason'] ?: '–') ?></td>
|
||
<td class="muted" style="font-size:.78rem"><?= format_dt($b['created_at']) ?><?= $b['added_by_name'] ? '<br>'.e($b['added_by_name']) : '' ?></td>
|
||
<td>
|
||
<form method="post" onsubmit="return confirm('Wirklich entsperren?')">
|
||
<?= csrf_field() ?>
|
||
<input type="hidden" name="action" value="delete">
|
||
<input type="hidden" name="id" value="<?= $b['id'] ?>">
|
||
<button type="submit" style="background:none;border:none;color:#dc2626;cursor:pointer;font-size:.82rem">Entsperren</button>
|
||
</form>
|
||
</td>
|
||
</tr>
|
||
<?php endforeach; ?>
|
||
<?php if (!$entries): ?>
|
||
<tr><td colspan="5" class="muted">Sperrliste ist leer.</td></tr>
|
||
<?php endif; ?>
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Formulare -->
|
||
<div style="flex:1; min-width:280px; display:flex; flex-direction:column; gap:16px">
|
||
|
||
<!-- Einzeln -->
|
||
<div class="card">
|
||
<h3>Einzeln sperren</h3>
|
||
<form method="post">
|
||
<?= csrf_field() ?>
|
||
<input type="hidden" name="action" value="create">
|
||
<label>Typ</label>
|
||
<select name="match_type">
|
||
<option value="email">E-Mail-Adresse</option>
|
||
<option value="domain">Domain (alle @domain.de)</option>
|
||
</select>
|
||
<label style="margin-top:10px">E-Mail / Domain</label>
|
||
<input type="text" name="value" placeholder="spam@beispiel.de oder spam-domain.de" required>
|
||
<label style="margin-top:10px">Grund (optional)</label>
|
||
<input type="text" name="reason" placeholder="z.B. wiederholter Spam">
|
||
<div class="btn-row"><button type="submit" class="btn">Sperren</button></div>
|
||
</form>
|
||
</div>
|
||
|
||
<!-- Massen-Import -->
|
||
<div class="card">
|
||
<h3>Mehrere sperren</h3>
|
||
<p class="muted" style="font-size:.82rem; margin-bottom:12px">Eine E-Mail oder Domain pro Zeile (oder kommagetrennt). Typ wird automatisch erkannt.</p>
|
||
<form method="post">
|
||
<?= csrf_field() ?>
|
||
<input type="hidden" name="action" value="bulk_add">
|
||
<label>E-Mails / Domains</label>
|
||
<textarea name="bulk_values" rows="8" placeholder="spam1@beispiel.de spam2@beispiel.de spam-domain.de ..." required style="font-family:monospace;font-size:.82rem"></textarea>
|
||
<label style="margin-top:10px">Grund für alle (optional)</label>
|
||
<input type="text" name="bulk_reason" placeholder="z.B. Import aus alter Sperrliste">
|
||
<div class="btn-row">
|
||
<button type="submit" class="btn" onclick="return confirm('Alle eingegebenen Adressen sperren und Tickets schließen?')">Alle sperren</button>
|
||
</div>
|
||
</form>
|
||
</div>
|
||
|
||
</div>
|
||
</div>
|
||
|
||
<?php require __DIR__ . '/_footer.php'; ?>
|