26ada9b54e
- 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
693 lines
35 KiB
PHP
693 lines
35 KiB
PHP
<?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 : <code>fr</code>, <code>fr-FR</code>). Utilisé dans <code><html lang></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';
|