a926e1825d
- Admin articles : filtres auteur/catégorie/statut par GET, compteur de résultats
- Admin articles : suppression massive avec checkboxes, confirmation JS, contrôle d'ownership
- Profil auteur : bio tronquée à 3 lignes avec bouton 'plus', limite à 6 articles affichés
- Profil auteur : bouton CTA pill 'Mes liens' vers /liens/{slug}
- Page liens : boutons pill colorés (palette auto par index), fond gris clair, grand avatar
157 lines
6.2 KiB
PHP
157 lines
6.2 KiB
PHP
<?php
|
||
require_once BASE_PATH . '/src/Parsedown.php';
|
||
$Parsedown = new Parsedown();
|
||
|
||
ob_start();
|
||
?>
|
||
|
||
<div class="post-grid">
|
||
<?php foreach ($posts as $i => $post): ?>
|
||
<?php
|
||
$html = $Parsedown->text($post['content']);
|
||
$preview = mb_strimwidth(strip_tags($html), 0, 120, '…');
|
||
$category = trim((string)($post['category'] ?? ''));
|
||
$gradient = coverGradient($category !== '' ? $category : $post['uuid'], $allCats ?? []);
|
||
$postUrl = '/post/' . rawurlencode($post['slug']);
|
||
$isDraft = !$post['published'];
|
||
$isAvantPremiere = $post['published'] && strtotime((string)($post['published_at'] ?? '')) > time();
|
||
$postCat = trim($post['category'] ?? '');
|
||
$isPrivate = $postCat !== '' && in_array($postCat, $privateCats ?? [], true);
|
||
$isLocked = $isAvantPremiere && !hasCapability('view_previews');
|
||
?>
|
||
<article class="card">
|
||
<?php if ($isDraft): ?>
|
||
<div class="draft-ribbon">Brouillon</div>
|
||
<?php elseif ($isAvantPremiere): ?>
|
||
<div class="premiere-ribbon">Avant-première</div>
|
||
<?php elseif ($isPrivate): ?>
|
||
<div class="private-ribbon">Privé</div>
|
||
<?php endif; ?>
|
||
<?php
|
||
$coverFile = $post['cover'] ?? '';
|
||
$coverStyle = $coverFile !== ''
|
||
? 'background-image: url(\'/file?uuid=' . rawurlencode($post['uuid']) . '&name=' . rawurlencode($coverFile) . '\')'
|
||
: 'background: ' . $gradient;
|
||
?>
|
||
<div class="card-cover" style="<?= $coverStyle ?>">
|
||
<?php if ($category !== ''): ?>
|
||
<span class="cover-category"><?= htmlspecialchars($category) ?></span>
|
||
<?php endif; ?>
|
||
</div>
|
||
<div class="card-body d-flex flex-column">
|
||
<h2 class="card-title">
|
||
<?php if ($isLocked): ?>
|
||
<?= htmlspecialchars($post['title']) ?>
|
||
<?php else: ?>
|
||
<a href="<?= htmlspecialchars($postUrl) ?>">
|
||
<?= htmlspecialchars($post['title']) ?>
|
||
</a>
|
||
<?php endif; ?>
|
||
</h2>
|
||
<p class="card-text flex-grow-1"><?= htmlspecialchars($preview) ?></p>
|
||
<div class="post-entry-meta mt-auto">
|
||
<?php if ($isAvantPremiere): ?>
|
||
<span class="text-muted">Disponible le <?= htmlspecialchars(date('d/m/Y \à H\hi', strtotime((string)($post['published_at'] ?? '')))) ?></span>
|
||
<?php else: ?>
|
||
<span><?= htmlspecialchars(date('d/m/Y', strtotime((string)($post['published_at'] ?? $post['created_at'] ?? '')))) ?></span>
|
||
<?php endif; ?>
|
||
<?php if (!$isLocked): ?>
|
||
<a href="<?= htmlspecialchars($postUrl) ?>" class="post-entry-read">→ lire</a>
|
||
<?php endif; ?>
|
||
</div>
|
||
</div>
|
||
<?php if (!$isLocked): ?>
|
||
<a href="<?= htmlspecialchars($postUrl) ?>" class="stretched-link"></a>
|
||
<?php endif; ?>
|
||
</article>
|
||
<?php endforeach; ?>
|
||
</div>
|
||
|
||
<?php if ($prevCursor !== null || $nextCursor !== null): ?>
|
||
<nav class="pagination-nav mt-5" aria-label="Navigation">
|
||
<?php
|
||
$hasCat = $filterCat !== '';
|
||
$catBase = $hasCat ? '/categorie/' . rawurlencode($filterCat) : null;
|
||
?>
|
||
<?php if ($prevCursor !== null): ?>
|
||
<?php
|
||
if ($prevCursor === '') {
|
||
$prevHref = $hasCat ? $catBase : '/';
|
||
} elseif ($hasCat) {
|
||
$prevHref = $catBase . '?cursor=' . rawurlencode($prevCursor);
|
||
} else {
|
||
$prevHref = '/page/' . rawurlencode($prevCursor);
|
||
}
|
||
?>
|
||
<a class="pagination-btn" href="<?= htmlspecialchars($prevHref) ?>">← Plus récents</a>
|
||
<?php endif; ?>
|
||
<?php if ($nextCursor !== null): ?>
|
||
<?php $nextHref = $hasCat ? $catBase . '?cursor=' . rawurlencode($nextCursor) : '/page/' . rawurlencode($nextCursor); ?>
|
||
<a class="pagination-btn ms-auto" href="<?= htmlspecialchars($nextHref) ?>">Plus anciens →</a>
|
||
<?php endif; ?>
|
||
</nav>
|
||
<?php endif; ?>
|
||
|
||
<?php
|
||
$_tagCats = array_filter(
|
||
$allCats ?? [],
|
||
static fn ($cat) => isLoggedIn() || !in_array($cat, $privateCats ?? [], true),
|
||
ARRAY_FILTER_USE_KEY
|
||
);
|
||
arsort($_tagCats);
|
||
if (!empty($_tagCats)):
|
||
$_minCount = min($_tagCats);
|
||
$_maxCount = max($_tagCats);
|
||
?>
|
||
<nav class="tag-cloud mt-5" aria-label="Catégories">
|
||
<?php foreach ($_tagCats as $_catName => $_catCount):
|
||
$_size = $_minCount === $_maxCount
|
||
? 1.1
|
||
: round(0.8 + ($_catCount - $_minCount) / ($_maxCount - $_minCount) * 0.9, 2);
|
||
$_active = ($filterCat === $_catName);
|
||
?>
|
||
<a href="/categorie/<?= rawurlencode($_catName) ?>"
|
||
class="tag-cloud-item<?= $_active ? ' active' : '' ?>">
|
||
<?= htmlspecialchars($_catName) ?>
|
||
<span class="tag-count"><?= $_catCount ?></span>
|
||
</a>
|
||
<?php endforeach; ?>
|
||
<?php if ($filterCat !== ''): ?>
|
||
<a href="/?cat=" class="tag-cloud-reset">× tout afficher</a>
|
||
<?php endif; ?>
|
||
</nav>
|
||
<?php endif; ?>
|
||
|
||
<?php if (function_exists('isLoggedIn') && isLoggedIn()): ?>
|
||
<a href="/new" class="fab-new" title="Nouvel article" aria-label="Nouvel article">+</a>
|
||
<?php endif; ?>
|
||
|
||
<?php
|
||
$content = ob_get_clean();
|
||
$title = siteTitle();
|
||
|
||
// Pages avec curseur = contenu non-canonique → noindex
|
||
if (!empty($cursor)) {
|
||
$metaRobots = 'noindex, follow';
|
||
$canonical = rtrim(APP_URL, '/') . '/';
|
||
} elseif ($filterCat !== '') {
|
||
$canonical = rtrim(APP_URL, '/') . '/categorie/' . rawurlencode($filterCat);
|
||
} else {
|
||
$canonical = rtrim(APP_URL, '/') . '/';
|
||
}
|
||
|
||
// JSON-LD WebSite sur la page d'accueil sans filtre
|
||
if (empty($cursor) && $filterCat === '') {
|
||
$jsonLd = json_encode([
|
||
'@context' => 'https://schema.org',
|
||
'@type' => 'WebSite',
|
||
'name' => siteTitle(),
|
||
'url' => rtrim(APP_URL, '/') . '/',
|
||
'description' => siteClaim(),
|
||
'inLanguage' => siteLang(),
|
||
], JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE);
|
||
}
|
||
|
||
$mainClass = 'container-fluid';
|
||
include __DIR__ . '/layout.php';
|