feat: rôles admin/user, page Utilisateurs, lien Vision dans navbar, filtre courriers par compte
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -3,7 +3,7 @@ require_once __DIR__ . '/../includes/db.php';
|
||||
require_once __DIR__ . '/../includes/auth.php';
|
||||
require_once __DIR__ . '/../includes/layout.php';
|
||||
|
||||
auth_check();
|
||||
admin_check();
|
||||
|
||||
$error = '';
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@ require_once __DIR__ . '/../includes/db.php';
|
||||
require_once __DIR__ . '/../includes/auth.php';
|
||||
require_once __DIR__ . '/../includes/layout.php';
|
||||
|
||||
auth_check();
|
||||
admin_check();
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
$fields = ['domain', 'hostname', 'smtp_port', 'submission_port', 'imap_port', 'imaps_port', 'smb_workgroup'];
|
||||
|
||||
@@ -3,7 +3,7 @@ require_once __DIR__ . '/../includes/db.php';
|
||||
require_once __DIR__ . '/../includes/auth.php';
|
||||
require_once __DIR__ . '/../includes/layout.php';
|
||||
|
||||
auth_check();
|
||||
admin_check();
|
||||
|
||||
$services = [
|
||||
'postfix' => ['Postfix (SMTP)', 'bi-envelope-at'],
|
||||
|
||||
232
web/admin/users.php
Normal file
232
web/admin/users.php
Normal file
@@ -0,0 +1,232 @@
|
||||
<?php
|
||||
require_once __DIR__ . '/../includes/db.php';
|
||||
require_once __DIR__ . '/../includes/auth.php';
|
||||
require_once __DIR__ . '/../includes/layout.php';
|
||||
|
||||
admin_check();
|
||||
|
||||
$error = '';
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
|
||||
if ($_POST['action'] === 'create') {
|
||||
$username = trim($_POST['username'] ?? '');
|
||||
$password = trim($_POST['password'] ?? '');
|
||||
$role = $_POST['role'] === 'admin' ? 'admin' : 'user';
|
||||
$account_id = ($role === 'user' && !empty($_POST['account_id'])) ? (int)$_POST['account_id'] : null;
|
||||
|
||||
if (strlen($username) < 2) {
|
||||
$error = 'Nom d\'utilisateur trop court.';
|
||||
} elseif (strlen($password) < 6) {
|
||||
$error = 'Mot de passe trop court (min 6 caractères).';
|
||||
} else {
|
||||
$hash = password_hash($password, PASSWORD_BCRYPT, ['cost' => 12]);
|
||||
try {
|
||||
db()->prepare('INSERT INTO admin_users (username, role, account_id, password) VALUES (?, ?, ?, ?)')
|
||||
->execute([$username, $role, $account_id, $hash]);
|
||||
flash('success', "Utilisateur <strong>{$username}</strong> créé.");
|
||||
} catch (Exception $e) {
|
||||
$error = "L'utilisateur « {$username} » existe déjà.";
|
||||
}
|
||||
}
|
||||
if (!$error) { header('Location: /admin/users.php'); exit; }
|
||||
}
|
||||
|
||||
if ($_POST['action'] === 'delete') {
|
||||
$id = (int)$_POST['user_id'];
|
||||
if ($id === (int)$_SESSION['admin_id']) {
|
||||
flash('danger', 'Impossible de supprimer votre propre compte.');
|
||||
} else {
|
||||
db()->prepare('DELETE FROM admin_users WHERE id = ?')->execute([$id]);
|
||||
flash('success', 'Utilisateur supprimé.');
|
||||
}
|
||||
header('Location: /admin/users.php'); exit;
|
||||
}
|
||||
|
||||
if ($_POST['action'] === 'edit') {
|
||||
$id = (int)$_POST['user_id'];
|
||||
$role = $_POST['role'] === 'admin' ? 'admin' : 'user';
|
||||
$account_id = ($role === 'user' && !empty($_POST['account_id'])) ? (int)$_POST['account_id'] : null;
|
||||
$newPassword = trim($_POST['new_password'] ?? '');
|
||||
|
||||
db()->prepare('UPDATE admin_users SET role = ?, account_id = ? WHERE id = ?')
|
||||
->execute([$role, $account_id, $id]);
|
||||
|
||||
if (strlen($newPassword) >= 6) {
|
||||
$hash = password_hash($newPassword, PASSWORD_BCRYPT, ['cost' => 12]);
|
||||
db()->prepare('UPDATE admin_users SET password = ? WHERE id = ?')->execute([$hash, $id]);
|
||||
}
|
||||
flash('success', 'Utilisateur mis à jour.');
|
||||
header('Location: /admin/users.php'); exit;
|
||||
}
|
||||
}
|
||||
|
||||
$users = db()->query('SELECT u.*, a.username as account_name FROM admin_users u LEFT JOIN accounts a ON u.account_id = a.id ORDER BY u.role, u.username')->fetchAll();
|
||||
$accounts = db()->query('SELECT id, username, email FROM accounts WHERE active = 1 ORDER BY username')->fetchAll();
|
||||
|
||||
html_head('Utilisateurs');
|
||||
html_navbar('users');
|
||||
?>
|
||||
<div class="container-fluid">
|
||||
<?php flash_render(); ?>
|
||||
<div class="row g-4">
|
||||
|
||||
<!-- Formulaire création -->
|
||||
<div class="col-lg-4">
|
||||
<div class="card shadow-sm">
|
||||
<div class="card-header fw-semibold"><i class="bi bi-person-plus-fill me-2"></i>Nouvel utilisateur</div>
|
||||
<div class="card-body">
|
||||
<?php if ($error): ?>
|
||||
<div class="alert alert-danger py-2 small"><?= htmlspecialchars($error) ?></div>
|
||||
<?php endif; ?>
|
||||
<form method="post" id="create-form">
|
||||
<input type="hidden" name="action" value="create">
|
||||
<div class="mb-3">
|
||||
<label class="form-label">Nom d'utilisateur</label>
|
||||
<input type="text" name="username" class="form-control" required>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label">Mot de passe</label>
|
||||
<input type="password" name="password" class="form-control" required minlength="6">
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label">Rôle</label>
|
||||
<select name="role" class="form-select" onchange="toggleAccount(this,'create-account')">
|
||||
<option value="user">Utilisateur — Courriers uniquement</option>
|
||||
<option value="admin">Administrateur — Accès complet</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="mb-3" id="create-account">
|
||||
<label class="form-label">Compte mail associé <span class="text-muted">(optionnel)</span></label>
|
||||
<select name="account_id" class="form-select">
|
||||
<option value="">— Tous les courriers —</option>
|
||||
<?php foreach ($accounts as $acc): ?>
|
||||
<option value="<?= $acc['id'] ?>"><?= htmlspecialchars($acc['username']) ?> — <?= htmlspecialchars($acc['email']) ?></option>
|
||||
<?php endforeach; ?>
|
||||
</select>
|
||||
<div class="form-text">Si défini, l'utilisateur ne voit que les courriers de ce compte.</div>
|
||||
</div>
|
||||
<button type="submit" class="btn btn-primary w-100">
|
||||
<i class="bi bi-plus-circle me-1"></i>Créer
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Liste -->
|
||||
<div class="col-lg-8">
|
||||
<div class="card shadow-sm">
|
||||
<div class="card-header d-flex justify-content-between align-items-center">
|
||||
<span><i class="bi bi-people-fill me-2"></i>Utilisateurs</span>
|
||||
<span class="badge bg-secondary"><?= count($users) ?></span>
|
||||
</div>
|
||||
<div class="table-responsive">
|
||||
<table class="table table-hover align-middle mb-0">
|
||||
<thead class="table-light">
|
||||
<tr>
|
||||
<th>Utilisateur</th>
|
||||
<th>Rôle</th>
|
||||
<th>Compte associé</th>
|
||||
<th class="text-center">Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php foreach ($users as $u): ?>
|
||||
<tr>
|
||||
<td><i class="bi bi-person-circle me-1 text-muted"></i><?= htmlspecialchars($u['username']) ?></td>
|
||||
<td>
|
||||
<?php if ($u['role'] === 'admin'): ?>
|
||||
<span class="badge bg-warning text-dark">admin</span>
|
||||
<?php else: ?>
|
||||
<span class="badge bg-secondary">user</span>
|
||||
<?php endif; ?>
|
||||
</td>
|
||||
<td class="small text-muted">
|
||||
<?= $u['account_name'] ? htmlspecialchars($u['account_name']) : '<span class="text-muted">tous</span>' ?>
|
||||
</td>
|
||||
<td class="text-center">
|
||||
<div class="d-flex gap-1 justify-content-center">
|
||||
<button class="btn btn-sm btn-outline-secondary" title="Modifier"
|
||||
onclick="openEditUser(<?= $u['id'] ?>,'<?= addslashes($u['role']) ?>',<?= $u['account_id'] ?? 'null' ?>)">
|
||||
<i class="bi bi-pencil"></i>
|
||||
</button>
|
||||
<?php if ($u['id'] != $_SESSION['admin_id']): ?>
|
||||
<form method="post" onsubmit="return confirm('Supprimer cet utilisateur ?')">
|
||||
<input type="hidden" name="action" value="delete">
|
||||
<input type="hidden" name="user_id" value="<?= $u['id'] ?>">
|
||||
<button class="btn btn-sm btn-outline-danger"><i class="bi bi-trash"></i></button>
|
||||
</form>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Modal édition -->
|
||||
<div class="modal fade" id="editUserModal" tabindex="-1">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<form method="post">
|
||||
<input type="hidden" name="action" value="edit">
|
||||
<input type="hidden" name="user_id" id="eu-id">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title"><i class="bi bi-pencil me-2"></i>Modifier l'utilisateur</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">Rôle</label>
|
||||
<select name="role" id="eu-role" class="form-select" onchange="toggleAccount(this,'eu-account')">
|
||||
<option value="user">Utilisateur — Courriers uniquement</option>
|
||||
<option value="admin">Administrateur — Accès complet</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="mb-3" id="eu-account">
|
||||
<label class="form-label">Compte mail associé</label>
|
||||
<select name="account_id" id="eu-account-sel" class="form-select">
|
||||
<option value="">— Tous les courriers —</option>
|
||||
<?php foreach ($accounts as $acc): ?>
|
||||
<option value="<?= $acc['id'] ?>"><?= htmlspecialchars($acc['username']) ?></option>
|
||||
<?php endforeach; ?>
|
||||
</select>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label">Nouveau mot de passe <span class="text-muted">(laisser vide)</span></label>
|
||||
<input type="password" name="new_password" class="form-control" minlength="6">
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-outline-secondary" data-bs-dismiss="modal">Annuler</button>
|
||||
<button type="submit" class="btn btn-primary"><i class="bi bi-floppy-fill me-1"></i>Enregistrer</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
function toggleAccount(sel, containerId) {
|
||||
document.getElementById(containerId).style.display = sel.value === 'user' ? '' : 'none';
|
||||
}
|
||||
// Init au chargement
|
||||
toggleAccount(document.querySelector('#create-form [name=role]'), 'create-account');
|
||||
|
||||
function openEditUser(id, role, accountId) {
|
||||
document.getElementById('eu-id').value = id;
|
||||
const roleSel = document.getElementById('eu-role');
|
||||
roleSel.value = role;
|
||||
toggleAccount(roleSel, 'eu-account');
|
||||
const accSel = document.getElementById('eu-account-sel');
|
||||
accSel.value = accountId || '';
|
||||
new bootstrap.Modal(document.getElementById('editUserModal')).show();
|
||||
}
|
||||
</script>
|
||||
<?php html_foot(); ?>
|
||||
Reference in New Issue
Block a user