Files
Cedric Abonnel 26ada9b54e Feat: onglet SMTP dans l'administration
- Formulaire d'édition des paramètres SMTP (serveur, port, chiffrement,
  utilisateur, mot de passe, expéditeur) stockés dans data/smtp_settings.json
  (écrit par www-data, contrairement au .env en lecture seule)
- Test de connexion SMTP avec logs PHPMailer complets (DEBUG_SERVER)
- Envoi d'email de test avec contenu personnalisé anti-spam
- src/SmtpSettings.php : lecture/écriture smtp_settings.json avec fallback env()
- mailer.php : lit les paramètres depuis SmtpSettings en priorité
- admin.js : indicateurs spinner sur les boutons pendant le traitement
2026-05-13 10:53:03 +02:00

693 lines
35 KiB
PHP
Raw Permalink 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
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="/new" 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="/admin/dashboard">Tableau de bord</a>
</li>
<?php endif; ?>
<li class="nav-item">
<a class="nav-link <?= $tab === 'articles' ? 'active' : '' ?>"
href="/admin/articles"><?= isAdmin() ? 'Articles' : 'Mes articles' ?></a>
</li>
<?php if (isAdmin()): ?>
<li class="nav-item">
<a class="nav-link <?= $tab === 'users' ? 'active' : '' ?>"
href="/admin/users">Utilisateurs</a>
</li>
<li class="nav-item">
<a class="nav-link <?= $tab === 'roles' ? 'active' : '' ?>"
href="/admin/roles">Rôles</a>
</li>
<li class="nav-item">
<a class="nav-link" href="/categories">Catégories</a>
</li>
<li class="nav-item">
<a class="nav-link <?= $tab === 'site' ? 'active' : '' ?>"
href="/admin/site">Site</a>
</li>
<li class="nav-item">
<a class="nav-link <?= $tab === 'comments' ? 'active' : '' ?>"
href="/admin/comments">Commentaires</a>
</li>
<li class="nav-item">
<a class="nav-link <?= $tab === 'smtp' ? 'active' : '' ?>"
href="/admin/smtp">SMTP</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'): ?>
<!-- Filtres -->
<form class="row g-2 align-items-center mb-3" method="get" action="/admin/articles">
<?php if (isAdmin() && !empty($adminData['filter_authors'])): ?>
<div class="col-auto">
<select name="filter_author" class="form-select form-select-sm">
<option value="">Tous les auteurs</option>
<?php foreach ($adminData['filter_authors'] as $_fa): ?>
<option value="<?= htmlspecialchars($_fa) ?>"
<?= ($adminData['filter_author'] ?? '') === $_fa ? 'selected' : '' ?>>
<?= htmlspecialchars($_fa) ?>
</option>
<?php endforeach; ?>
</select>
</div>
<?php endif; ?>
<?php if (!empty($adminData['filter_categories'])): ?>
<div class="col-auto">
<select name="filter_category" class="form-select form-select-sm">
<option value="">Toutes les catégories</option>
<?php foreach ($adminData['filter_categories'] as $_fc): ?>
<option value="<?= htmlspecialchars($_fc) ?>"
<?= ($adminData['filter_category'] ?? '') === $_fc ? 'selected' : '' ?>>
<?= htmlspecialchars($_fc) ?>
</option>
<?php endforeach; ?>
</select>
</div>
<?php endif; ?>
<div class="col-auto">
<select name="filter_status" class="form-select form-select-sm">
<option value="">Tous les statuts</option>
<option value="published" <?= ($adminData['filter_status'] ?? '') === 'published' ? 'selected' : '' ?>>Publié</option>
<option value="draft" <?= ($adminData['filter_status'] ?? '') === 'draft' ? 'selected' : '' ?>>Brouillon</option>
<option value="preview" <?= ($adminData['filter_status'] ?? '') === 'preview' ? 'selected' : '' ?>>Avant-première</option>
</select>
</div>
<div class="col-auto d-flex gap-2">
<button type="submit" class="btn btn-secondary btn-sm">Filtrer</button>
<?php $hasFilter = ($adminData['filter_author'] ?? '') !== '' || ($adminData['filter_category'] ?? '') !== '' || ($adminData['filter_status'] ?? '') !== ''; ?>
<?php if ($hasFilter): ?>
<a href="/admin/articles" class="btn btn-link btn-sm p-0">Réinitialiser</a>
<?php endif; ?>
</div>
<?php if ($hasFilter): ?>
<div class="col-auto">
<span class="text-muted small"><?= count($adminData['articles']) ?> résultat(s)</span>
</div>
<?php endif; ?>
</form>
<?php if (empty($adminData['articles'])): ?>
<p class="text-muted">Aucun article.</p>
<?php else: ?>
<form method="post" action="/?action=admin_bulk_delete" id="bulk-form">
<div class="d-flex justify-content-between align-items-center mb-2">
<div class="form-check mb-0">
<input class="form-check-input" type="checkbox" id="check-all">
<label class="form-check-label small text-muted" for="check-all">Tout sélectionner</label>
</div>
<button type="submit" class="btn btn-danger btn-sm"
onclick="return document.querySelectorAll('.bulk-check:checked').length > 0 && confirm('Supprimer les articles sélectionnés ? Cette action est irréversible.')">
Supprimer la sélection
</button>
</div>
<table class="table table-sm table-hover align-middle">
<thead>
<tr>
<th style="width:2rem"></th>
<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>
<input class="form-check-input bulk-check" type="checkbox"
name="uuids[]" value="<?= htmlspecialchars($a['uuid']) ?>">
</td>
<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="/edit/<?= htmlspecialchars($a['uuid']) ?>"
class="btn btn-outline-secondary btn-sm">Modifier</a>
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</form>
<script src="/assets/js/admin.js" defer></script>
<?php endif; ?>
<!-- ─────────────────────────── UTILISATEURS ─────────────────────────── -->
<?php elseif ($tab === 'users' && isAdmin()): ?>
<?php if (($_GET['error'] ?? '') === 'last_admin'): ?>
<div class="alert alert-danger py-2 mb-3">
Impossible de retirer le rôle Administrateur : il doit rester au moins un administrateur.
</div>
<?php endif; ?>
<!-- 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 $role): ?>
<span class="badge bg-primary me-1"><?= htmlspecialchars($role['label']) ?></span>
<form method="post" action="/?action=admin_revoke_role"
class="d-inline"
data-confirm="Retirer le rôle «<?= htmlspecialchars($role['label']) ?>» à <?= htmlspecialchars($u['email']) ?> ?">
<input type="hidden" name="email" value="<?= htmlspecialchars($u['email']) ?>">
<input type="hidden" name="role" value="<?= htmlspecialchars($role['name']) ?>">
<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
$currentRoleNames = array_column($u['roles'], 'name');
$missing = array_filter($adminData['roles'], fn ($r) => !in_array($r['name'], $currentRoleNames, 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">
<!-- Tableau des rôles existants -->
<div class="col-lg-8">
<?php if (empty($adminData['roles'])): ?>
<p class="text-muted">Aucun rôle défini.</p>
<?php else: ?>
<table class="table table-hover align-middle">
<thead>
<tr>
<th>Rôle</th>
<th class="text-center" style="width:100px">Utilisateurs</th>
<th style="width:110px"></th>
</tr>
</thead>
<tbody>
<?php foreach ($adminData['roles'] as $r): ?>
<tr>
<td>
<?= htmlspecialchars($r['label']) ?>
<?php if ($r['name'] !== 'admin'): ?>
<div class="text-muted small">
<?php
$capLabels = array_map(
fn ($c) => KNOWN_CAPABILITIES[$c] ?? $c,
$r['capabilities']
);
echo htmlspecialchars(implode(', ', $capLabels) ?: '');
?>
</div>
<?php else: ?>
<div class="text-muted small">Toutes les permissions</div>
<?php endif; ?>
</td>
<td class="text-center">
<span class="badge bg-secondary"><?= (int)$r['user_count'] ?></span>
</td>
<td class="text-end d-flex gap-1 justify-content-end">
<a href="/admin/role/<?= rawurlencode($r['name']) ?>"
class="btn btn-outline-secondary btn-sm">Éditer</a>
<?php if ((int)$r['user_count'] === 0 && $r['name'] !== 'admin'): ?>
<form method="post" action="/?action=admin_delete_role"
data-confirm="Supprimer le rôle «<?= htmlspecialchars($r['name']) ?>» ?">
<input type="hidden" name="id" value="<?= (int)$r['id'] ?>">
<button type="submit" class="btn btn-outline-danger btn-sm">×</button>
</form>
<?php endif; ?>
</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 for="role-label" class="form-label small fw-semibold">Nom du rôle</label>
<input type="text" id="role-label" name="label" class="form-control form-control-sm"
placeholder="ex : Modérateur" required autocomplete="off">
</div>
<input type="hidden" id="role-name" name="name">
<button type="submit" class="btn btn-primary btn-sm w-100">Créer</button>
</form>
</div>
</div>
</div>
</div>
<?php elseif ($tab === 'site' && isAdmin()): ?>
<?php if (!empty($siteSettingsSaved)): ?>
<div class="alert alert-success py-2 mb-3">Paramètres enregistrés.</div>
<?php endif; ?>
<div class="card" style="max-width:540px">
<div class="card-header">Paramètres du site</div>
<div class="card-body">
<form method="post" action="/?action=admin_save_site">
<div class="mb-3">
<label for="site-title" class="form-label small fw-semibold">Titre du site</label>
<input type="text" id="site-title" name="site_title"
class="form-control form-control-sm"
value="<?= htmlspecialchars(siteTitle()) ?>"
required maxlength="80">
<div class="form-text">Affiché dans la barre de navigation et les onglets.</div>
</div>
<div class="mb-3">
<label for="site-claim" class="form-label small fw-semibold">Claim / accroche</label>
<input type="text" id="site-claim" name="site_claim"
class="form-control form-control-sm"
value="<?= htmlspecialchars(siteClaim()) ?>"
required maxlength="200">
<div class="form-text">Affiché sous le titre dans la navbar et dans le pied de page.</div>
</div>
<div class="mb-3">
<label for="site-lang" class="form-label small fw-semibold">Langue du site</label>
<input type="text" id="site-lang" name="site_lang"
class="form-control form-control-sm"
value="<?= htmlspecialchars(siteLang()) ?>"
maxlength="20" placeholder="ex : fr-FR">
<div class="form-text">Format BCP 47 (ex&nbsp;: <code>fr</code>, <code>fr-FR</code>). Utilisé dans <code>&lt;html lang&gt;</code>, og:locale, RSS et JSON-LD.</div>
</div>
<div class="mb-3">
<label for="posts-per-page" class="form-label small fw-semibold">Articles par page</label>
<input type="number" id="posts-per-page" name="posts_per_page"
class="form-control form-control-sm"
value="<?= postsPerPage() ?>"
min="1" max="100">
</div>
<div class="mb-3">
<label for="site-license-label" class="form-label small fw-semibold">Licence (libellé)</label>
<input type="text" id="site-license-label" name="site_license_label"
class="form-control form-control-sm"
value="<?= htmlspecialchars(siteLicenseLabel()) ?>"
maxlength="80" placeholder="ex : CC BY 4.0">
</div>
<div class="mb-3">
<label for="site-license-url" class="form-label small fw-semibold">Licence (URL)</label>
<input type="url" id="site-license-url" name="site_license_url"
class="form-control form-control-sm"
value="<?= htmlspecialchars(siteLicenseUrl()) ?>"
maxlength="200">
<div class="form-text">Affiché dans le footer.</div>
</div>
<button type="submit" class="btn btn-primary btn-sm">Enregistrer</button>
</form>
</div>
</div>
<?php endif; ?>
<!-- ─────────────────────────── SMTP ─────────────────────────────── -->
<?php if ($tab === 'smtp' && isAdmin()): ?>
<?php $sc = $adminData['smtp_config']; ?>
<?php if (isset($_GET['saved'])): ?>
<div class="alert alert-success py-2 mb-3">Paramètres SMTP enregistrés.</div>
<?php endif; ?>
<div class="row g-4">
<!-- Formulaire config + actions -->
<div class="col-lg-5">
<div class="card">
<div class="card-header">Configuration SMTP</div>
<div class="card-body">
<form method="post" id="smtp-config-form">
<div class="mb-3">
<label for="smtp-host" class="form-label small fw-semibold">Serveur</label>
<input type="text" id="smtp-host" name="smtp_host"
class="form-control form-control-sm font-monospace"
value="<?= htmlspecialchars($sc['host']) ?>"
placeholder="smtp.exemple.fr">
</div>
<div class="row g-2 mb-3">
<div class="col-5">
<label for="smtp-port" class="form-label small fw-semibold">Port</label>
<input type="number" id="smtp-port" name="smtp_port"
class="form-control form-control-sm"
value="<?= htmlspecialchars($sc['port']) ?>"
placeholder="587" min="1" max="65535">
</div>
<div class="col-7">
<label for="smtp-secure" class="form-label small fw-semibold">Chiffrement</label>
<select id="smtp-secure" name="smtp_secure" class="form-select form-select-sm">
<option value="" <?= $sc['secure'] === '' ? 'selected' : '' ?>>Aucun</option>
<option value="tls" <?= strtolower($sc['secure']) === 'tls' ? 'selected' : '' ?>>STARTTLS (587)</option>
<option value="ssl" <?= strtolower($sc['secure']) === 'ssl' ? 'selected' : '' ?>>SSL/TLS (465)</option>
</select>
</div>
</div>
<div class="mb-3">
<label for="smtp-user" class="form-label small fw-semibold">Utilisateur</label>
<input type="text" id="smtp-user" name="smtp_user"
class="form-control form-control-sm"
value="<?= htmlspecialchars($sc['user']) ?>"
placeholder="user@exemple.fr"
autocomplete="off">
</div>
<div class="mb-3">
<label for="smtp-pass" class="form-label small fw-semibold">Mot de passe</label>
<input type="password" id="smtp-pass" name="smtp_pass"
class="form-control form-control-sm"
placeholder="<?= $sc['has_pass'] ? '(inchangé si vide)' : '' ?>"
autocomplete="new-password">
<?php if ($sc['has_pass']): ?>
<div class="form-text">Laisser vide pour conserver le mot de passe actuel.</div>
<?php endif; ?>
</div>
<div class="mb-3">
<label for="smtp-from" class="form-label small fw-semibold">Email expéditeur</label>
<input type="email" id="smtp-from" name="smtp_from"
class="form-control form-control-sm"
value="<?= htmlspecialchars($sc['from']) ?>"
placeholder="no-reply@exemple.fr">
</div>
<div class="mb-3">
<label for="smtp-from-name" class="form-label small fw-semibold">Nom expéditeur</label>
<input type="text" id="smtp-from-name" name="smtp_from_name"
class="form-control form-control-sm"
value="<?= htmlspecialchars($sc['from_name']) ?>"
placeholder="Mon Site">
</div>
<div class="d-flex flex-wrap gap-2">
<button type="submit" formaction="/?action=admin_smtp_save"
class="btn btn-primary btn-sm" id="smtp-save-btn">
Enregistrer
</button>
<button type="submit" formaction="/?action=admin_smtp_test"
name="mode" value="connect"
class="btn btn-outline-secondary btn-sm">
Tester la connexion
</button>
</div>
</form>
</div>
</div>
</div>
<!-- Envoi de test + résultats -->
<div class="col-lg-7">
<div class="card mb-3">
<div class="card-body">
<form method="post" action="/?action=admin_smtp_test" id="smtp-test-form">
<input type="hidden" name="mode" value="send">
<label for="smtp-email" class="form-label small fw-semibold">Envoyer un email de test</label>
<div class="d-flex gap-2">
<input type="email" id="smtp-email" name="test_email"
class="form-control form-control-sm"
placeholder="destinataire@exemple.fr"
value="<?= htmlspecialchars($adminData['smtp_test']['email'] ?? '') ?>"
required>
<button type="submit" class="btn btn-outline-secondary btn-sm text-nowrap" id="smtp-send-btn">
Envoyer
</button>
</div>
</form>
</div>
</div>
<?php if (isset($adminData['smtp_test'])): ?>
<?php $st = $adminData['smtp_test']; ?>
<div class="card">
<div class="card-header d-flex justify-content-between align-items-center">
<span>
<?php if ($st['success']): ?>
<span class="text-success fw-semibold">✓ Succès</span>
<?php else: ?>
<span class="text-danger fw-semibold">✗ Échec</span>
<?php endif; ?>
<?= $st['mode'] === 'send'
? 'Envoi vers ' . htmlspecialchars($st['email'])
: 'Test de connexion' ?>
</span>
<span class="text-muted small"><?= htmlspecialchars($st['ts']) ?></span>
</div>
<?php if (!$st['success'] && $st['error'] !== ''): ?>
<div class="alert alert-danger mb-0 rounded-0 border-0 border-bottom py-2 px-3 small">
<?= htmlspecialchars($st['error']) ?>
</div>
<?php endif; ?>
<?php if (!empty($st['logs'])): ?>
<pre class="p-3 mb-0 small" style="max-height:420px;overflow-y:auto;font-size:0.75rem;background:var(--vl-code-bg,#f8f9fa);border-radius:0 0 var(--bs-card-inner-border-radius) var(--bs-card-inner-border-radius)"><?php
foreach ($st['logs'] as $line) {
echo htmlspecialchars($line) . "\n";
}
?></pre>
<?php endif; ?>
</div>
<?php endif; ?>
</div>
</div>
<script src="/assets/js/admin.js" defer></script>
<?php endif; ?>
<!-- ─────────────────────────── COMMENTAIRES ──────────────────────── -->
<?php if ($tab === 'comments' && isAdmin()): ?>
<h5 class="mb-3">Commentaires</h5>
<?php if (empty($adminData['comments'])): ?>
<p class="text-muted">Aucun commentaire pour l'instant.</p>
<?php else: ?>
<div class="table-responsive">
<table class="table table-sm table-hover align-middle">
<thead>
<tr>
<th>Article</th>
<th>Auteur</th>
<th>Commentaire</th>
<th>Date</th>
<th>État</th>
<th></th>
</tr>
</thead>
<tbody>
<?php foreach ($adminData['comments'] as $c): ?>
<?php
$cSlug = $adminData['articleSlugs'][$c['article_uuid']] ?? null;
$cDate = date('d/m/Y H:i', strtotime((string)$c['created_at']));
$cVerif = $c['verified'] ? '<span class="badge bg-success">Vérifié</span>' : '<span class="badge bg-warning text-dark">En attente</span>';
$cPub = $c['published'] ? '<span class="badge bg-primary">Publié</span>' : '<span class="badge bg-secondary">Masqué</span>';
?>
<tr>
<td class="small">
<?php if ($cSlug): ?>
<a href="/post/<?= rawurlencode($cSlug) ?>#comments"><?= htmlspecialchars($cSlug) ?></a>
<?php else: ?>
<span class="text-muted"><?= htmlspecialchars(substr($c['article_uuid'], 0, 8)) ?>…</span>
<?php endif; ?>
</td>
<td class="small">
<div><?= htmlspecialchars($c['author_name']) ?></div>
<div class="text-muted"><?= htmlspecialchars($c['author_email']) ?></div>
</td>
<td class="small" style="max-width:30ch">
<span title="<?= htmlspecialchars($c['content']) ?>">
<?= htmlspecialchars(mb_strimwidth($c['content'], 0, 80, '…')) ?>
</span>
<?php if (!empty($c['verification_code'])): ?>
<br><span class="text-muted">Code : <?= htmlspecialchars($c['verification_code']) ?></span>
<?php endif; ?>
</td>
<td class="text-muted small text-nowrap"><?= htmlspecialchars($cDate) ?></td>
<td><?= $cVerif ?> <?= $cPub ?></td>
<td>
<?php if ($c['verified'] && $c['published']): ?>
<form method="post" action="/comment-moderate" class="d-inline">
<input type="hidden" name="id" value="<?= (int)$c['id'] ?>">
<input type="hidden" name="pub" value="0">
<button type="submit" class="btn btn-sm btn-outline-danger">Masquer</button>
</form>
<?php elseif ($c['verified'] && !$c['published']): ?>
<form method="post" action="/comment-moderate" class="d-inline">
<input type="hidden" name="id" value="<?= (int)$c['id'] ?>">
<input type="hidden" name="pub" value="1">
<button type="submit" class="btn btn-sm btn-outline-success">Publier</button>
</form>
<?php endif; ?>
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</div>
<?php endif; ?>
<?php endif; ?>
<?php
$content = ob_get_clean();
$title = 'Administration — ' . siteTitle();
include __DIR__ . '/layout.php';