2026-02-19 15:58:15 +01:00

421 lines
19 KiB
PHP

<?php
/**
* User Management - Admin Only
* Telvero Talpa Planning System
*/
ini_set('display_errors', 1);
ini_set('display_startup_errors', 1);
error_reporting(E_ALL);
require_once __DIR__ . '/../vendor/autoload.php';
use Dotenv\Dotenv;
$dotenv = Dotenv::createImmutable(__DIR__ . '/..');
$dotenv->load();
require_once __DIR__ . '/../auth/auth_functions.php';
// Only admins can access this page
requireAdmin();
$db = getAuthDb();
$success = '';
$error = '';
// Handle create user
if (isset($_POST['create_user'])) {
$username = trim($_POST['username'] ?? '');
$email = trim($_POST['email'] ?? '');
$password = $_POST['password'] ?? '';
$role = $_POST['role'] ?? 'guest';
if (empty($username) || empty($email) || empty($password)) {
$error = 'Vul alle verplichte velden in.';
} elseif (strlen($password) < 8) {
$error = 'Wachtwoord moet minimaal 8 tekens lang zijn.';
} elseif (!in_array($role, ['admin', 'guest'])) {
$error = 'Ongeldige rol geselecteerd.';
} else {
try {
$passwordHash = password_hash($password, PASSWORD_BCRYPT);
$stmt = $db->prepare("
INSERT INTO users (username, email, password_hash, role, is_active)
VALUES (?, ?, ?, ?, 1)
");
$stmt->execute([$username, $email, $passwordHash, $role]);
$success = "Gebruiker '{$username}' succesvol aangemaakt.";
} catch (PDOException $e) {
if ($e->getCode() == 23000) {
$error = 'Gebruikersnaam of email is al in gebruik.';
} else {
$error = 'Er is een fout opgetreden bij het aanmaken van de gebruiker.';
}
}
}
}
// Handle update user
if (isset($_POST['update_user'])) {
$userId = (int)($_POST['user_id'] ?? 0);
$username = trim($_POST['username'] ?? '');
$email = trim($_POST['email'] ?? '');
$role = $_POST['role'] ?? 'guest';
$isActive = isset($_POST['is_active']) ? 1 : 0;
$newPassword = $_POST['new_password'] ?? '';
if (empty($username) || empty($email)) {
$error = 'Vul alle verplichte velden in.';
} elseif (!in_array($role, ['admin', 'guest'])) {
$error = 'Ongeldige rol geselecteerd.';
} else {
try {
if (!empty($newPassword)) {
if (strlen($newPassword) < 8) {
$error = 'Nieuw wachtwoord moet minimaal 8 tekens lang zijn.';
} else {
$passwordHash = password_hash($newPassword, PASSWORD_BCRYPT);
$stmt = $db->prepare("
UPDATE users
SET username = ?, email = ?, password_hash = ?, role = ?, is_active = ?
WHERE id = ?
");
$stmt->execute([$username, $email, $passwordHash, $role, $isActive, $userId]);
$success = "Gebruiker succesvol bijgewerkt (inclusief wachtwoord).";
}
} else {
$stmt = $db->prepare("
UPDATE users
SET username = ?, email = ?, role = ?, is_active = ?
WHERE id = ?
");
$stmt->execute([$username, $email, $role, $isActive, $userId]);
$success = "Gebruiker succesvol bijgewerkt.";
}
} catch (PDOException $e) {
if ($e->getCode() == 23000) {
$error = 'Gebruikersnaam of email is al in gebruik.';
} else {
$error = 'Er is een fout opgetreden bij het bijwerken van de gebruiker.';
}
}
}
}
// Handle delete user
if (isset($_POST['delete_user'])) {
$userId = (int)($_POST['user_id'] ?? 0);
$currentUserId = getCurrentUser()['id'];
if ($userId === $currentUserId) {
$error = 'Je kunt je eigen account niet verwijderen.';
} else {
try {
$stmt = $db->prepare("DELETE FROM users WHERE id = ?");
$stmt->execute([$userId]);
$success = "Gebruiker succesvol verwijderd.";
} catch (PDOException $e) {
$error = 'Er is een fout opgetreden bij het verwijderen van de gebruiker.';
}
}
}
// Get all users
$users = $db->query("
SELECT id, username, email, role, is_active, last_login, created_at
FROM users
ORDER BY created_at DESC
")->fetchAll();
// Get login stats
$loginStats = $db->query("
SELECT
COUNT(*) as total_attempts,
SUM(success) as successful_logins,
COUNT(*) - SUM(success) as failed_logins
FROM login_attempts
WHERE attempted_at > DATE_SUB(NOW(), INTERVAL 24 HOUR)
")->fetch();
$activePage = 'users';
?>
<!DOCTYPE html>
<html lang="nl">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Gebruikersbeheer - Telvero Talpa</title>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.0/font/bootstrap-icons.css">
<link rel="stylesheet" href="/assets/css/custom.css">
</head>
<body class="bg-light">
<?php include __DIR__ . '/../includes/nav.php'; ?>
<div class="container mt-4">
<div class="d-flex justify-content-between align-items-center mb-4">
<h1><i class="bi bi-people"></i> Gebruikersbeheer</h1>
<button type="button" class="btn btn-primary" data-bs-toggle="modal" data-bs-target="#createUserModal">
<i class="bi bi-plus-circle"></i> Nieuwe Gebruiker
</button>
</div>
<?php if (!empty($success)): ?>
<div class="alert alert-success alert-dismissible fade show" role="alert">
<i class="bi bi-check-circle"></i> <?= htmlspecialchars($success) ?>
<button type="button" class="btn-close" data-bs-dismiss="alert"></button>
</div>
<?php endif; ?>
<?php if (!empty($error)): ?>
<div class="alert alert-danger alert-dismissible fade show" role="alert">
<i class="bi bi-exclamation-triangle"></i> <?= htmlspecialchars($error) ?>
<button type="button" class="btn-close" data-bs-dismiss="alert"></button>
</div>
<?php endif; ?>
<!-- Login Stats -->
<div class="row mb-4">
<div class="col-md-4">
<div class="card shadow-sm text-center">
<div class="card-body">
<div class="fs-2 fw-bold text-primary"><?= count($users) ?></div>
<div class="text-muted small">Totaal Gebruikers</div>
</div>
</div>
</div>
<div class="col-md-4">
<div class="card shadow-sm text-center">
<div class="card-body">
<div class="fs-2 fw-bold text-success"><?= $loginStats['successful_logins'] ?? 0 ?></div>
<div class="text-muted small">Succesvolle Logins (24u)</div>
</div>
</div>
</div>
<div class="col-md-4">
<div class="card shadow-sm text-center">
<div class="card-body">
<div class="fs-2 fw-bold text-danger"><?= $loginStats['failed_logins'] ?? 0 ?></div>
<div class="text-muted small">Mislukte Logins (24u)</div>
</div>
</div>
</div>
</div>
<!-- Users Table -->
<div class="card shadow-sm">
<div class="card-header bg-dark text-white">
<h5 class="mb-0"><i class="bi bi-table"></i> Gebruikers</h5>
</div>
<div class="card-body p-0">
<table class="table table-hover mb-0">
<thead class="table-secondary">
<tr>
<th>ID</th>
<th>Gebruikersnaam</th>
<th>Email</th>
<th>Rol</th>
<th>Status</th>
<th>Laatste Login</th>
<th>Aangemaakt</th>
<th class="text-center">Acties</th>
</tr>
</thead>
<tbody>
<?php foreach ($users as $user): ?>
<tr>
<td class="text-muted small"><?= $user['id'] ?></td>
<td>
<i class="bi bi-person-circle me-1"></i>
<strong><?= htmlspecialchars($user['username']) ?></strong>
<?php if ($user['id'] === getCurrentUser()['id']): ?>
<span class="badge bg-info ms-1">Jij</span>
<?php endif; ?>
</td>
<td><?= htmlspecialchars($user['email']) ?></td>
<td>
<span class="badge <?= $user['role'] === 'admin' ? 'bg-danger' : 'bg-secondary' ?>">
<i class="bi <?= $user['role'] === 'admin' ? 'bi-shield-fill' : 'bi-eye' ?>"></i>
<?= ucfirst($user['role']) ?>
</span>
</td>
<td>
<span class="badge <?= $user['is_active'] ? 'bg-success' : 'bg-warning text-dark' ?>">
<?= $user['is_active'] ? 'Actief' : 'Inactief' ?>
</span>
</td>
<td class="small text-muted">
<?= $user['last_login']
? date('d-m-Y H:i', strtotime($user['last_login']))
: '<em>Nooit</em>' ?>
</td>
<td class="small text-muted">
<?= date('d-m-Y', strtotime($user['created_at'])) ?>
</td>
<td class="text-center">
<button type="button"
class="btn btn-sm btn-outline-primary"
onclick="editUser(<?= htmlspecialchars(json_encode($user)) ?>)"
title="Bewerken">
<i class="bi bi-pencil"></i>
</button>
<?php if ($user['id'] !== getCurrentUser()['id']): ?>
<button type="button"
class="btn btn-sm btn-outline-danger ms-1"
onclick="confirmDelete(<?= $user['id'] ?>, '<?= htmlspecialchars($user['username']) ?>')"
title="Verwijderen">
<i class="bi bi-trash"></i>
</button>
<?php endif; ?>
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</div>
</div>
</div>
<!-- Create User Modal -->
<div class="modal fade" id="createUserModal" tabindex="-1">
<div class="modal-dialog">
<div class="modal-content">
<form method="POST">
<div class="modal-header bg-primary text-white">
<h5 class="modal-title">
<i class="bi bi-person-plus"></i> Nieuwe Gebruiker Aanmaken
</h5>
<button type="button" class="btn-close btn-close-white" data-bs-dismiss="modal"></button>
</div>
<div class="modal-body">
<div class="mb-3">
<label class="form-label fw-semibold">Gebruikersnaam <span class="text-danger">*</span></label>
<input type="text" name="username" class="form-control" required
pattern="[a-zA-Z0-9_-]{3,50}"
title="3-50 tekens, alleen letters, cijfers, _ en -">
<div class="form-text">3-50 tekens, alleen letters, cijfers, _ en -</div>
</div>
<div class="mb-3">
<label class="form-label fw-semibold">Email <span class="text-danger">*</span></label>
<input type="email" name="email" class="form-control" required>
</div>
<div class="mb-3">
<label class="form-label fw-semibold">Wachtwoord <span class="text-danger">*</span></label>
<input type="password" name="password" class="form-control" required minlength="8">
<div class="form-text">Minimaal 8 tekens</div>
</div>
<div class="mb-3">
<label class="form-label fw-semibold">Rol <span class="text-danger">*</span></label>
<select name="role" class="form-select" required>
<option value="guest">Guest (alleen lezen)</option>
<option value="admin">Admin (volledige toegang)</option>
</select>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Annuleren</button>
<button type="submit" name="create_user" class="btn btn-primary">
<i class="bi bi-person-plus"></i> Aanmaken
</button>
</div>
</form>
</div>
</div>
</div>
<!-- Edit User Modal -->
<div class="modal fade" id="editUserModal" tabindex="-1">
<div class="modal-dialog">
<div class="modal-content">
<form method="POST">
<input type="hidden" name="user_id" id="editUserId">
<div class="modal-header bg-warning">
<h5 class="modal-title">
<i class="bi bi-pencil"></i> Gebruiker Bewerken
</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
</div>
<div class="modal-body">
<div class="mb-3">
<label class="form-label fw-semibold">Gebruikersnaam <span class="text-danger">*</span></label>
<input type="text" name="username" id="editUsername" class="form-control" required
pattern="[a-zA-Z0-9_-]{3,50}">
</div>
<div class="mb-3">
<label class="form-label fw-semibold">Email <span class="text-danger">*</span></label>
<input type="email" name="email" id="editEmail" class="form-control" required>
</div>
<div class="mb-3">
<label class="form-label fw-semibold">Rol <span class="text-danger">*</span></label>
<select name="role" id="editRole" class="form-select" required>
<option value="guest">Guest (alleen lezen)</option>
<option value="admin">Admin (volledige toegang)</option>
</select>
</div>
<div class="mb-3">
<div class="form-check">
<input class="form-check-input" type="checkbox" name="is_active" id="editIsActive" value="1">
<label class="form-check-label" for="editIsActive">
Account actief
</label>
</div>
</div>
<hr>
<div class="mb-3">
<label class="form-label fw-semibold">Nieuw Wachtwoord</label>
<input type="password" name="new_password" id="editPassword" class="form-control" minlength="8">
<div class="form-text text-muted">
<i class="bi bi-info-circle"></i>
Laat leeg om het huidige wachtwoord te behouden.
</div>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Annuleren</button>
<button type="submit" name="update_user" class="btn btn-warning">
<i class="bi bi-check-circle"></i> Opslaan
</button>
</div>
</form>
</div>
</div>
</div>
<!-- Delete User Form (hidden) -->
<form method="POST" id="deleteUserForm" style="display: none;">
<input type="hidden" name="user_id" id="deleteUserId">
<input type="hidden" name="delete_user" value="1">
</form>
<footer class="mt-5 py-4 bg-dark text-white text-center">
<div class="container">
<p class="mb-0">
<i class="bi bi-tv"></i> Telvero Talpa Planning System &copy; <?= date('Y') ?>
</p>
</div>
</footer>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
<script>
function editUser(user) {
document.getElementById('editUserId').value = user.id;
document.getElementById('editUsername').value = user.username;
document.getElementById('editEmail').value = user.email;
document.getElementById('editRole').value = user.role;
document.getElementById('editIsActive').checked = user.is_active == 1;
document.getElementById('editPassword').value = '';
const modal = new bootstrap.Modal(document.getElementById('editUserModal'));
modal.show();
}
function confirmDelete(userId, username) {
if (confirm(`Weet je zeker dat je gebruiker "${username}" wilt verwijderen?\n\nDeze actie kan niet ongedaan worden gemaakt.`)) {
document.getElementById('deleteUserId').value = userId;
document.getElementById('deleteUserForm').submit();
}
}
</script>
</body>
</html>