pagination curseur, layout 3 colonnes article, sidebar fixe
This commit is contained in:
@@ -0,0 +1,394 @@
|
||||
<?php
|
||||
ob_start();
|
||||
|
||||
$now = time();
|
||||
|
||||
function adminStatusBadge(array $a, int $now): string
|
||||
{
|
||||
if (!$a['published']) {
|
||||
return '<span class="badge bg-secondary">Brouillon</span>';
|
||||
}
|
||||
if (strtotime((string)($a['published_at'] ?? '')) > $now) {
|
||||
return '<span class="badge bg-warning text-dark">Avant-première</span>';
|
||||
}
|
||||
return '<span class="badge bg-success">Publié</span>';
|
||||
}
|
||||
?>
|
||||
|
||||
<div class="d-flex justify-content-between align-items-center mb-4">
|
||||
<h1 class="h3 mb-0">Administration</h1>
|
||||
<a href="/?action=create" class="btn btn-primary btn-sm">+ Nouvel article</a>
|
||||
</div>
|
||||
|
||||
<!-- Onglets -->
|
||||
<ul class="nav nav-tabs mb-4">
|
||||
<?php if (isAdmin()): ?>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link <?= $tab === 'dashboard' ? 'active' : '' ?>"
|
||||
href="/?action=admin&tab=dashboard">Tableau de bord</a>
|
||||
</li>
|
||||
<?php endif; ?>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link <?= $tab === 'articles' ? 'active' : '' ?>"
|
||||
href="/?action=admin&tab=articles"><?= isAdmin() ? 'Articles' : 'Mes articles' ?></a>
|
||||
</li>
|
||||
<?php if (isAdmin()): ?>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link <?= $tab === 'users' ? 'active' : '' ?>"
|
||||
href="/?action=admin&tab=users">Utilisateurs</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link <?= $tab === 'roles' ? 'active' : '' ?>"
|
||||
href="/?action=admin&tab=roles">Rôles</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="/?action=categories">Catégories</a>
|
||||
</li>
|
||||
<?php endif; ?>
|
||||
</ul>
|
||||
|
||||
<!-- ─────────────────────────── DASHBOARD ─────────────────────────── -->
|
||||
<?php if ($tab === 'dashboard' && isAdmin()): ?>
|
||||
|
||||
<div class="row g-3 mb-4">
|
||||
<?php
|
||||
$stats = [
|
||||
['label' => 'Publiés', 'value' => $adminData['published'], 'color' => 'success'],
|
||||
['label' => 'Avant-premières', 'value' => $adminData['previews'], 'color' => 'warning'],
|
||||
['label' => 'Brouillons', 'value' => $adminData['drafts'], 'color' => 'secondary'],
|
||||
['label' => 'Total', 'value' => $adminData['total'], 'color' => 'primary'],
|
||||
];
|
||||
foreach ($stats as $s): ?>
|
||||
<div class="col-6 col-md-3">
|
||||
<div class="card text-center">
|
||||
<div class="card-body">
|
||||
<div class="display-6 fw-bold text-<?= $s['color'] ?>"><?= $s['value'] ?></div>
|
||||
<div class="text-muted small"><?= $s['label'] ?></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<?php endforeach; ?>
|
||||
</div>
|
||||
|
||||
<h5>Activité récente</h5>
|
||||
<table class="table table-sm table-hover">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Titre</th>
|
||||
<th>Auteur</th>
|
||||
<th>Statut</th>
|
||||
<th>Modifié le</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php foreach ($adminData['recent'] as $a): ?>
|
||||
<tr>
|
||||
<td>
|
||||
<a href="/post/<?= htmlspecialchars($a['slug'] ?? '') ?>">
|
||||
<?= htmlspecialchars($a['title']) ?>
|
||||
</a>
|
||||
</td>
|
||||
<td class="text-muted small"><?= htmlspecialchars($a['author'] ?? '–') ?></td>
|
||||
<td><?= adminStatusBadge($a, $now) ?></td>
|
||||
<td class="text-muted small">
|
||||
<?= htmlspecialchars(date('d/m/Y H:i', strtotime((string)($a['updated_at'] ?? $a['created_at'] ?? '')))) ?>
|
||||
</td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<!-- ─────────────────────────── ARTICLES ─────────────────────────── -->
|
||||
<?php elseif ($tab === 'articles'): ?>
|
||||
|
||||
<?php if (empty($adminData['articles'])): ?>
|
||||
<p class="text-muted">Aucun article.</p>
|
||||
<?php else: ?>
|
||||
<table class="table table-sm table-hover align-middle">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Titre</th>
|
||||
<?php if (isAdmin()): ?><th>Auteur</th><?php endif; ?>
|
||||
<th>Catégorie</th>
|
||||
<th>Statut</th>
|
||||
<th>Date</th>
|
||||
<th></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php foreach ($adminData['articles'] as $a): ?>
|
||||
<tr>
|
||||
<td>
|
||||
<a href="/post/<?= htmlspecialchars($a['slug'] ?? '') ?>">
|
||||
<?= htmlspecialchars($a['title']) ?>
|
||||
</a>
|
||||
</td>
|
||||
<?php if (isAdmin()): ?>
|
||||
<td class="text-muted small"><?= htmlspecialchars($a['author'] ?? '–') ?></td>
|
||||
<?php endif; ?>
|
||||
<td class="text-muted small"><?= htmlspecialchars($a['category'] ?? '–') ?></td>
|
||||
<td><?= adminStatusBadge($a, $now) ?></td>
|
||||
<td class="text-muted small text-nowrap">
|
||||
<?= htmlspecialchars(date('d/m/Y', strtotime((string)($a['published_at'] ?? $a['created_at'] ?? '')))) ?>
|
||||
</td>
|
||||
<td class="text-end text-nowrap">
|
||||
<a href="/?action=edit&uuid=<?= htmlspecialchars($a['uuid']) ?>"
|
||||
class="btn btn-outline-secondary btn-sm">Modifier</a>
|
||||
</td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
<?php endif; ?>
|
||||
|
||||
<!-- ─────────────────────────── UTILISATEURS ─────────────────────────── -->
|
||||
<?php elseif ($tab === 'users' && isAdmin()): ?>
|
||||
|
||||
<!-- Ajouter / attribuer un rôle -->
|
||||
<div class="card mb-4">
|
||||
<div class="card-header">Attribuer un rôle</div>
|
||||
<div class="card-body">
|
||||
<form method="post" action="/?action=admin_grant_role" class="row g-2 align-items-end">
|
||||
<div class="col-md-5">
|
||||
<label class="form-label small">Email</label>
|
||||
<input type="email" name="email" class="form-control form-control-sm"
|
||||
placeholder="utilisateur@exemple.fr" required>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<label class="form-label small">Rôle</label>
|
||||
<select name="role" class="form-select form-select-sm" required>
|
||||
<?php foreach ($adminData['roles'] as $r): ?>
|
||||
<option value="<?= htmlspecialchars($r['name']) ?>">
|
||||
<?= htmlspecialchars($r['label']) ?>
|
||||
</option>
|
||||
<?php endforeach; ?>
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<button type="submit" class="btn btn-primary btn-sm w-100">Attribuer</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Liste des utilisateurs -->
|
||||
<?php if (empty($adminData['users'])): ?>
|
||||
<p class="text-muted">Aucun utilisateur.</p>
|
||||
<?php else: ?>
|
||||
<table class="table table-sm table-hover align-middle">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Email</th>
|
||||
<th>Statut</th>
|
||||
<th>Rôles</th>
|
||||
<th></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php foreach ($adminData['users'] as $u): ?>
|
||||
<tr>
|
||||
<td class="small"><?= htmlspecialchars($u['email']) ?></td>
|
||||
<td>
|
||||
<?php if ($u['is_active'] === null): ?>
|
||||
<span class="badge bg-light text-muted">Pré-inscrit</span>
|
||||
<?php elseif ($u['is_active']): ?>
|
||||
<span class="badge bg-success">Actif</span>
|
||||
<?php else: ?>
|
||||
<span class="badge bg-danger">Inactif</span>
|
||||
<?php endif; ?>
|
||||
</td>
|
||||
<td>
|
||||
<?php foreach ($u['roles'] as $roleName): ?>
|
||||
<span class="badge bg-primary me-1"><?= htmlspecialchars($roleName) ?></span>
|
||||
<form method="post" action="/?action=admin_revoke_role"
|
||||
class="d-inline"
|
||||
data-confirm="Retirer le rôle <?= htmlspecialchars($roleName) ?> à <?= htmlspecialchars($u['email']) ?> ?">
|
||||
<input type="hidden" name="email" value="<?= htmlspecialchars($u['email']) ?>">
|
||||
<input type="hidden" name="role" value="<?= htmlspecialchars($roleName) ?>">
|
||||
<button type="submit" class="btn btn-link btn-sm p-0 text-danger" title="Retirer">×</button>
|
||||
</form>
|
||||
<?php endforeach; ?>
|
||||
<?php if (empty($u['roles'])): ?>
|
||||
<span class="text-muted small">–</span>
|
||||
<?php endif; ?>
|
||||
</td>
|
||||
<td class="text-end">
|
||||
<!-- Ajout rapide d'un rôle existant -->
|
||||
<?php
|
||||
$currentRoles = $u['roles'];
|
||||
$missing = array_filter($adminData['roles'], fn ($r) => !in_array($r['name'], $currentRoles, true));
|
||||
?>
|
||||
<?php if ($missing): ?>
|
||||
<form method="post" action="/?action=admin_grant_role" class="d-inline-flex gap-1">
|
||||
<input type="hidden" name="email" value="<?= htmlspecialchars($u['email']) ?>">
|
||||
<select name="role" class="form-select form-select-sm" style="width:auto">
|
||||
<?php foreach ($missing as $r): ?>
|
||||
<option value="<?= htmlspecialchars($r['name']) ?>">
|
||||
<?= htmlspecialchars($r['label']) ?>
|
||||
</option>
|
||||
<?php endforeach; ?>
|
||||
</select>
|
||||
<button type="submit" class="btn btn-outline-primary btn-sm">+</button>
|
||||
</form>
|
||||
<?php endif; ?>
|
||||
</td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
<?php endif; ?>
|
||||
|
||||
<!-- ─────────────────────────── RÔLES ─────────────────────────── -->
|
||||
<?php elseif ($tab === 'roles' && isAdmin()): ?>
|
||||
|
||||
<div class="row g-4">
|
||||
|
||||
<!-- Liste des rôles existants -->
|
||||
<div class="col-lg-8">
|
||||
<h5 class="mb-3">Rôles existants</h5>
|
||||
<?php if (empty($adminData['roles'])): ?>
|
||||
<p class="text-muted">Aucun rôle défini.</p>
|
||||
<?php else: ?>
|
||||
<table class="table table-sm table-hover align-middle">
|
||||
<thead>
|
||||
<tr>
|
||||
<th style="width:160px">Nom technique</th>
|
||||
<th>Label affiché</th>
|
||||
<th class="text-center" style="width:90px">Utilisateurs</th>
|
||||
<th style="width:100px"></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php foreach ($adminData['roles'] as $r): ?>
|
||||
<tr>
|
||||
<td><code class="text-body"><?= htmlspecialchars($r['name']) ?></code></td>
|
||||
<td>
|
||||
<form method="post" action="/?action=admin_update_role"
|
||||
class="d-flex gap-2 align-items-center">
|
||||
<input type="hidden" name="id" value="<?= (int)$r['id'] ?>">
|
||||
<input type="text" name="label"
|
||||
value="<?= htmlspecialchars($r['label']) ?>"
|
||||
class="form-control form-control-sm" required>
|
||||
<button type="submit" class="btn btn-outline-secondary btn-sm text-nowrap">
|
||||
Sauver
|
||||
</button>
|
||||
</form>
|
||||
</td>
|
||||
<td class="text-center">
|
||||
<span class="badge bg-secondary"><?= (int)$r['user_count'] ?></span>
|
||||
</td>
|
||||
<td class="text-end">
|
||||
<form method="post" action="/?action=admin_delete_role"
|
||||
data-confirm="Supprimer le rôle «<?= htmlspecialchars($r['name']) ?>» ?<?= (int)$r['user_count'] > 0 ? ' ' . (int)$r['user_count'] . ' utilisateur(s) perdront ce rôle.' : '' ?>">
|
||||
<input type="hidden" name="id" value="<?= (int)$r['id'] ?>">
|
||||
<button type="submit" class="btn btn-outline-danger btn-sm">
|
||||
Supprimer
|
||||
</button>
|
||||
</form>
|
||||
</td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
|
||||
<!-- Créer un rôle -->
|
||||
<div class="col-lg-4">
|
||||
<div class="card">
|
||||
<div class="card-header">Nouveau rôle</div>
|
||||
<div class="card-body">
|
||||
<form method="post" action="/?action=admin_create_role">
|
||||
<div class="mb-3">
|
||||
<label class="form-label small fw-semibold">Nom technique</label>
|
||||
<input type="text" name="name" class="form-control form-control-sm"
|
||||
placeholder="ex : moderator"
|
||||
pattern="[a-z0-9_-]+"
|
||||
title="Lettres minuscules, chiffres, tirets et underscores uniquement"
|
||||
required>
|
||||
<div class="form-text">Utilisé dans le code — ne change pas.</div>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label small fw-semibold">Label affiché</label>
|
||||
<input type="text" name="label" class="form-control form-control-sm"
|
||||
placeholder="ex : Modérateur" required>
|
||||
</div>
|
||||
<button type="submit" class="btn btn-primary btn-sm w-100">Créer</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Permissions par rôle -->
|
||||
<?php if (!empty($adminData['roles'])): ?>
|
||||
<div class="col-12 mt-2">
|
||||
<h5 class="mb-3">Permissions par rôle</h5>
|
||||
<p class="text-muted small mb-3">Le rôle <code>admin</code> a toutes les permissions implicitement.</p>
|
||||
<div class="row g-3">
|
||||
<?php foreach ($adminData['roles'] as $r):
|
||||
if ($r['name'] === 'admin') {
|
||||
continue;
|
||||
} ?>
|
||||
<div class="col-md-6 col-lg-4">
|
||||
<div class="card h-100">
|
||||
<div class="card-header py-2 d-flex align-items-center justify-content-between">
|
||||
<span class="fw-semibold"><?= htmlspecialchars($r['label']) ?></span>
|
||||
<code class="text-muted small"><?= htmlspecialchars($r['name']) ?></code>
|
||||
</div>
|
||||
<div class="card-body py-3">
|
||||
<form method="post" action="/?action=admin_update_role_caps">
|
||||
<input type="hidden" name="role_id" value="<?= (int)$r['id'] ?>">
|
||||
<?php foreach (CAPABILITY_GROUPS as $group): ?>
|
||||
<?php if (isset($group['single'])): ?>
|
||||
<?php $cap = $group['single']; ?>
|
||||
<div class="form-check mb-3">
|
||||
<input class="form-check-input" type="checkbox"
|
||||
name="caps[]" value="<?= htmlspecialchars($cap) ?>"
|
||||
id="cap_<?= (int)$r['id'] ?>_<?= htmlspecialchars($cap) ?>"
|
||||
<?= in_array($cap, $r['capabilities'], true) ? 'checked' : '' ?>>
|
||||
<label class="form-check-label small fw-semibold"
|
||||
for="cap_<?= (int)$r['id'] ?>_<?= htmlspecialchars($cap) ?>">
|
||||
<?= htmlspecialchars($group['label']) ?>
|
||||
</label>
|
||||
</div>
|
||||
<?php else: ?>
|
||||
<div class="mb-3">
|
||||
<div class="small fw-semibold mb-1"><?= htmlspecialchars($group['label']) ?></div>
|
||||
<div class="d-flex gap-3 ps-1">
|
||||
<?php foreach (['own' => 'Propres articles', 'all' => 'Tous'] as $scope => $scopeLabel):
|
||||
$cap = $group[$scope]; ?>
|
||||
<div class="form-check">
|
||||
<input class="form-check-input" type="checkbox"
|
||||
name="caps[]" value="<?= htmlspecialchars($cap) ?>"
|
||||
id="cap_<?= (int)$r['id'] ?>_<?= htmlspecialchars($cap) ?>"
|
||||
<?= in_array($cap, $r['capabilities'], true) ? 'checked' : '' ?>>
|
||||
<label class="form-check-label small"
|
||||
for="cap_<?= (int)$r['id'] ?>_<?= htmlspecialchars($cap) ?>">
|
||||
<?= $scopeLabel ?>
|
||||
</label>
|
||||
</div>
|
||||
<?php endforeach; ?>
|
||||
</div>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
<?php endforeach; ?>
|
||||
<button type="submit" class="btn btn-outline-secondary btn-sm mt-1 w-100">
|
||||
Enregistrer
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<?php endforeach; ?>
|
||||
</div>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
</div>
|
||||
|
||||
<?php endif; ?>
|
||||
|
||||
<?php
|
||||
$content = ob_get_clean();
|
||||
$title = 'Administration — varlog';
|
||||
include __DIR__ . '/layout.php';
|
||||
Reference in New Issue
Block a user