feat : recherche titre, toggle à la une, date modif, retour sources (v1.6.15)

- admin/articles : champ filter_search (titre, insensible casse) cumulable avec auteur/catégorie/statut (#85)
- admin/articles : colonne ★ avec toggle rapide featured + filtre filter_featured (#84)
- post/ : date de modification sous la date de publication si modifié après mise en ligne (#81)
- sources/ : bouton ← Retour à l'article vers post/<slug> au lieu de /edit/<uuid> (#83)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-05-16 09:40:43 +02:00
parent 347e4be0b7
commit ae4ac11305
6 changed files with 80 additions and 3 deletions
+12
View File
@@ -5,6 +5,18 @@ Format : [Keep a Changelog](https://keepachangelog.com/fr/1.0.0/) — versionnag
---
## [1.6.15] - 2026-05-16
### Ajouté
- `admin/articles` : champ de recherche par titre (`filter_search`), cumulable avec les autres filtres (#85)
- `admin/articles` : colonne « ★ À la une » avec toggle rapide par ligne et filtre `filter_featured` (#84)
- `post/` : date de modification affichée sous la date de publication si l'article a été modifié après sa mise en ligne (#81)
### Modifié
- `sources/` : bouton « ← Modifier » remplacé par « ← Retour à l'article » pointant vers `post/<slug>` (#83)
---
## [1.6.14] - 2026-05-15
### Modifié
+25
View File
@@ -2355,9 +2355,13 @@ switch ($action) {
$filterAuthor = trim($_GET['filter_author'] ?? '');
$filterCategory = trim($_GET['filter_category'] ?? '');
$filterStatus = trim($_GET['filter_status'] ?? '');
$filterSearch = trim($_GET['filter_search'] ?? '');
$filterFeatured = trim($_GET['filter_featured'] ?? '');
$adminData['filter_author'] = $filterAuthor;
$adminData['filter_category'] = $filterCategory;
$adminData['filter_status'] = $filterStatus;
$adminData['filter_search'] = $filterSearch;
$adminData['filter_featured'] = $filterFeatured;
$nowTs = time();
if ($filterAuthor !== '') {
@@ -2373,6 +2377,12 @@ switch ($action) {
} elseif ($filterStatus === 'preview') {
$allArticles = array_values(array_filter($allArticles, fn ($a) => $a['published'] && strtotime((string)($a['published_at'] ?? '')) > $nowTs));
}
if ($filterSearch !== '') {
$allArticles = array_values(array_filter($allArticles, fn ($a) => mb_stripos($a['title'] ?? '', $filterSearch) !== false));
}
if ($filterFeatured === 'yes') {
$allArticles = array_values(array_filter($allArticles, fn ($a) => !empty($a['featured'])));
}
$sortBy = in_array($_GET['sort'] ?? '', ['title', 'published', 'updated']) ? $_GET['sort'] : 'updated';
$sortDir = ($_GET['dir'] ?? '') === 'asc' ? 'asc' : 'desc';
@@ -2741,6 +2751,21 @@ switch ($action) {
header('Location: /admin/smtp');
exit;
case 'admin_toggle_featured':
requireAuth();
if (!isAdmin() || $_SERVER['REQUEST_METHOD'] !== 'POST') {
http_response_code(403);
exit;
}
$uid = trim((string)($_POST['uuid'] ?? ''));
$art = $uid !== '' ? $articles->getByUuid($uid) : null;
if ($art) {
$articles->setFeatured($uid, !((bool)($art['featured'] ?? false)));
}
$back = $_POST['_back'] ?? '/admin/articles';
header('Location: ' . $back);
exit;
case 'admin_bulk_delete':
requireAuth();
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
+1 -1
View File
@@ -1 +1 @@
1.6.14
1.6.15
+30 -1
View File
@@ -214,6 +214,8 @@ function adminStatusBadge(array $a, int $now): string
'filter_author' => $adminData['filter_author'] ?? '',
'filter_category' => $adminData['filter_category'] ?? '',
'filter_status' => $adminData['filter_status'] ?? '',
'filter_search' => $adminData['filter_search'] ?? '',
'filter_featured' => $adminData['filter_featured'] ?? '',
], fn ($v) => $v !== '');
$p['sort'] = $col;
$p['dir'] = $dir;
@@ -263,9 +265,19 @@ function adminStatusBadge(array $a, int $now): string
<option value="preview" <?= ($adminData['filter_status'] ?? '') === 'preview' ? 'selected' : '' ?>>Avant-première</option>
</select>
</div>
<div class="col-auto">
<select name="filter_featured" class="form-select form-select-sm">
<option value="">Tous</option>
<option value="yes" <?= ($adminData['filter_featured'] ?? '') === 'yes' ? 'selected' : '' ?>>★ À la une</option>
</select>
</div>
<div class="col-auto">
<input type="text" name="filter_search" class="form-control form-control-sm"
placeholder="Rechercher…" value="<?= htmlspecialchars($adminData['filter_search'] ?? '') ?>">
</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 $hasFilter = ($adminData['filter_author'] ?? '') !== '' || ($adminData['filter_category'] ?? '') !== '' || ($adminData['filter_status'] ?? '') !== '' || ($adminData['filter_search'] ?? '') !== '' || ($adminData['filter_featured'] ?? '') !== ''; ?>
<?php if ($hasFilter): ?>
<a href="/admin/articles" class="btn btn-link btn-sm p-0">Réinitialiser</a>
<?php endif; ?>
@@ -304,6 +316,7 @@ function adminStatusBadge(array $a, int $now): string
<?php if (isAdmin()): ?><th>Auteur</th><?php endif; ?>
<th>Catégorie</th>
<th>Statut</th>
<th title="À la une">★</th>
<th>
<a href="<?= htmlspecialchars($_mkSortUrl('published')) ?>"
class="text-decoration-none text-reset">
@@ -330,6 +343,22 @@ function adminStatusBadge(array $a, int $now): string
<?php endif; ?>
<td class="text-muted small"><?= htmlspecialchars($a['category'] ?? '') ?></td>
<td><?= adminStatusBadge($a, $now) ?></td>
<td class="text-center">
<?php if (isAdmin()): ?>
<?php $_isFeatured = !empty($a['featured']); ?>
<?php $_backUrl = '/admin/articles?' . http_build_query(array_filter(['filter_author' => $adminData['filter_author'] ?? '', 'filter_category' => $adminData['filter_category'] ?? '', 'filter_status' => $adminData['filter_status'] ?? '', 'filter_search' => $adminData['filter_search'] ?? '', 'filter_featured' => $adminData['filter_featured'] ?? '', 'sort' => $_sortBy, 'dir' => $_sortDir], fn ($v) => $v !== '')); ?>
<form method="post" action="/?action=admin_toggle_featured" class="d-inline m-0">
<input type="hidden" name="uuid" value="<?= htmlspecialchars($a['uuid']) ?>">
<input type="hidden" name="_back" value="<?= htmlspecialchars($_backUrl) ?>">
<button type="submit" class="btn btn-link p-0 border-0 lh-1 fs-6"
title="<?= $_isFeatured ? 'Retirer de la une' : 'Mettre à la une' ?>">
<?= $_isFeatured ? '★' : '<span class="text-muted">☆</span>' ?>
</button>
</form>
<?php else: ?>
<?= !empty($a['featured']) ? '★' : '' ?>
<?php endif; ?>
</td>
<td class="text-muted small text-nowrap">
<?= htmlspecialchars(date('d/m/Y', strtotime((string)($a['published_at'] ?? $a['created_at'] ?? '')))) ?>
</td>
+11
View File
@@ -96,6 +96,14 @@ $authorName = ($authorEmail !== '' && function_exists('authorDisplayName')
$authorProfileUrl = ($authorEmail !== '' && function_exists('authorProfileUrl')) ? authorProfileUrl($authorEmail) : '';
$authorSlugVal = ($authorEmail !== '' && function_exists('authorSlug')) ? authorSlug($authorEmail) : '';
$pubDate = htmlspecialchars(date('d/m/Y', strtotime((string)($article['published_at'] ?? $article['created_at'] ?? ''))));
$modDate = '';
$_updatedTs = strtotime((string)($article['updated_at'] ?? ''));
$_publishedTs = strtotime((string)($article['published_at'] ?? $article['created_at'] ?? ''));
if ($_updatedTs > 0 && $_publishedTs > 0 && $_updatedTs > $_publishedTs) {
$_frMonths = ['janvier','février','mars','avril','mai','juin','juillet','août','septembre','octobre','novembre','décembre'];
$modDate = 'Modifié le ' . (int)date('j', $_updatedTs) . ' ' . $_frMonths[(int)date('n', $_updatedTs) - 1]
. ' ' . date('Y', $_updatedTs) . ' à ' . date('H', $_updatedTs) . 'h' . date('i', $_updatedTs);
}
$hasCover = $coverFile !== '';
$heroExtraClass = $hasCover ? '' : ' article-cover--gradient';
$heroStyle = $hasCover ? '' : ' style="background:' . htmlspecialchars($gradient) . '"';
@@ -137,6 +145,9 @@ $hasSources = (!empty($externalLinks) || !empty($files))
<span class="mx-1 opacity-50">·</span>
<?php endif; ?>
<?= $pubDate ?>
<?php if ($modDate !== ''): ?>
<br><small class="opacity-75"><?= htmlspecialchars($modDate) ?></small>
<?php endif; ?>
</p>
</div>
<div class="article-hero-right">
+1 -1
View File
@@ -38,7 +38,7 @@ function renderMetaCell(string $key, mixed $val, array $row = []): string
?>
<div class="d-flex align-items-center gap-3 mb-1">
<a href="/edit/<?= rawurlencode($article['uuid']) ?>" class="btn btn-secondary btn-sm">← Modifier</a>
<a href="/post/<?= rawurlencode($article['slug'] ?? $article['uuid']) ?>" class="btn btn-secondary btn-sm">← Retour à l'article</a>
<h1 class="h4 mb-0">Sources &amp; médias</h1>
</div>
<p class="text-muted small mb-4"><?= htmlspecialchars($article['title']) ?></p>