- Admin stats : sparklines SVG par page (120×28 px, courbe + dégradé),
carte « Pages les plus visitées » en pleine largeur
- AccessLogParser : données par jour (pages_by_day) sur 14 jours
- AccessLogParser : IPs privées/LAN exclues de la répartition réseau
- ArticleManager : suppression opérateur nullsafe superflu (PHPStan)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- post_view.php : barre de partage (mail, X, LinkedIn, Mastodon, copier, Web Share) sur articles publiés (#47)
- share.js : logique clipboard + navigator.share sans script tiers, compatible CSP (#47)
- addFile() : hardlink vers fichier identique si même hash16-size.ext dans un autre article (#35)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- getAll() : cache fichier articles_list.json, invalidé à chaque écriture (#16)
- layout.php : fingerprinting ?v=<hash> sur CSS/JS pour invalidation navigateur (#18)
- case 'view' : Last-Modified + 304 Not Modified pour les articles publiés (#18)
- case 'not_found' : logging JSON des 404 dans _logs/not_found.jsonl (#52)
- case 'view' : echo nu → templates/404.php pour brouillons/privés (#52)
- admin.js : clic sur ligne tableau → toggle bulk-check (#86)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- magic.php : GET=confirmation page, POST=consommation (protège vs scanners) (#27)
- verify_comment : email de notification à l'auteur de l'article (#44)
- login/index.php : rate limit par IP (MAGIC_MAX_PER_IP_HOUR=10) (#23)
- ArticleManager::duplicate() + route POST /duplicate/{uuid} + bouton ⧉ admin/articles (#7)
- post_view.php : cache JSON du rendu Markdown (invalidé sur mtime index.md) (#17)
- post_view.php : loading="lazy" sur toutes les <img> du contenu (#21)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- loadArticle($dir, false) dans loadAll() — meta.json seulement, pas d'index.md
- loadAll() enrichit les articles avec plain depuis search_index (1 lecture JSON)
- rebuildSearchIndex() lit index.md directement + ajoute featured au schéma
- getSearchIndex() rebuilde automatiquement si featured absent
- post_list, author_articles, author_profile : excerpts via plain, plus de Parsedown
- Ferme #24
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- mkArticleDir() crée les répertoires avec chmod 0775 explicite (bypass umask)
- delete() retourne bool et détecte l'échec sans reconstruire les index
- removeDir() supprime les warnings PHP (@unlink, @rmdir, @scandir)
- post_view.php affiche un message d'erreur si delete_failed=1
- index.php redirige vers l'article avec ?delete_failed=1 si échec
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- DATA_PATH : chemin /data hors document root, configurable via .env
(fallback sur BASE_PATH/data si absent)
- DataGit : auto-commit git sur toutes les écritures articles/livres
(create, update, delete, meta, tags, fichiers, liens…) sauf autosave
- UpdateChecker : getBranch() / getLastChecked() / clearCache(),
branche configurable via FOLIO_UPDATE_BRANCH (plus de main hardcodé)
- Admin dashboard : affiche la branche suivie, date du dernier contrôle,
bouton Vérifier pour forcer le check sans attendre le TTL
- CLAUDE.md : architecture DATA_PATH et flux de déploiement documentés
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- ArticleManager : invalider le cache si index.md est plus récent que meta.json
- migration_001 : touch(meta.json) après maj index.md pour forcer l'invalidation
- post_view.php : masquer le H1 initial du contenu (déjà affiché par le template)
- step1.php : en-tête "Modifier" sans le titre de l'article
- wizard.js : retirer scrollToCursor (erroné sur auto-resize) ; Ctrl+Home/End via scrollIntoView
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Remplace le formulaire unique par un wizard 5 étapes (création) et
6 étapes (édition) avec auto-sauvegarde en brouillon, détection de
tags depuis le texte (TagSuggester), aperçu SEO, diff avant validation
et plan Markdown dynamique dans l'éditeur.
Détail des changements :
- ArticleManager : +6 méthodes (updatePartialMeta, saveDraftOverlay,
getDraftOverlay, hasDraftOverlay, discardDraftOverlay, commitDraftOverlay)
- .htaccess : routes /new/{uuid}/{1-5} et /edit/{uuid}/{1-6}
- index.php : cases create et edit réécrits en switch($step),
nouveau case autosave_draft et edit_discard_draft
- assets/js/wizard.js : autosave debounce, auto-resize textarea,
scroll curseur, plan TOC dynamique, toggle pills tags
- templates/wizard/ : nav.php + step1..6.php
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- ArticleManager::getSearchIndex() : rebuild automatique si un UUID
référencé dans search_index.json n'existe plus sur le disque (article
supprimé hors CMS via rsync ou suppression manuelle). Même logique que
getBySlug() qui nettoie déjà le slug_index à la volée.
- bootstrap.php : lire SESSION_NAME depuis $_ENV avant session_start(),
permettant de personnaliser le nom du cookie de session via le .env.
- oidc/{start,callback,me}.php : inverser l'ordre des require pour charger
config.php (dotenv) avant bootstrap.php, condition nécessaire pour que
SESSION_NAME soit disponible au démarrage de la session.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>