'a','â' => 'a','ä' => 'a','á' => 'a','é' => 'e','è' => 'e','ê' => 'e','ë' => 'e', 'î' => 'i','ï' => 'i','í' => 'i','ô' => 'o','ö' => 'o','ó' => 'o','ù' => 'u','û' => 'u', 'ü' => 'u','ú' => 'u','ç' => 'c','ñ' => 'n','æ' => 'ae','œ' => 'oe', ]; $_tocItems = []; $_tocSeen = []; // Le titre H1 est déjà affiché par le template ; on le retire du rendu. $_rawForRender = preg_replace('/^\s*# [^\n]*\n*/u', '', $rawContent); $_renderedContent = preg_replace_callback( '/<(h[23])>(.+?)<\/h[23]>/i', function ($m) use (&$_tocItems, &$_tocSeen, $_accentMap) { $tag = $m[1]; $inner = $m[2]; $level = (int) substr($tag, 1); $plain = strip_tags($inner); $slug = trim(preg_replace( '/[^a-z0-9]+/', '-', mb_strtolower(strtr($plain, $_accentMap), 'UTF-8') ), '-') ?: 'section'; if (isset($_tocSeen[$slug])) { $_tocSeen[$slug]++; $id = $slug . '-' . $_tocSeen[$slug]; } else { $_tocSeen[$slug] = 0; $id = $slug; } $_tocItems[] = ['level' => $level, 'text' => $plain, 'id' => $id]; return "<{$tag} id=\"" . htmlspecialchars($id) . "\">{$inner}"; }, $Parsedown->text($_rawForRender) ); ob_start(); $coverFile = $article['cover'] ?? ''; $ogImage = $coverFile !== '' ? url('file?uuid=' . rawurlencode($article['uuid']) . '&name=' . rawurlencode($coverFile)) : null; $category = trim((string)($article['category'] ?? '')); $gradient = coverGradient($category !== '' ? $category : $article['uuid'], $allCats ?? []); // Pièces jointes (hors fichiers intégrés, thumbs et cover) $attachments = []; if ($files) { $referenced = []; preg_match_all('/\(\/file\?uuid=[^&]+&name=([^)]+)\)/', $rawContent, $m); foreach ($m[1] as $encodedName) { $referenced[rawurldecode($encodedName)] = true; } $attachments = array_values(array_filter( $files, static fn ($f) => !isset($referenced[$f['name']]) && !str_starts_with($f['name'], '_thumb_') && $f['name'] !== $coverFile )); } $externalLinks = $article['external_links'] ?? []; ?>
Brouillon
Privé
> <?= htmlspecialchars($article['title']) ?>
← Retour ✎ Modifier 🗑 Supprimer

·

ℹ Sources ['👍', 'Utile'], 'important' => ['🔥', 'Important'], 'interesting' => ['🤔', 'À creuser'], ]; ?>
[$icon, $label]): ?>
= 3): ?>
Table des matières
  • À lire aussi
  • Réactions
  • Commentaires
Rétroliens
Pièces jointes
▶
♪
📎
Ko · ·
Liens & sources
📑
↗
· · PDF p.
text($article['content'])); $plain = preg_replace('/\s+/', ' ', $plain); $seoDescription = mb_strimwidth(trim((string)$plain), 0, 155, '…'); } // og:image : cover puis fallback og_image du meta if ($ogImage === null || $ogImage === '') { $ogImage = $article['og_image'] ?? ''; } // Auteur : nom et URL de profil résolus depuis le champ author du JSON de l'article $metaAuthor = $authorName; $metaAuthorUrl = $authorProfileUrl; // JSON-LD Article $jsonLdData = [ '@context' => 'https://schema.org', '@type' => 'BlogPosting', 'headline' => $seoTitle, 'description' => $seoDescription, 'url' => $canonical, 'datePublished' => date('c', strtotime((string)$articlePublishedAt)), 'dateModified' => date('c', strtotime((string)($article['updated_at'] ?? $articlePublishedAt))), 'author' => array_filter([ '@type' => 'Person', 'name' => $metaAuthor !== '' ? $metaAuthor : siteTitle(), 'url' => $metaAuthorUrl !== '' ? $metaAuthorUrl : null, ]), 'publisher' => [ '@type' => 'Blog', 'name' => siteTitle(), 'url' => rtrim(APP_URL, '/'), ], 'inLanguage' => siteLang(), ]; if (!empty($ogImage)) { $jsonLdData['image'] = $ogImage; } $jsonLd = json_encode($jsonLdData, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE); include __DIR__ . '/layout.php';