Files
deskhub/DeskHub-Webseite/staff/customers.php
T
2026-10-16 19:12:21 +00:00

216 lines
9.8 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?php
require_once __DIR__ . '/../includes/bootstrap.php';
$staff = require_staff_login();
$pdo = get_db();
$errors = [];
$organizations = $pdo->query('SELECT * FROM organizations ORDER BY name')->fetchAll();
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
verify_csrf();
$action = $_POST['action'] ?? '';
if ($action === 'assign_org' && $staff['role'] === 'admin') {
$userId = (int) $_POST['user_id'];
$orgId = $_POST['organization_id'] !== '' ? (int) $_POST['organization_id'] : null;
$pdo->prepare('UPDATE users SET organization_id = ? WHERE id = ?')->execute([$orgId, $userId]);
audit_log($staff['id'], "Organisation zugeordnet für Kunde #{$userId}");
flash('success', 'Organisation zugeordnet.');
redirect('/staff/customers.php' . ($_SERVER['QUERY_STRING'] ? '?' . $_SERVER['QUERY_STRING'] : ''));
}
if ($action === 'ban_email') {
$email = trim($_POST['email'] ?? '');
$matchType = ($_POST['match_type'] ?? 'email') === 'domain' ? 'domain' : 'email';
$value = $matchType === 'domain' ? (substr(strrchr($email, '@'), 1) ?: $email) : $email;
if ($value !== '') {
// Nur einfügen wenn noch nicht vorhanden
$exists = $pdo->prepare('SELECT COUNT(*) FROM banlist WHERE match_type=? AND LOWER(value)=?');
$exists->execute([$matchType, strtolower($value)]);
if (!(int)$exists->fetchColumn()) {
$pdo->prepare('INSERT INTO banlist (match_type, value, reason, created_by, created_at) VALUES (?,?,?,?,?)')
->execute([$matchType, strtolower($value), 'Gesperrt aus Kundenverwaltung', $staff['id'], db_now()]);
audit_log($staff['id'], "Kunde gesperrt: {$value} ({$matchType})");
flash('success', '"' . $value . '" wurde zur Sperrliste hinzugefuegt.');
} else {
flash('info', '"' . $value . '" ist bereits auf der Sperrliste.');
}
}
redirect('/staff/customers.php' . ($_SERVER['QUERY_STRING'] ? '?' . $_SERVER['QUERY_STRING'] : ''));
}
if ($action === 'unban_email') {
$email = strtolower(trim($_POST['email'] ?? ''));
$domain = substr(strrchr($email, '@'), 1) ?: '';
$pdo->prepare('DELETE FROM banlist WHERE (match_type=\'email\' AND LOWER(value)=?) OR (match_type=\'domain\' AND LOWER(value)=?)')
->execute([$email, $domain]);
audit_log($staff['id'], "Kundensperrung aufgehoben: {$email}");
flash('success', "Sperrung aufgehoben fuer: {$email}");
}
}
$search = trim($_GET['q'] ?? '');
$like = '%' . $search . '%';
// Registrierte Kunden
$regSql = "SELECT u.*, o.name AS organization_name,
(SELECT COUNT(*) FROM tickets t WHERE t.user_id = u.id) AS ticket_count,
(SELECT MAX(t2.requester_ip) FROM tickets t2 WHERE t2.user_id = u.id AND t2.requester_ip IS NOT NULL) AS last_ip
FROM users u LEFT JOIN organizations o ON o.id = u.organization_id";
$regParams = [];
if ($search !== '') {
$regSql .= ' WHERE u.name LIKE ? OR u.email LIKE ?';
$regParams = [$like, $like];
}
$regSql .= ' ORDER BY u.name LIMIT 200';
$regStmt = $pdo->prepare($regSql);
$regStmt->execute($regParams);
$registered = array_map(fn($c) => array_merge($c, ['is_guest' => false]), $regStmt->fetchAll());
// Gast-Kunden
$guestSql = "SELECT requester_email AS email, MAX(requester_name) AS name,
COUNT(*) AS ticket_count, MIN(created_at) AS created_at,
MAX(requester_ip) AS last_ip
FROM tickets WHERE user_id IS NULL";
$guestParams = [];
if ($search !== '') {
$guestSql .= ' AND (requester_name LIKE ? OR requester_email LIKE ?)';
$guestParams = [$like, $like];
}
$guestSql .= ' GROUP BY requester_email ORDER BY requester_email LIMIT 200';
$guestStmt = $pdo->prepare($guestSql);
$guestStmt->execute($guestParams);
$guests = array_map(fn($g) => array_merge($g, [
'id' => null, 'organization_id' => null, 'organization_name' => null, 'is_guest' => true,
]), $guestStmt->fetchAll());
$customers = array_slice(
usort_return(array_merge($registered, $guests), fn($a, $b) => strcasecmp($a['name'] ?? '', $b['name'] ?? '')),
0, 200
);
// Alle aktuell gesperrten E-Mails/Domains aus der Sperrliste vorausberechnen
$bannedEmails = $pdo->query("SELECT LOWER(value) FROM banlist WHERE match_type='email'")->fetchAll(PDO::FETCH_COLUMN);
$bannedDomains = $pdo->query("SELECT LOWER(value) FROM banlist WHERE match_type='domain'")->fetchAll(PDO::FETCH_COLUMN);
$bannedEmailSet = array_flip($bannedEmails);
$bannedDomainSet = array_flip($bannedDomains);
function is_customer_banned(string $email, array $bannedEmailSet, array $bannedDomainSet): bool
{
$email = strtolower($email);
$domain = substr(strrchr($email, '@'), 1) ?: '';
return isset($bannedEmailSet[$email]) || isset($bannedDomainSet[$domain]);
}
function usort_return(array $arr, callable $fn): array { usort($arr, $fn); return $arr; }
$pageTitle = 'Kunden';
$activePage = 'customers';
require __DIR__ . '/_header.php';
?>
<h1>Kunden</h1>
<p class="muted">Registrierte Kundenkonten und Gast-Anfrager (ohne Konto) aus allen Tickets.</p>
<form method="get" class="filters card">
<div style="flex:2">
<label>Suche</label>
<input type="search" name="q" value="<?= e($search) ?>" placeholder="Name oder E-Mail">
</div>
<div><button type="submit" class="btn secondary">Suchen</button></div>
<?php if ($search): ?>
<div><a href="/staff/customers.php" class="btn secondary">Zurücksetzen</a></div>
<?php endif; ?>
</form>
<div class="card">
<table>
<thead>
<tr>
<th>Name</th>
<th>E-Mail</th>
<th>IP-Adresse</th>
<th>Typ</th>
<th>Organisation</th>
<th>Tickets</th>
<th>Seit</th>
<th>Status</th>
<th></th>
</tr>
</thead>
<tbody>
<?php foreach ($customers as $c):
$banned = is_customer_banned($c['email'], $bannedEmailSet, $bannedDomainSet);
?>
<tr style="<?= $banned ? 'opacity:.65' : '' ?>">
<td style="font-weight:500"><a href="/staff/customer_profile.php?<?= $c['is_guest'] ? 'email=' . urlencode($c['email']) : 'id=' . (int) $c['id'] ?>"><?= e($c['name']) ?></a></td>
<td><?= e($c['email']) ?></td>
<td class="muted" style="font-family:var(--font-mono); font-size:.8rem">
<?= $c['last_ip'] ? e($c['last_ip']) : '<span style="color:var(--muted-lt)">–</span>' ?>
</td>
<td>
<?= $c['is_guest']
? '<span class="badge" style="background:#64748b">Gast</span>'
: '<span class="badge" style="background:#0f6f6a">Konto</span>' ?>
</td>
<td>
<?php if ($c['is_guest']): ?>
<span class="muted" style="font-size:.82rem">– (kein Konto)</span>
<?php else: ?>
<form method="post" style="display:flex; gap:6px; align-items:center">
<?= csrf_field() ?>
<input type="hidden" name="action" value="assign_org">
<input type="hidden" name="user_id" value="<?= $c['id'] ?>">
<select name="organization_id" onchange="this.form.submit()" style="padding:4px 8px; font-size:.82rem">
<option value="">– keine –</option>
<?php foreach ($organizations as $o): ?>
<option value="<?= $o['id'] ?>" <?= $c['organization_id'] == $o['id'] ? 'selected' : '' ?>><?= e($o['name']) ?></option>
<?php endforeach; ?>
</select>
</form>
<?php endif; ?>
</td>
<td>
<?php if ((int)$c['ticket_count'] > 0): ?>
<a href="/staff/tickets.php?q=<?= urlencode($c['email']) ?>" style="font-weight:600"><?= (int)$c['ticket_count'] ?></a>
<?php else: ?>
<span class="muted">0</span>
<?php endif; ?>
</td>
<td class="muted" style="white-space:nowrap"><?= format_dt($c['created_at']) ?></td>
<td>
<?php if ($banned): ?>
<span class="badge" style="background:var(--danger)">Gesperrt</span>
<?php else: ?>
<span class="badge" style="background:var(--success)">Aktiv</span>
<?php endif; ?>
</td>
<td style="white-space:nowrap">
<?php if ($banned): ?>
<form method="post" style="display:inline" onsubmit="return confirm('<?= e(addslashes($c['email'])) ?> wirklich aufheben?');">
<?= csrf_field() ?>
<input type="hidden" name="action" value="unban_email">
<input type="hidden" name="email" value="<?= e($c['email']) ?>">
<button type="submit" class="btn secondary" style="font-size:.78rem; padding:5px 10px; color:var(--success); border-color:var(--success)">✓ Entsperren</button>
</form>
<?php else: ?>
<form method="post" style="display:inline" onsubmit="return confirm('<?= e(addslashes($c['name'])) ?>" sperren? Zukünftige Tickets von <?= e(addslashes($c['email'])) ?> werden abgewiesen.');">
<?= csrf_field() ?>
<input type="hidden" name="action" value="ban_email">
<input type="hidden" name="email" value="<?= e($c['email']) ?>">
<input type="hidden" name="match_type" value="email">
<button type="submit" class="btn secondary" style="font-size:.78rem; padding:5px 10px; color:var(--danger); border-color:var(--danger)">🚫 Sperren</button>
</form>
<?php endif; ?>
</td>
</tr>
<?php endforeach; ?>
<?php if (!$customers): ?>
<tr><td colspan="9" class="muted" style="text-align:center; padding:30px">Keine Kunden gefunden.</td></tr>
<?php endif; ?>
</tbody>
</table>
</div>
<?php require __DIR__ . '/_footer.php'; ?>