421 lines
19 KiB
PHP
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 © <?= 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>
|