# Changelog Toutes les modifications notables sont documentées ici. Format : [Keep a Changelog](https://keepachangelog.com/fr/1.0.0/) — versionnage [semver](https://semver.org/lang/fr/). --- ## [1.6.14] - 2026-05-15 ### Modifié - Perf : `getAll()` ne charge plus le contenu Markdown — `loadArticle()` reçoit `$withContent = false` dans `loadAll()`, seul `getByUuid()` lit encore `index.md` (#24) - Perf : `search_index.json` enrichi du champ `featured` ; `rebuildSearchIndex()` lit `index.md` directement (indépendant du cache article) - Perf : excerpts dans `post_list`, `author_articles`, `author_profile` proviennent du champ `plain` pré-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 `` et `
` préservés (#15)

### Corrigé
- Suppression du dead code : `AuthService`, `UserRepository` et `Domain\User` — incompatibles avec le système de session actuel, aucune référence active (#19)
- Factorisation des helpers `env()` et `db()` dans `src/helpers.php`, chargé par `config/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` (permissions `2755` → groupe sans écriture) — `removeDir()` échouait sans erreur visible et l'article restait accessible
- `ArticleManager::delete()` retourne maintenant `bool` : 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'erreur
- `removeDir()` : erreurs PHP supprimées silencieusement (`@unlink`, `@rmdir`, `@scandir`) pour éviter les warnings qui cassaient les redirects
- `mkArticleDir()` : nouvelle méthode privée qui crée les répertoires d'articles avec `chmod 0775` explicite (contourne le umask), garantissant que `www-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ètres `sort` et `dir` pré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é CSP `script-src 'self'` (varlog)
- `onclick` / `onchange` inline dans `admin.php` migrés vers `admin.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/stats` section "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 de `topGrouped` pour les pages ; seuls les livres (`/book/`) et l'ASN conservent le parsing log côté serveur

---

## [1.6.5] - 2026-05-15

### Modifié
- `/tendances` et 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éthode `topGrouped()` (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 adaptatif
- `public/tendances.php` : page publique présentant les tendances par période, les flux RSS disponibles et la méthodologie
- Route `/tendances` dans `.htaccess`

### Modifié
- `/admin/stats` : utilise `TrendingParser` (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é par `sudo` depuis le bouton admin "Mettre à jour"
- `UpdateChecker::getLastUpgradeLog()` : affiche le journal de la dernière mise à jour dans l'admin (`
`) ### Modifié - `run_engine_update` : délègue entièrement le déploiement au script `sudo /usr/local/bin/folio-upgrade.sh` — supprime le `git pull` inline qui ne fonctionnait pas avec les contraintes de permissions root - `run_content_migrations` ajouté aux actions `noindex` - Stats admin (`/admin/stats`) : cache 60 s dans `DATA_PATH/.stats_cache.json` pour le parsing des logs Apache et le lookup ASN --- ## [1.6.2] - 2026-05-15 ### Corrigé - `oidc/start.php` : garde explicite après `session_start()` — erreur 500 immédiate si `session.save_path` est inaccessible, évite un flux OIDC condamné à l'échec silencieux - `oidc/callback.php` : même garde de session ; `error_log` en cas d'échec du contrôle de state pour faciliter le diagnostic - `consignes.md` : règle ajoutée — pool PHP-FPM avec `user = 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.php` avant `bootstrap.php`) pour que `SESSION_NAME` soit défini avant `session_start()` - `data/site/` retiré du suivi git du moteur (contenu site-spécifique) ; `.gitignore` mis à 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 git `origin` correspond à `FOLIO_REPO_URL` avant tout `git 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_branch` dans `site_settings.json`) - `APP_TIMEZONE` : fuseau horaire configurable via `.env` (défaut `Europe/Paris`), appliqué globalement dans `bootstrap.php` ### Corrigé - Bouton « Vérifier » masqué avec message explicatif si `FOLIO_REPO_URL` n'est pas configuré (ni dans `.env` ni dans l'admin) - `FOLIO_REPO_URL` et `FOLIO_UPDATE_BRANCH` documentés dans `.env.example` - `scripts/push.sh` : ne pousse plus directement sur `main` — 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 `/data` hors 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…) sauf `autosave` — no-op silencieux si `DATA_PATH` n'est pas un dépôt git - **Admin — Moteur Folio** : affiche la branche suivie pour les mises à jour (`FOLIO_UPDATE_BRANCH`, défaut `main`), 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 via `FOLIO_UPDATE_BRANCH` (plus de `main` hardcodé 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 min - `AsnLookup` : 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-w` assure la lisibilité groupe sur les fichiers déployés - `saveSiteSettings()` et `saveSmtpSettings()` : retournent un `bool` et affichent une erreur si l'écriture échoue - `scripts/setup.sh` : script d'initialisation Folio (composer, répertoires, droits, migrations, groupe `adm`) --- ## [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.md` est plus récent que `meta.json` (migration de contenu ne se reflétait pas) - Migration 001 : `touch(meta.json)` après écriture de `index.md` pour invalider le cache - `post_view.php` : le `# Titre` Markdown 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 de `scrollToCursor` (calcul erroné sur textarea auto-resize) ; Ctrl+Home / Ctrl+End scrollent correctement via `scrollIntoView` --- ## [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) - `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 overlay - `lineDiff` : normalisation `\r\n` → `\n`, seuil relevé à 2 000 000, fallback ligne par ligne - `push.sh` : génère `public/version.txt` (numéro de version semver) à chaque release ### Corrigé - Diff étape 6 "violent" (tout supprimé/ajouté) dû aux fins de ligne `\r\n` du 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/`, `/new`, `/admin`, `/categorie/`, `/files//add`, `/import/`, 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`) : guard `file_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