40656631ba
- Admin stats : clic sur un réseau AS affiche les IPs avec mini sparkline 14 jours + articles/livres consultés - AccessLogParser : calcul ip_data (daily + top paths) inclus dans le cache stats - Suppression du tableau statique "Répartition par réseau" (fusionné dans accordéon pays) - PHP-CS-Fixer appliqué sur l'ensemble des fichiers modifiés Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
20 KiB
20 KiB
Changelog
Toutes les modifications notables sont documentées ici. Format : Keep a Changelog — versionnage semver.
[1.6.28] - 2026-05-19
Ajouté
- Admin stats : drill-down AS → IPs dans l'accordéon « Visiteurs par pays » — mini sparkline 14 jours + articles/livres consultés par IP
- Admin stats :
ip_datadans le cache stats (daily + top paths par IP publique)
Supprimé
- Admin stats : section « Répartition par réseau » (fusionnée dans l'accordéon pays)
[1.6.27] - 2026-05-19
Ajouté
- Admin stats : sparklines SVG 14 jours par page dans « Pages les plus visitées » — courbe + dégradé, carte pleine largeur (#101)
Corrigé
- Admin stats : IPs privées/LAN exclues de la répartition par réseau (Uptime Kuma et hairpin NAT ne polluent plus les stats) (#102)
[1.6.26] - 2026-05-16
Ajouté
- Page publique
/books— catalogue de tous les livres avec ≥ 1 article publié, cards cover/titre/description/nombre de pages (#99) - Accueil : section « Livres » (max 6) après les redécouvertes avec lien « Voir tous → /books » (#100)
[1.6.25] - 2026-05-16
Ajouté
- Admin : onglet « IA » — statut provider/clé, sélecteur
anthropic/claude_code, champ modèle, procédure d'installation CLI, sauvegarde danssite_settings.json(#97) AiService: support du provider Claude Code CLI viaproc_open+ lecture provider/modèle depuisSiteSettings(#97)- Éditeur : bouton IA unique « Analyser et proposer » — un seul appel retourne l'analyse critique et la réécriture via séparateur
===CRITIQUE===/===REWRITE===(#96)
Corrigé
- Éditeur IA : boutons placés dans
wizard/step1.php(la vraie page d'édition) ;ai-editor.jsadapté pour#wz-contentet extraction du titre depuis le Markdown (#96) - Sécurité CSP : extraction du
<script>inline decomments_section.phpverscomments.js(#95) - Sécurité CSP : remplacement du
onclickinline danswizard/step6.phppardata-confirm-discard+ listener dansadmin.js(#95) - Sécurité CSP : remplacement du
oninputinline danspost_confirm.phppar unaddEventListenerdanspost_confirm.js(#95)
[1.6.24] - 2026-05-16
Ajouté
- Éditeur : intégration IA — service
AiService, routeai_query, scriptai-editor.js, cléANTHROPIC_API_KEYdans.env(#96)
[1.6.23] - 2026-05-16
Ajouté
- Section « Historique » dans la sidebar des articles (connectés) : liste des révisions avec lien vers le diff (#82)
[1.6.22] - 2026-05-16
Ajouté
- Widget de notation ★ (1-5 étoiles) sur les articles, accessible aux utilisateurs connectés ; affiche la moyenne et le nombre de votes pour tous (#13)
- Admin
flux: onglet listant tous les flux RSS agrégés avec action de suppression admin (#87)
[1.6.21] - 2026-05-16
Ajouté
- Feed RSS : balise
<media:thumbnail>avec l'image de couverture de l'article (namespacemedia:) (#90) - Admin livres : slug généré automatiquement depuis le titre à la création (#89)
- Admin livres : champ de filtre texte en temps réel sur le sélecteur « Ajouter une page » (#89)
Corrigé
autoSeoDesc: décodage des entités HTML (&, …) + suppression du titre en tête de description (#91)post_confirm.js: guard null sur#confirm-slugabsent (étape 5 du wizard) — plus d'erreur JS (#91)
[1.6.20] - 2026-05-16
Ajouté
- Barre de partage sur les articles publiés : mail, X, LinkedIn, Mastodon, copie de lien, Web Share API mobile (#47)
- Déduplication des images uploadées par hardlink si même hash+taille existe déjà dans un autre article (#35)
[1.6.19] - 2026-05-16
Ajouté
admin/articles: clic sur la ligne entière pour cocher/décocher la case de sélection bulk (#86)- Cache HTTP
Last-Modified+ réponse304 Not Modifiedpour les articles publiés (#18) - Fingerprinting des assets CSS/JS dans
layout.php(?v=<hash>) pour invalidation automatique du cache navigateur (#18) - Cache fichier
_cache/articles_list.jsonpourgetAll()— invalidé à chaque écriture, élimine le scan complet par requête (#16) - Logging des 404 dans
DATA_PATH/_logs/not_found.json(url, referer, user-agent, date) (#52)
Corrigé
case 'view': les accès refusés (brouillon, avant-première, catégorie privée) utilisent désormaistemplates/404.phpau lieu d'unechonu (#52)
[1.6.18] - 2026-05-16
Ajouté
- Lien magique : page de confirmation GET avant consommation POST — protège contre les scanners email (#27)
- Lien magique : notification email à l'auteur de l'article lors de la vérification d'un commentaire (#44)
- Lien magique : rate limit par IP (
MAGIC_MAX_PER_IP_HOUR, défaut 10/h) en plus du rate limit par email (#23) ArticleManager::duplicate()+ route/duplicate/{uuid}+ bouton ⧉ dansadmin/articles(#7)- Cache du rendu Markdown par article (
_cache/content_rendered.json, invalidé surmtimedeindex.md) (#17) - Lazy loading (
loading="lazy") sur toutes les images du contenu Markdown (#21)
[1.6.17] - 2026-05-16
Ajouté
- RSS : élément
<content:encoded>avec HTML complet par article + namespacecontent(#42) - RSS : filtre
?category=nom— flux filtré par catégorie, titre et description du channel adaptés (#43) - Commentaires : cookie
cmt_name/cmt_email(1 an) pour pré-remplir le formulaire à la prochaine visite (#51) flux/: bandeau d'alerte admin listant les feeds en erreur (URL, label, email) (#45)admin/emails: bouton « Voir ↗ » ouvre le contenu HTML de l'email dans un nouvel onglet via/admin/email-preview/{id}(#37)
Modifié
- RSS :
<description>utilise désormais le champplainpré-calculé (fix : contenu vide depuis v1.6.14) (#42)
[1.6.16] - 2026-05-16
Ajouté
SearchLogParser: paramètre$days(7 ou 14) — cache distinct par période, filtre logFiles par date (#46)admin/searches: boutons 7 j / 14 j pour choisir la fenêtre d'analyse (#46)
Modifié
SearchLogParser: tri par visiteurs uniques (IPs distinctes) au lieu de hits bruts — colonne renommée « Visiteurs » (#41)- URL inconnue / article introuvable : redirection 302 vers
/search?q=…au lieu de page 404 (#57) edit_tags: sections « Abréviations » et « Noms composés » masquées si des valeurs connues existent pour le type (#48)
[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 filtrefilter_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 verspost/<slug>(#83)
[1.6.14] - 2026-05-15
Modifié
- Perf :
getAll()ne charge plus le contenu Markdown —loadArticle()reçoit$withContent = falsedansloadAll(), seulgetByUuid()lit encoreindex.md(#24) - Perf :
search_index.jsonenrichi du champfeatured;rebuildSearchIndex()litindex.mddirectement (indépendant du cache article) - Perf : excerpts dans
post_list,author_articles,author_profileproviennent du champplainpré-calculé — plus de passage par Parsedown (#24)
[1.6.13] - 2026-05-15
Ajouté
- Typographie : guillemets droits convertis en guillemets courbes (
"→"/",'→'/') dans le rendu des articles — blocs<code>et<pre>préservés (#15)
Corrigé
- Suppression du dead code :
AuthService,UserRepositoryetDomain\User— incompatibles avec le système de session actuel, aucune référence active (#19) - Factorisation des helpers
env()etdb()danssrc/helpers.php, chargé parconfig/config.php— plus de triple définition dans les pages login/OIDC (#22)
[1.6.12] - 2026-05-15
Ajouté
- Wizard édition — étape SEO : sélecteur d'image de couverture (og:image) désormais affiché en mode édition (était limité à la création) — sélection parmi les fichiers images existants, appliquée immédiatement via
setCover()et mémorisée dans le draft overlay
[1.6.11] - 2026-05-15
Corrigé
- En mode édition, le slug de l'article n'est plus jamais modifié : suppression du
hidden[slug]dans l'étape 6 de confirmation et du bloc qui le propagait dans le draft overlay
[1.6.10] - 2026-05-15
Corrigé
- Suppression d'article échouait silencieusement quand le répertoire UUID appartenait à un autre utilisateur que
www-data(permissions2755→ groupe sans écriture) —removeDir()échouait sans erreur visible et l'article restait accessible ArticleManager::delete()retourne maintenantbool: si le répertoire existe encore après tentative de suppression, les index ne sont pas reconstruits et l'utilisateur est redirigé vers l'article avec un message d'erreurremoveDir(): erreurs PHP supprimées silencieusement (@unlink,@rmdir,@scandir) pour éviter les warnings qui cassaient les redirectsmkArticleDir(): nouvelle méthode privée qui crée les répertoires d'articles avecchmod 0775explicite (contourne le umask), garantissant quewww-data(groupe) a toujours les droits d'écriture
[Unreleased]
[1.6.9] - 2026-05-15
Ajouté
/admin/articles: tri par Titre (A→Z / Z→A) et par Date (publication) en cliquant les en-têtes de colonne — indicateur ↑ / ↓ sur la colonne active, paramètressortetdirpréservés lors du filtrage
[1.6.8] - 2026-05-15
Corrigé
- Tous les scripts inline déplacés vers des fichiers JS statiques (
density-fouc.js,density.js,trending-home.js,admin-stats.js) — conformité CSPscript-src 'self'(varlog) onclick/onchangeinline dansadmin.phpmigrés versadmin.js- Densité M (980 px) définie comme valeur par défaut au lieu de L (pleine largeur)
[1.6.7] - 2026-05-15
Ajouté
- Sélecteur de densité L / M / S sur la page liste : pleine largeur (défaut), normal (980 px), compact (660 px) — préférence persistée dans
localStorage
[1.6.6] - 2026-05-15
Modifié
- Page d'accueil "Meilleures audiences" : chargement AJAX depuis le flux RSS XML
/trending?period=1h(DOMParser côté client, plus de rendu PHP) /admin/statssection "Pages les plus visitées" : chargement AJAX depuis le flux RSS XML/trending?period=14d— plus de parsing de logs direct pour cette colonne/admin/stats: suppression detopGroupedpour les pages ; seuls les livres (/book/) et l'ASN conservent le parsing log côté serveur
[1.6.5] - 2026-05-15
Modifié
/tendanceset page d'accueil (rubrique "Meilleures audiences") : lecture seule du cache généré par/trending?period=…— plus aucun parsing de logs en dehors du flux RSS- Rubrique renommée "Meilleures audiences · 1 heure" (ex "Tendances · 10 derniers jours")
[1.6.4] - 2026-05-15
Ajouté
src/TrendingParser.php: parseur de logs Apache comptant les visiteurs uniques (IPs distinctes, HTTP 200) par article, avec support multi-préfixes et méthodetopGrouped()(un seul parse pour pages + livres)public/trending.php: flux RSS des 50 articles les plus consultés, paramétrable par période (?period=10m|20m|30m|1h|8h|1d|7d|14d|30d|1y), cache TTL adaptatifpublic/tendances.php: page publique présentant les tendances par période, les flux RSS disponibles et la méthodologie- Route
/tendancesdans.htaccess
Modifié
/admin/stats: utiliseTrendingParser(visiteurs uniques) au lieu d'AccessLogParser(hits bruts) pour les pages et les livres ; label mis à jour- Page d'accueil — rubrique Tendances : source principale désormais les logs Apache sur 1 heure (cache 12 min), fallback sur le score pondéré DB si les logs ne sont pas lisibles
[1.6.3] - 2026-05-15
Ajouté
scripts/server/folio-upgrade.sh: script de déploiement serveur (clone fresh, permissions, composer, migrations SQL,.sessions,safe.directory) appelé parsudodepuis le bouton admin "Mettre à jour"UpdateChecker::getLastUpgradeLog(): affiche le journal de la dernière mise à jour dans l'admin (<details>)
Modifié
run_engine_update: délègue entièrement le déploiement au scriptsudo /usr/local/bin/folio-upgrade.sh— supprime legit pullinline qui ne fonctionnait pas avec les contraintes de permissions rootrun_content_migrationsajouté aux actionsnoindex- Stats admin (
/admin/stats) : cache 60 s dansDATA_PATH/.stats_cache.jsonpour le parsing des logs Apache et le lookup ASN
[1.6.2] - 2026-05-15
Corrigé
oidc/start.php: garde explicite aprèssession_start()— erreur 500 immédiate sisession.save_pathest inaccessible, évite un flux OIDC condamné à l'échec silencieuxoidc/callback.php: même garde de session ;error_logen cas d'échec du contrôle de state pour faciliter le diagnosticconsignes.md: règle ajoutée — pool PHP-FPM avecuser = www-data, pas le compte admin personnel
[1.6.1] - 2026-05-15
Corrigé
login/index.php,login/magic.php,logout.php: ordre de chargement corrigé (config.phpavantbootstrap.php) pour queSESSION_NAMEsoit défini avantsession_start()data/site/retiré du suivi git du moteur (contenu site-spécifique) ;.gitignoremis à jour
[1.6.0] - 2026-05-15
Ajouté
- Admin → Dashboard : bouton unique Mettre à jour (git pull + migrations SQL + migrations contenu) remplace les boutons séparés
- Branche
dev: branche d'intégration permanente pour le développement quotidien
Corrigé
run_engine_update: vérifie que le remote gitorigincorrespond àFOLIO_REPO_URLavant toutgit pull(évite le pull sur le mauvais dépôt)
[1.5.0] - 2026-05-15
Ajouté
- Admin → Site : configuration de l'URL du dépôt Folio et de la branche suivie pour les mises à jour, sans modifier le
.env(folio_repo_url,folio_update_branchdanssite_settings.json) APP_TIMEZONE: fuseau horaire configurable via.env(défautEurope/Paris), appliqué globalement dansbootstrap.php
Corrigé
- Bouton « Vérifier » masqué avec message explicatif si
FOLIO_REPO_URLn'est pas configuré (ni dans.envni dans l'admin) FOLIO_REPO_URLetFOLIO_UPDATE_BRANCHdocumentés dans.env.examplescripts/push.sh: ne pousse plus directement surmain— pousse sur la branche courante pour forcer le passage par une PR
[1.4.0] - 2026-05-15
Ajouté
DATA_PATH: chemin des articles configurable via.env, indépendant du document root — permet de stocker/datahors de l'arborescence web (ex./srv/data/folio)DataGit: auto-commit git sur toutes les écritures articles et livres (création, modification, suppression, métadonnées, tags, fichiers, liens…) saufautosave— no-op silencieux siDATA_PATHn'est pas un dépôt git- Admin — Moteur Folio : affiche la branche suivie pour les mises à jour (
FOLIO_UPDATE_BRANCH, défautmain), la date du dernier contrôle, et un bouton Vérifier pour forcer la vérification sans attendre le TTL du cache (1 h)
Modifié
UpdateChecker: branche cible configurable viaFOLIO_UPDATE_BRANCH(plus demainhardcodé dans l'URL Gitea)
[1.3.0] - 2026-05-15
Ajouté
- Onglet Statistiques dans l'admin : pages les plus visitées, livres consultés, répartition par AS (#64)
AccessLogParser: lecture des logs Apache (plain,.gz,.tar.gz), cache 10 minAsnLookup: résolution ASN via ip-api.com (batch, cache 30 j), détection LAN automatique- Filtrage des AS par groupes configurables (motifs case-insensitive, formulaire admin)
- Pattern de log configurable via l'UI (onglet Recherches) avec support glob
Corrigé
- Permissions rsync :
--chmod=Fug+rw,Fo-wassure la lisibilité groupe sur les fichiers déployés saveSiteSettings()etsaveSmtpSettings(): retournent unboolet affichent une erreur si l'écriture échouescripts/setup.sh: script d'initialisation Folio (composer, répertoires, droits, migrations, groupeadm)
[1.2.2] - 2026-05-14
Corrigé
- URL introuvable : redirige vers la page de recherche (
/search?q=…) au lieu du premier résultat (#61)
[1.2.1] - 2026-05-14
Corrigé
- Cache article invalidé si
index.mdest plus récent quemeta.json(migration de contenu ne se reflétait pas) - Migration 001 :
touch(meta.json)après écriture deindex.mdpour invalider le cache post_view.php: le# TitreMarkdown est retiré du rendu (déjà affiché par le template)- Wizard étape 1 : en-tête affiche « Modifier » sans répéter le titre de l'article
wizard.js: suppression descrollToCursor(calcul erroné sur textarea auto-resize) ; Ctrl+Home / Ctrl+End scrollent correctement viascrollIntoView
[1.2.0] - 2026-05-14
Ajouté
- Wizard multi-étapes pour la création (5 écrans) et l'édition (6 écrans) d'articles (#58)
- Auto-sauvegarde en brouillon (debounce 3 s) avec indicateur visible
- Étape tags : champ plat avec détection automatique depuis le texte (abréviations, CamelCase, noms propres)
- Étape SEO : aperçu moteur de recherche en temps réel
- Étape 6 (édition) : diff ligne à ligne avant confirmation
- Plan Markdown dynamique (TOC) dans la colonne droite de l'éditeur
- Titre extrait du premier
# …du contenu Markdown (plus de champ titre séparé) - Système de migrations de contenu (
scripts/migrate_content.php)- Mode maintenance automatique (
data/.maintenance→ page HTTP 503) - Migration
001: ajout du titre Markdown dans les articles existants - Bouton "Mettre à jour" dans l'administration (sans accès CLI)
- Mode maintenance automatique (
UpdateChecker: détection de mise à jour et migrations en attente- Bandeau d'alerte pour les administrateurs sur toutes les pages
- Dashboard
/admin: version déployée vs version disponible
Modifié
ArticleManager: +6 méthodes pour les brouillons overlaylineDiff: normalisation\r\n→\n, seuil relevé à 2 000 000, fallback ligne par lignepush.sh: génèrepublic/version.txt(numéro de version semver) à chaque release
Corrigé
- Diff étape 6 "violent" (tout supprimé/ajouté) dû aux fins de ligne
\r\ndu navigateur
[1.1.0] - 2026-05-13
Ajouté
- Réactions visiteurs : boutons 👍 / 🔥 / 🤔 sous chaque article, toggle async avec fallback formulaire natif
- Commentaires avec vérification email : code 6 chiffres, honeypot + CSRF, modération dans
/admin - URLs propres :
/edit/<u>,/new,/admin,/categorie/<cat>,/files/<u>/add,/import/<u>, etc. - Moteur de recherche : index trigramme+substring pré-construit, résultats scorés avec mise en évidence
Amélioré
- Cache multi-niveaux : chargement réduit de ~5 s à ~0,4 s sur 1 000+ articles (mémoïsation, cache disque, slug index O(1))
- Upload fichiers : détection et message d'erreur explicite pour les fichiers > limite PHP
Corrigé
- Métadonnées fichiers (
addFileMeta) : guardfile_exists()trop strict supprimé - Sidebar droite article : classe Bootstrap
flex-nowrap-lg→flex-lg-nowrap - Flux RSS : exclusion catégories privées, redirection 301
/rss→/feed
[1.0.0] - 2026-05-09
Ajouté
- Moteur de blog PHP Folio — première release versionnée
- Articles en Markdown avec fichiers attachés, liens externes, images de couverture
- Authentification par lien magique envoyé par email (#29)
- SSO via Keycloak/OIDC avec PKCE
- Rôles, capacités et gestion des utilisateurs
- Catégories avec swatches couleur générées algorithmiquement
- Tags par type avec suggestions
- SEO : canonical,
sitemap.xml,robots.txt, JSON-LD,og:image - Avant-premières (articles futurs visibles aux utilisateurs autorisés)
- Pagination curseur (sans offset SQL)
- Import depuis URL (EXIF, OpenGraph, PDF)
- Historique des révisions avec diff
- Flux RSS (
/feed) paginé avec autodiscovery - Formulaire de contact (CSRF, honeypot, rate-limit)
- Pages légales (LCEN/RGPD), licences, à propos
- Migrations SQL versionnées (
database/migrate.php) - Système de déploiement par rsync