feat : notion de livre — grouper des pages en série ordonnée
Ajoute un concept de "livre" (série de pages ordonnées) avec : - BookManager : CRUD JSON dans data/books/<slug>.json - Route /book/<slug> → page de sommaire (table des matières) - Navigation chapitre ← → en bas de chaque article membre du livre - Bandeau "Chapitre X/N — Nom du livre" en haut de l'article - Admin → onglet Livres : créer, éditer, supprimer un livre, ajouter/ordonner les pages via textarea slug Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -15,6 +15,9 @@ RewriteRule ^ - [L]
|
|||||||
# URL propre pour les articles : /post/<slug>
|
# URL propre pour les articles : /post/<slug>
|
||||||
RewriteRule ^post/([a-z0-9][a-z0-9-]*)/?$ /index.php?action=view&slug=$1 [L,QSA]
|
RewriteRule ^post/([a-z0-9][a-z0-9-]*)/?$ /index.php?action=view&slug=$1 [L,QSA]
|
||||||
|
|
||||||
|
# Livres : /book/<slug>
|
||||||
|
RewriteRule ^book/([a-z0-9][a-z0-9-]*)/?$ /index.php?action=book&book_slug=$1 [L,QSA]
|
||||||
|
|
||||||
# Filtre par catégorie : /categorie/<nom>
|
# Filtre par catégorie : /categorie/<nom>
|
||||||
RewriteRule ^categorie/(.+?)/?$ /index.php?cat=$1 [L,QSA,B]
|
RewriteRule ^categorie/(.+?)/?$ /index.php?cat=$1 [L,QSA,B]
|
||||||
|
|
||||||
|
|||||||
@@ -1807,3 +1807,171 @@ footer.mt-5 { margin-top: 0 !important; }
|
|||||||
color: var(--vl-muted);
|
color: var(--vl-muted);
|
||||||
margin-top: 0.15rem;
|
margin-top: 0.15rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ─── Livres ─────────────────────────────────────────────────────── */
|
||||||
|
|
||||||
|
/* Bandeau dans un article appartenant à un livre */
|
||||||
|
.book-article-banner {
|
||||||
|
border-radius: var(--vl-radius);
|
||||||
|
background: var(--vl-accent-soft);
|
||||||
|
border: 1px solid rgba(79,70,229,.18);
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
.book-article-banner-link {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 0.6rem;
|
||||||
|
padding: 0.6rem 1rem;
|
||||||
|
text-decoration: none;
|
||||||
|
color: var(--vl-accent);
|
||||||
|
transition: background 0.15s;
|
||||||
|
}
|
||||||
|
.book-article-banner-link:hover {
|
||||||
|
background: rgba(79,70,229,.08);
|
||||||
|
color: var(--vl-accent-dark);
|
||||||
|
}
|
||||||
|
.book-article-banner-icon { font-size: 1.1rem; flex-shrink: 0; }
|
||||||
|
.book-article-banner-text { flex: 1; font-size: 0.875rem; }
|
||||||
|
.book-article-banner-cta { font-size: 0.8rem; opacity: .75; white-space: nowrap; }
|
||||||
|
|
||||||
|
/* Navigation précédent/suivant en bas d'article */
|
||||||
|
.book-chapter-nav {
|
||||||
|
border-top: 1px solid var(--vl-border);
|
||||||
|
margin-top: 1.5rem;
|
||||||
|
padding-top: 1rem;
|
||||||
|
}
|
||||||
|
.book-chapter-nav-inner {
|
||||||
|
display: flex;
|
||||||
|
gap: 0.75rem;
|
||||||
|
align-items: stretch;
|
||||||
|
}
|
||||||
|
.book-nav-btn {
|
||||||
|
flex: 1;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
padding: 0.65rem 0.875rem;
|
||||||
|
background: var(--vl-surface);
|
||||||
|
border: 1px solid var(--vl-border);
|
||||||
|
border-radius: var(--vl-radius);
|
||||||
|
text-decoration: none;
|
||||||
|
color: var(--vl-text);
|
||||||
|
transition: border-color 0.15s, box-shadow 0.15s;
|
||||||
|
min-width: 0;
|
||||||
|
}
|
||||||
|
.book-nav-btn:hover {
|
||||||
|
border-color: var(--vl-accent);
|
||||||
|
box-shadow: var(--vl-shadow-sm);
|
||||||
|
color: var(--vl-text);
|
||||||
|
}
|
||||||
|
.book-nav-btn--disabled {
|
||||||
|
opacity: .45;
|
||||||
|
cursor: default;
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
.book-nav-btn--next { text-align: right; }
|
||||||
|
.book-nav-dir {
|
||||||
|
font-size: 0.72rem;
|
||||||
|
font-weight: 600;
|
||||||
|
text-transform: uppercase;
|
||||||
|
letter-spacing: .04em;
|
||||||
|
color: var(--vl-muted);
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
.book-nav-title {
|
||||||
|
font-size: 0.875rem;
|
||||||
|
font-weight: 500;
|
||||||
|
display: block;
|
||||||
|
margin-top: 0.15rem;
|
||||||
|
overflow: hidden;
|
||||||
|
display: -webkit-box;
|
||||||
|
-webkit-line-clamp: 2;
|
||||||
|
-webkit-box-orient: vertical;
|
||||||
|
}
|
||||||
|
.book-nav-toc {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
padding: 0.5rem 0.75rem;
|
||||||
|
border: 1px solid var(--vl-border);
|
||||||
|
border-radius: var(--vl-radius);
|
||||||
|
color: var(--vl-muted);
|
||||||
|
text-decoration: none;
|
||||||
|
font-size: 1.1rem;
|
||||||
|
transition: border-color 0.15s, color 0.15s;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
.book-nav-toc:hover {
|
||||||
|
border-color: var(--vl-accent);
|
||||||
|
color: var(--vl-accent);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Page sommaire d'un livre (/book/<slug>) */
|
||||||
|
.book-page { max-width: 720px; margin: 0 auto; padding: 2rem 0; }
|
||||||
|
.book-label {
|
||||||
|
font-size: 0.75rem;
|
||||||
|
font-weight: 700;
|
||||||
|
text-transform: uppercase;
|
||||||
|
letter-spacing: .08em;
|
||||||
|
color: var(--vl-accent);
|
||||||
|
}
|
||||||
|
.book-chapters {
|
||||||
|
list-style: none;
|
||||||
|
padding: 0;
|
||||||
|
margin: 0;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 0.5rem;
|
||||||
|
}
|
||||||
|
.book-chapter-link {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 0.875rem;
|
||||||
|
padding: 0.75rem 1rem;
|
||||||
|
background: var(--vl-surface);
|
||||||
|
border: 1px solid var(--vl-border);
|
||||||
|
border-radius: var(--vl-radius);
|
||||||
|
text-decoration: none;
|
||||||
|
color: var(--vl-text);
|
||||||
|
transition: border-color 0.15s, box-shadow 0.15s;
|
||||||
|
}
|
||||||
|
.book-chapter-link:hover {
|
||||||
|
border-color: var(--vl-accent);
|
||||||
|
box-shadow: var(--vl-shadow-sm);
|
||||||
|
color: var(--vl-text);
|
||||||
|
}
|
||||||
|
.book-chapter-num {
|
||||||
|
width: 1.75rem;
|
||||||
|
height: 1.75rem;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
background: var(--vl-accent-soft);
|
||||||
|
color: var(--vl-accent);
|
||||||
|
border-radius: 50%;
|
||||||
|
font-size: 0.8rem;
|
||||||
|
font-weight: 700;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
.book-chapter-thumb {
|
||||||
|
width: 56px;
|
||||||
|
height: 44px;
|
||||||
|
border-radius: 6px;
|
||||||
|
flex-shrink: 0;
|
||||||
|
background: var(--vl-accent-soft);
|
||||||
|
background-size: cover;
|
||||||
|
background-position: center;
|
||||||
|
}
|
||||||
|
.book-chapter-body { flex: 1; min-width: 0; }
|
||||||
|
.book-chapter-title {
|
||||||
|
font-size: 0.9375rem;
|
||||||
|
font-weight: 600;
|
||||||
|
line-height: 1.3;
|
||||||
|
overflow: hidden;
|
||||||
|
white-space: nowrap;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
}
|
||||||
|
.book-chapter-meta {
|
||||||
|
font-size: 0.78rem;
|
||||||
|
color: var(--vl-muted);
|
||||||
|
margin-top: 0.15rem;
|
||||||
|
}
|
||||||
|
|||||||
@@ -0,0 +1,131 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
class BookManager
|
||||||
|
{
|
||||||
|
public function __construct(private string $booksDir)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
// ------------------------------------------------------------------ //
|
||||||
|
// Lecture
|
||||||
|
// ------------------------------------------------------------------ //
|
||||||
|
|
||||||
|
public function getAll(): array
|
||||||
|
{
|
||||||
|
$books = [];
|
||||||
|
if (!is_dir($this->booksDir)) {
|
||||||
|
return $books;
|
||||||
|
}
|
||||||
|
foreach (scandir($this->booksDir) as $file) {
|
||||||
|
if (!str_ends_with($file, '.json')) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
$raw = file_get_contents($this->booksDir . '/' . $file);
|
||||||
|
if ($raw === false) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
$book = json_decode($raw, true);
|
||||||
|
if (!is_array($book) || empty($book['slug'])) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
$books[] = $book;
|
||||||
|
}
|
||||||
|
usort($books, static fn ($a, $b) => strcmp($a['title'] ?? '', $b['title'] ?? ''));
|
||||||
|
return $books;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getBySlug(string $slug): ?array
|
||||||
|
{
|
||||||
|
$path = $this->bookPath($slug);
|
||||||
|
if (!file_exists($path)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
$raw = file_get_contents($path);
|
||||||
|
if ($raw === false) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
$book = json_decode($raw, true);
|
||||||
|
return is_array($book) && !empty($book['slug']) ? $book : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cherche dans quel livre se trouve un article (par son slug).
|
||||||
|
* Retourne le contexte complet ou null si l'article n'appartient à aucun livre.
|
||||||
|
*
|
||||||
|
* @return array{book: array, position: int, total: int, prev: ?string, next: ?string}|null
|
||||||
|
*/
|
||||||
|
public function findForArticle(string $articleSlug): ?array
|
||||||
|
{
|
||||||
|
foreach ($this->getAll() as $book) {
|
||||||
|
$arts = $book['articles'] ?? [];
|
||||||
|
$pos = array_search($articleSlug, $arts, true);
|
||||||
|
if ($pos === false) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
$pos = (int) $pos;
|
||||||
|
return [
|
||||||
|
'book' => $book,
|
||||||
|
'position' => $pos + 1,
|
||||||
|
'total' => count($arts),
|
||||||
|
'prev' => $pos > 0 ? $arts[$pos - 1] : null,
|
||||||
|
'next' => $pos < count($arts) - 1 ? $arts[$pos + 1] : null,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ------------------------------------------------------------------ //
|
||||||
|
// Écriture
|
||||||
|
// ------------------------------------------------------------------ //
|
||||||
|
|
||||||
|
public function save(array $book): void
|
||||||
|
{
|
||||||
|
$slug = $this->sanitizeSlug($book['slug'] ?? '');
|
||||||
|
if ($slug === '') {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
$book['slug'] = $slug;
|
||||||
|
$book['articles'] = array_values(array_filter(array_map('strval', $book['articles'] ?? [])));
|
||||||
|
if (!is_dir($this->booksDir)) {
|
||||||
|
mkdir($this->booksDir, 0755, true);
|
||||||
|
}
|
||||||
|
file_put_contents(
|
||||||
|
$this->bookPath($slug),
|
||||||
|
json_encode($book, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES) . "\n"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function delete(string $slug): void
|
||||||
|
{
|
||||||
|
$path = $this->bookPath($slug);
|
||||||
|
if (file_exists($path)) {
|
||||||
|
@unlink($path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ------------------------------------------------------------------ //
|
||||||
|
// Helpers
|
||||||
|
// ------------------------------------------------------------------ //
|
||||||
|
|
||||||
|
private function bookPath(string $slug): string
|
||||||
|
{
|
||||||
|
return $this->booksDir . '/' . $slug . '.json';
|
||||||
|
}
|
||||||
|
|
||||||
|
private function sanitizeSlug(string $slug): string
|
||||||
|
{
|
||||||
|
$map = [
|
||||||
|
'à' => 'a', 'â' => 'a', 'ä' => 'a',
|
||||||
|
'é' => 'e', 'è' => 'e', 'ê' => 'e', 'ë' => 'e',
|
||||||
|
'î' => 'i', 'ï' => 'i',
|
||||||
|
'ô' => 'o', 'ö' => 'o',
|
||||||
|
'ù' => 'u', 'û' => 'u', 'ü' => 'u',
|
||||||
|
'ç' => 'c', 'æ' => 'ae', 'œ' => 'oe',
|
||||||
|
];
|
||||||
|
$slug = mb_strtolower(strtr(trim($slug), $map), 'UTF-8');
|
||||||
|
$slug = (string) preg_replace('/[^a-z0-9]+/', '-', $slug);
|
||||||
|
return trim($slug, '-');
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -69,6 +69,10 @@ function adminStatusBadge(array $a, int $now): string
|
|||||||
<a class="nav-link <?= $tab === 'books' ? 'active' : '' ?>"
|
<a class="nav-link <?= $tab === 'books' ? 'active' : '' ?>"
|
||||||
href="/admin/books">Livres</a>
|
href="/admin/books">Livres</a>
|
||||||
</li>
|
</li>
|
||||||
|
<li class="nav-item">
|
||||||
|
<a class="nav-link <?= $tab === 'stats' ? 'active' : '' ?>"
|
||||||
|
href="/admin/stats">Statistiques</a>
|
||||||
|
</li>
|
||||||
<?php endif; ?>
|
<?php endif; ?>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
@@ -1300,6 +1304,12 @@ foreach (COLOR_PALETTE_16 as $_i => $_rgb):
|
|||||||
|
|
||||||
<?php endif; ?>
|
<?php endif; ?>
|
||||||
|
|
||||||
|
<?php if ($tab === 'stats' && isAdmin()): ?>
|
||||||
|
|
||||||
|
<?php include __DIR__ . '/admin_stats.php'; ?>
|
||||||
|
|
||||||
|
<?php endif; ?>
|
||||||
|
|
||||||
<?php
|
<?php
|
||||||
$content = ob_get_clean();
|
$content = ob_get_clean();
|
||||||
$title = 'Administration — ' . siteTitle();
|
$title = 'Administration — ' . siteTitle();
|
||||||
|
|||||||
@@ -0,0 +1,62 @@
|
|||||||
|
<?php ob_start(); ?>
|
||||||
|
|
||||||
|
<div class="book-page">
|
||||||
|
|
||||||
|
<div class="book-header mb-4">
|
||||||
|
<p class="book-label">Livre</p>
|
||||||
|
<h1 class="h2 mb-2"><?= htmlspecialchars($book['title']) ?></h1>
|
||||||
|
<?php if (!empty($book['description'])): ?>
|
||||||
|
<p class="lead text-muted"><?= htmlspecialchars($book['description']) ?></p>
|
||||||
|
<?php endif; ?>
|
||||||
|
<p class="text-muted small"><?= count($bookArticles) ?> page<?= count($bookArticles) > 1 ? 's' : '' ?></p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<?php if (empty($bookArticles)): ?>
|
||||||
|
<p class="text-muted">Ce livre ne contient pas encore de pages publiées.</p>
|
||||||
|
<?php else: ?>
|
||||||
|
<ol class="book-chapters">
|
||||||
|
<?php foreach ($bookArticles as $i => $a):
|
||||||
|
$cat = trim($a['category'] ?? '');
|
||||||
|
$gradient = coverGradient($cat !== '' ? $cat : $a['uuid'], $allCats);
|
||||||
|
$cover = $a['cover'] ?? '';
|
||||||
|
$date = $a['published_at'] ? date('d/m/Y', strtotime((string)$a['published_at'])) : '';
|
||||||
|
?>
|
||||||
|
<li class="book-chapter">
|
||||||
|
<a href="/post/<?= rawurlencode($a['slug'] ?? '') ?>" class="book-chapter-link">
|
||||||
|
<span class="book-chapter-num"><?= $i + 1 ?></span>
|
||||||
|
<div class="book-chapter-thumb" style="<?= $cover !== ''
|
||||||
|
? 'background-image:url(/file?uuid=' . rawurlencode($a['uuid']) . '&name=' . rawurlencode($cover) . ');background-size:cover;background-position:center'
|
||||||
|
: 'background:' . htmlspecialchars($gradient) ?>">
|
||||||
|
</div>
|
||||||
|
<div class="book-chapter-body">
|
||||||
|
<div class="book-chapter-title"><?= htmlspecialchars($a['title'] ?? '') ?></div>
|
||||||
|
<div class="book-chapter-meta">
|
||||||
|
<?php if ($cat !== ''): ?><?= htmlspecialchars($cat) ?><?php endif; ?>
|
||||||
|
<?php if ($cat !== '' && $date !== ''): ?> · <?php endif; ?>
|
||||||
|
<?php if ($date !== ''): ?><?= $date ?><?php endif; ?>
|
||||||
|
</div>
|
||||||
|
<?php if (!$a['published']): ?>
|
||||||
|
<span class="badge bg-secondary small">Brouillon</span>
|
||||||
|
<?php endif; ?>
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
</ol>
|
||||||
|
<?php endif; ?>
|
||||||
|
|
||||||
|
<?php if (function_exists('isAdmin') && isAdmin()): ?>
|
||||||
|
<div class="mt-4 text-end">
|
||||||
|
<a href="/admin/books?edit=<?= rawurlencode($book['slug']) ?>" class="btn btn-sm btn-outline-secondary">
|
||||||
|
✎ Modifier ce livre
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
<?php endif; ?>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<?php
|
||||||
|
$content = ob_get_clean();
|
||||||
|
$title = htmlspecialchars($book['title']) . ' — ' . siteTitle();
|
||||||
|
$canonical = rtrim(APP_URL, '/') . '/book/' . rawurlencode($book['slug']);
|
||||||
|
include __DIR__ . '/layout.php';
|
||||||
@@ -70,6 +70,19 @@ $externalLinks = $article['external_links'] ?? [];
|
|||||||
<!-- Colonne principale -->
|
<!-- Colonne principale -->
|
||||||
<div class="col">
|
<div class="col">
|
||||||
|
|
||||||
|
<?php if (!empty($bookContext)): ?>
|
||||||
|
<div class="book-article-banner mb-3">
|
||||||
|
<a href="/book/<?= rawurlencode($bookContext['book']['slug']) ?>" class="book-article-banner-link">
|
||||||
|
<span class="book-article-banner-icon">📖</span>
|
||||||
|
<span class="book-article-banner-text">
|
||||||
|
Chapitre <?= $bookContext['position'] ?>/<?= $bookContext['total'] ?> —
|
||||||
|
<strong><?= htmlspecialchars($bookContext['book']['title']) ?></strong>
|
||||||
|
</span>
|
||||||
|
<span class="book-article-banner-cta">Voir le sommaire →</span>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
<?php endif; ?>
|
||||||
|
|
||||||
<div class="card mb-4">
|
<div class="card mb-4">
|
||||||
<?php if (!$article['published']): ?>
|
<?php if (!$article['published']): ?>
|
||||||
<div class="draft-ribbon">Brouillon</div>
|
<div class="draft-ribbon">Brouillon</div>
|
||||||
@@ -163,6 +176,41 @@ $hasSources = (!empty($externalLinks) || !empty($files))
|
|||||||
<div class="card-text post-content">
|
<div class="card-text post-content">
|
||||||
<?= $_renderedContent ?>
|
<?= $_renderedContent ?>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<?php if (!empty($bookContext)): ?>
|
||||||
|
<nav class="book-chapter-nav">
|
||||||
|
<div class="book-chapter-nav-inner">
|
||||||
|
<?php if (!empty($bookContext['prev_article'])): ?>
|
||||||
|
<a href="/post/<?= rawurlencode($bookContext['prev_article']['slug'] ?? '') ?>"
|
||||||
|
class="book-nav-btn book-nav-btn--prev">
|
||||||
|
<span class="book-nav-dir">← Précédent</span>
|
||||||
|
<span class="book-nav-title"><?= htmlspecialchars($bookContext['prev_article']['title'] ?? '') ?></span>
|
||||||
|
</a>
|
||||||
|
<?php else: ?>
|
||||||
|
<span class="book-nav-btn book-nav-btn--prev book-nav-btn--disabled">
|
||||||
|
<span class="book-nav-dir">Premier chapitre</span>
|
||||||
|
</span>
|
||||||
|
<?php endif; ?>
|
||||||
|
|
||||||
|
<a href="/book/<?= rawurlencode($bookContext['book']['slug']) ?>"
|
||||||
|
class="book-nav-toc" title="Sommaire du livre">
|
||||||
|
☰
|
||||||
|
</a>
|
||||||
|
|
||||||
|
<?php if (!empty($bookContext['next_article'])): ?>
|
||||||
|
<a href="/post/<?= rawurlencode($bookContext['next_article']['slug'] ?? '') ?>"
|
||||||
|
class="book-nav-btn book-nav-btn--next">
|
||||||
|
<span class="book-nav-dir">Suivant →</span>
|
||||||
|
<span class="book-nav-title"><?= htmlspecialchars($bookContext['next_article']['title'] ?? '') ?></span>
|
||||||
|
</a>
|
||||||
|
<?php else: ?>
|
||||||
|
<span class="book-nav-btn book-nav-btn--next book-nav-btn--disabled">
|
||||||
|
<span class="book-nav-dir">Dernier chapitre</span>
|
||||||
|
</span>
|
||||||
|
<?php endif; ?>
|
||||||
|
</div>
|
||||||
|
</nav>
|
||||||
|
<?php endif; ?>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user