# Audit de l'architecture `ancien-code/`

Audit de compréhension uniquement — aucune modification de code n'a été effectuée. Objectif : préparer une future migration vers une architecture Laravel multi-tenant.

---

## 1. Le template maître

Il existe en réalité **deux générations** de template client, coexistant dans `ancien-code/`. La confusion vient du fait que tous les dossiers clients ont la même arborescence de surface (`index.php`, pages métier, `inc/header.php`, `inc/footer.php`, `inc/open-graph.php`, `inc/testimonials.php`, `inc/citation.php`, `inc/cta-button.php`), mais le contenu de ces fichiers diffère radicalement selon la génération.

### Génération 1 — contenu codé en dur
Exemples : `aix-en-provence/avocat/caroline-poplawec`, `sandbox/slibour`, et les 7 autres clients réels listés en section 4. Chaque `inc/*.php` est réécrit à la main par client : nom, téléphone, e-mail, avis clients, JSON-LD… tout est en valeurs littérales. Il n'y a **aucun chargement de JSON au rendu**. Le JSON-LD `schema.org` (un seul bloc `LocalBusiness`/`MedicalClinic`) est présent mais lui aussi en dur, avec parfois des artefacts non résolus (ex. `"name": "{catalog_name}"` retrouvé tel quel dans un fichier).

### Génération 2 — le vrai « template maître » piloté par JSON
Exemples : `carcassonne/ferronnier-metallier/jerome-loze`, `france/preparateur-cavaliers/yannick-hebras`. C'est la structure à retenir comme référence pour la migration.

**Chargement des données** — `_data/_config.php`, identique mot pour mot chez les deux clients :
```php
$d = json_decode(file_get_contents(__DIR__ . '/data-client.json'), true) ?? [];
```
Chemin résolu via `__DIR__` (toujours relatif au fichier), pas de cache, gestion d'erreur minimale et silencieuse (`?? []` : si le fichier est absent ou invalide, la page se rend avec des champs vides, sans log ni exception).

**Injection dans le HTML** — trois helpers globaux, aucun `extract()` :
- `t($k)` → `htmlspecialchars($d[$k] ?? '', ...)` (texte échappé)
- `raw($k)` → `$d[$k] ?? ''` (HTML brut autorisé, ex. `<b>`, `<br>`)
- `on($k)` → booléen pour affichage conditionnel

Exemples concrets :
```php
<h2><?= t('met_desc_titre') ?></h2>                          // texte
<img src="assets/img/<?= t('slug_image') ?>-1.webp">          // image
<meta name="description" content="<?= t('index_seo_metadesc') ?>">
```
Les listes (spécialités, prestations, avis, FAQ) n'utilisent pas de tableau JSON imbriqué mais une **convention de clés numérotées** (`ent_specialite_1`, `prest_01_nom`, `faq_01_quest`…) parcourue par une boucle `for` jusqu'à ce que `on()` retourne faux :
```php
<?php for ($n = 1; ; $n++): if (!on("ent_specialite_{$n}")) break; ?>
<li><?= t("ent_specialite_{$n}") ?></li>
<?php endfor; ?>
```

**Schema.org** — un tableau de 3 entités JSON-LD (`LocalBusiness`, `Person`, `FAQPage`) généré directement dans `index.php` à partir du JSON (pas de fichier `inc` dédié), avec `json_encode()` pour les sous-listes générées par boucle (`hasOfferCatalog.itemListElement` depuis `prest_NN_*`, `FAQPage.mainEntity` depuis `faq_NN_*`).

**Statique vs dynamique** — Statique dans les deux générations : structure Bootstrap, classes CSS, accordéons, icônes `bi-*`, scripts vendor. Dynamique (génération 2 uniquement) : title/meta/canonical, identité, spécialités, biographie, approche, prestations, témoignages, FAQ, coordonnées, cartes Google Maps, JSON-LD entier.

**Réutilisation réelle du template** — Entre `jerome-loze` et `yannick-hebras` (génération 2), les `inc/*.php` sont quasi identiques mot pour mot (seule différence relevée : un commentaire HTML en plus chez l'un). C'est un vrai template partagé. Entre les clients de génération 1, les `inc/*.php` sont du copier-coller adapté à la main, sans aucune factorisation.

---

## 2. Analyse des JSON clients

Deux familles de fichiers JSON totalement distinctes cohabitent, à ne pas confondre :
- **Fichiers de contenu** (`data-client*.json` dans des dossiers `_data/`) : alimentent le rendu du site (génération 2 uniquement).
- **Fichiers de suivi de prestation** (`data.json` dans des dossiers à nom aléatoire type `K7mP2x`, `HhfYihN`, `dNNmh8`, `HsiR6F`, `Jjf7JK`) : suivent l'avancement de la production (étapes, options souscrites), sans aucun rapport avec le contenu du site. Voir section 3.

### 2.a. Fichiers de contenu (`data-client*.json`)

Schéma plat (objet à un seul niveau), quasi exclusivement des `string` (souvent avec HTML inline `<b>`, `<br>`). Une seule exception de type : `_cms/v1/data-client.json` utilise des `boolean` (`prest_01_active`…`prest_06_active`) — **seul fichier du corpus à utiliser ce type**, abandonné dans toutes les versions suivantes.

**Clés communes à tous les fichiers de contenu** : `prenom`, `nom`, `ent_specialite_1..N`, `ent_adr`, `ent_cp`, `ent_ville`, `ent_tel`, `ent_mail`, `met_desc_titre/texte`, `biographie_intro`, `biographie_item_01..NN`, `approche_intro`/`vision_*`/`mission_*`/`valeurs_*`, `prest_01_*` (nom, url, tarif, seo_*…), `avis_01..N`, `faq_01..N`, `index_seo_title/metadesc/keywords/canonical`, `ent_adeli`, `ent_rpps`.

**Clés présentes chez certains seulement** :
| Clé | Présente dans | Absente de |
|---|---|---|
| `prest_01_active` (booléen) | v1 seulement | v2, v3, tous les clients finaux (slot vide = inactif) |
| `cta_lien` / `cta_label` | v3, jerome-loze, yannick-hebras | v1, v2 |
| `lien_partenaire_01_texte/_url` | v3, yannick-hebras | v1, v2, jerome-loze |
| `prest_XX_citation` / `_citation_auteur` | v3, jerome-loze, yannick-hebras | v1, v2 |
| `prest_01_portfolio_*`, `prest_01_folio_01..11` | `sandbox/cms_portfolio`, `sandbox/jloze` | tous les autres |
| `slug_image` | v2 et suivants | v1 |

**Incohérences de nommage / structure repérées** :
- **Téléphone** : `ent_tel` en format humain (`"07 82 18 14 78"`) coexiste avec `cta_lien`/`ent_lien_rdv` au format URI (`"tel:0608786184"`) — deux représentations différentes pour la même donnée.
- **Nom du client** : `prenom` + `nom` séparés côté contenu, mais `client` en une seule chaîne (`"Jérôme LOZE"`) côté suivi — deux modèles pour la même information selon la famille de fichier.
- **Adresse** : `ent_cp_ville` (ex. `"11220 Taurize"`) fait doublon avec `ent_cp` + `ent_ville` séparés ; les champs `ent_adr_2/ent_cp_2/ent_ville_2…` (adresse secondaire) sont présents et vides presque partout, incomplets dans v1, et carrément **dupliqués avec la valeur de l'adresse principale** dans `data-clientJOZE.json`.
- **Auteur de citation vide** : trois conventions différentes coexistent pour « pas d'auteur » : `""`, `"Auteur anonyme"`, ou un vrai nom.
- **Paire lien/libellé** : `cta_lien`/`cta_label` (suffixes `_lien`/`_label`) vs `lien_partenaire_01_texte`/`_url` (suffixes `_texte`/`_url`) — deux conventions pour le même concept, dans les mêmes fichiers.
- **Granularité incohérente** : `ent_specialite_1` contient soit une phrase complète unique (`data-clientYHEBRAS.json`), soit un fragment découpé sur `ent_specialite_1/2/3` (v3 et fichier final Hebras) — même clé, granularité différente selon le fichier.
- **Champs médicaux hors-sujet** : `ent_adeli` et `ent_rpps` (identifiants réglementés pour professions de santé) sont présents et vides dans **tous** les fichiers, y compris pour un ferronnier ou un avocat — résidu du template générique jamais adapté au métier.
- **Format de valeur incohérent pour une même clé** : `prest_XX_public_conc`/`_lieu_interv` en phrase libre dans v1, mais en liste HTML à puces (`"• X<br>• Y"`) dans les versions suivantes.
- **Dérive sémantique d'une clé stable** : `prest_01_ind_titre` vaut `"Pour qui ?"` dans un fichier et `"Quand me contacter ?"` dans un autre fichier du même client — la clé reste identique mais son usage change.
- **Nommage de fichier non descriptif** : `data-client.json` est réutilisé identiquement dans 6+ dossiers différents ; toute l'information sur le client réside dans le chemin du dossier, jamais dans le nom du fichier.

### 2.b. Fichiers de suivi de prestation (`<slug>/data.json`)

Schéma uniforme sur les 6 instances trouvées (`K7mP2x`, `HhfYihN` ×3, `dNNmh8`, `HsiR6F`, `Jjf7JK`) : objet `{ client: string, date: string, steps: {...}, opts: {...} }`.
- `steps` : clés hétérogènes et non séquentielles — mélange de numériques (`"2"`, `"4"`…`"10"`) et alphanumériques (`"1a"`, `"1b"`, `"3a"`, `"3b"`), stockées dans un ordre non chronologique. Valeurs : `"none" | "active" | "done"`.
- `opts` : `idv, photo, report, mail, gads, cv, fly, pack, seo, rs, rsplus` → mêmes valeurs `"none"/"active"/"done"`.

Aucune incohérence de schéma entre ces 6 fichiers — c'est le sous-système le plus homogène du corpus. Le seul point faible : le nom du dossier parent (`K7mP2x`…) est un identifiant aléatoire sans rapport avec le client, purement opaque.

---

## 3. Le « petit CMS »

Il existe en réalité **deux outils distincts** sous ce nom, à ne pas confondre :

### 3.a. CMS d'édition de contenu (`_data/admin.php` + `_data/save.php`)
Édite directement le `data-client.json` de génération 2 décrit en section 2.a. Présent dans `_cms/v1/`, `_cms/v2/_data/`, `_cms/v3/_data/`, et déployé sur `france/preparateur-cavaliers/yannick-hebras/_data/` (client réel !), ainsi que dans les sandbox `jloze` et `cms_portfolio`.

Interface : formulaire HTML classique organisé en sections de navigation (`identite`, `coordonnees`, `metier`, `biographie`, `approche`, `prest_01`…`06`, `avis`, `faq`, `seo`, `credits`), avec échappement à l'affichage via un helper `val()` :
```php
function val($data, $key) {
    return htmlspecialchars($data[$key] ?? '', ENT_QUOTES, 'UTF-8');
}
```

**Sécurité — aucune authentification.** Recherche de `password|auth|login|session_start|$_SESSION` sans aucun résultat dans `admin.php` ni `save.php`. Le chemin est **prévisible** (`/_data/admin.php`, identique sur tous les clients qui l'ont), donc devinable — contrairement aux dossiers-slugs aléatoires. C'est le point de sécurité le plus sérieux relevé dans cet audit : n'importe qui connaissant ou devinant l'URL peut lire et réécrire tout le contenu du site de `yannick-hebras` en production.

Bonus inquiétant dans `_cms/v1/save.php` : un bloc de diagnostic (`$diag`, version PHP, droits d'écriture, `open_basedir`) est renvoyé dans la réponse JSON et dans un en-tête `X-Diag`, sans authentification — fuite d'informations serveur.

### 3.b. Tableau de suivi de prestation (dossier-slug : `admin.html` + `data.json` + `save.php` + `suivi-prestations.html`)
Sans rapport avec le contenu du site : c'est un tableau de bord interne de suivi de production (10 étapes, de « Interview » à « Mise en ligne », + options souscriptibles comme identité visuelle, photos, SEO, réseaux sociaux). Deux instances vérifiées (`K7mP2x`, `Jjf7JK`) sont strictement identiques au fichier près (seul `data.json` diffère).

Stockage : `file_put_contents` + `json_encode(..., JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE)`, pas de base de données.

`save.php` — lit le corps brut en JSON, seule « validation » est la validité syntaxique JSON :
```php
$body = file_get_contents('php://input');
$data = json_decode($body, true);
if ($data !== null) {
    file_put_contents($file, json_encode($data, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE));
```
Aucun contrôle de structure/type des clés. `GET` renvoie tout le fichier sans restriction, et `Access-Control-Allow-Origin: *` autorise n'importe quel site tiers à appeler l'endpoint.

**Sécurité — aucune authentification non plus.** Seule protection : l'obscurité du nom de dossier (`K7mP2x`, `Jjf7JK`…). `admin.html` est la version éditable (clic sur des « cartes » d'étapes, `fetch()` en JS) ; `suivi-prestations.html` est la version lecture seule destinée à être montrée au client.

**Évolution du gabarit** : livré en v2 sous le nom explicite `dossier_suivi_prestations_a_renommer` (à copier-renommer manuellement par client), rebaptisé `_suivi` en v3 (nom générique fixe).

### Différence avec le système JSON statique
Les clients de génération 1 (section 1) n'ont ni CMS de contenu ni fichier de contenu JSON : toute modification nécessite d'éditer le PHP en dur. Les clients de génération 2 disposent d'un vrai découplage contenu/présentation via JSON, mais l'outil d'édition de ce JSON (le CMS 3.a) n'est protégé par aucune authentification là où il existe.

---

## 4. Quel dossier client utilise quel système

| Dossier | Contenu du site | Édition du contenu | Suivi de prestation |
|---|---|---|---|
| `aix-en-provence/avocat/caroline-poplawec` | Codé en dur (gén. 1) | — (aucun) | `K7mP2x/` (obscurci) |
| `carcassonne/ferronnier-metallier/jerome-loze` | JSON dynamique (gén. 2) | — (aucun `_data/admin.php`) | `HhfYihN/` (obscurci) |
| `france/preparateur-cavaliers/yannick-hebras` | JSON dynamique (gén. 2) | `_data/admin.php` **exposé, sans auth** | `dNNmh8/` (obscurci) |
| `narbonne/osteopathe/david-zerbib` | Codé en dur (gén. 1) | — (aucun) | `HsiR6F/` (obscurci) |
| `narbonne/dieteticienne/jessica-poux` | Codé en dur (gén. 1) | — (aucun) | — (aucun) |
| `narbonne/medium-magnetiseur/virginie-rouzeau` | Codé en dur (gén. 1) | — (aucun) | — (aucun) |
| `narbonne/osteopathe/edouard-le-gourieres` | Codé en dur (gén. 1) | — (aucun) | — (aucun) |
| `narbonne/osteopathe/paul-prevot` | Codé en dur (gén. 1) | — (aucun) | — (aucun) |
| `narbonne/psychologue/luella-barlow` | Codé en dur (gén. 1) | — (aucun) | — (aucun) |
| `narbonne/psychopraticien/marie-eve-millet` | Codé en dur (gén. 1) | — (aucun) | — (aucun) |
| `perpignan/psychopraticien/marjorie-marti` | Codé en dur (gén. 1) | — (aucun) | — (aucun) |
| `sandbox/breix` | Codé en dur, sans même `inc/` | — (aucun) | — (aucun) |
| `sandbox/jgrall` | Codé en dur, sans même `inc/` | — (aucun) | — (aucun) |
| `sandbox/lbarlow` | Codé en dur, sans même `inc/` | — (aucun) | — (aucun) |
| `sandbox/slibour` | Codé en dur (gén. 1) | — (aucun) | `Jjf7JK/` (obscurci) |
| `sandbox/jloze` | JSON dynamique (gén. 2 + portfolio/Isotope) | `_data/admin.php` **exposé, sans auth** | `HhfYihN/` (obscurci) |
| `sandbox/cms_portfolio` | JSON dynamique (gén. 2 + portfolio/Isotope) | `_data/admin.php` **exposé, sans auth** | `HhfYihN/` (obscurci) |

`_cms/v1/`, `_cms/v2/`, `_cms/v3/` ne sont pas des sites clients mais les gabarits/itérations successives de l'outillage lui-même (v1 : CMS à la racine, pas de vrai site ; v2/v3 : site complet + CMS déplacé dans `_data/`, suivi renommé `dossier_suivi_prestations_a_renommer` puis `_suivi`).

---

## 5. Dépendances front-end par client

| Dossier client | Bootstrap | AOS | GLightbox | Swiper | Isotope |
|---|---|---|---|---|---|
| aix-en-provence/avocat/caroline-poplawec | ✅ | ✅ (~89 occ.) | ✅ (~14) | ✅ (~26) | ⚠️ non utilisé |
| carcassonne/ferronnier-metallier/jerome-loze | ✅ | ✅ (~37) | ✅ (~8) | ✅ (~14) | ⚠️ non utilisé |
| france/preparateur-cavaliers/yannick-hebras | ✅ | ✅ (~37) | ✅ (~8) | ✅ (~14) | ⚠️ non utilisé |
| narbonne/dieteticienne/jessica-poux | ✅ | ✅ (~63) | ✅ (~12) | ✅ (~22) | ⚠️ non utilisé |
| narbonne/medium-magnetiseur/virginie-rouzeau | ✅ | ✅ (~77) | ✅ (~12) | ✅ (~20) | ⚠️ non utilisé |
| narbonne/osteopathe/david-zerbib | ✅ | ✅ (~51) | ✅ (~10) | ✅ (~20) | ⚠️ non utilisé |
| narbonne/osteopathe/edouard-le-gourieres | ✅ | ✅ (~85) | ✅ (~14) | ✅ (~24) | ⚠️ non utilisé |
| narbonne/osteopathe/paul-prevot | ✅ | ✅ (~97) | ✅ (~16) | ✅ (~45) | ⚠️ non utilisé |
| narbonne/psychologue/luella-barlow | ✅ | ✅ (~71) | ✅ (~16) | ✅ (~25) | ⚠️ non utilisé |
| narbonne/psychopraticien/marie-eve-millet | ✅ | ✅ (~50) | ✅ (~10) | ✅ (~19) | ⚠️ non utilisé |
| perpignan/psychopraticien/marjorie-marti | ✅ | ✅ (~63) | ✅ (~12) | ✅ (~28) | ⚠️ non utilisé |
| sandbox/breix | ✅ | ✅ (~67) | ✅ (~16) | ✅ (~25) | ⚠️ non utilisé |
| sandbox/cms_portfolio | ✅ | ✅ (~41) | ✅ (~12) | ✅ (~14) | ✅ (grille filtrable) |
| sandbox/jgrall | ✅ | ✅ (~86) | ✅ (~18) | ✅ (~27) | ⚠️ non utilisé |
| sandbox/jloze | ✅ | ✅ (~41) | ✅ (~12) | ✅ (~14) | ✅ (grille filtrable) |
| sandbox/lbarlow | ✅ | ✅ (~71) | ✅ (~16) | ✅ (~25) | ⚠️ non utilisé |
| sandbox/slibour | ✅ | ✅ (~81) | ✅ (~14) | ✅ (~22) | ⚠️ non utilisé |

Les 17 dossiers partagent exactement le même socle `assets/vendor/` (bootstrap, bootstrap-icons, aos, glightbox, swiper, isotope-layout, imagesloaded, php-email-form) : c'est un template unique dupliqué à chaque client. Bootstrap, AOS, GLightbox et Swiper sont systématiquement présents **et** réellement utilisés partout. Isotope est chargé sur les 17 sites mais n'est réellement exploité (markup filtrable) que dans `sandbox/cms_portfolio` et `sandbox/jloze` — sur les 15 autres, c'est un script mort chargé pour rien.

---

## 6. Points d'attention pour une future normalisation

1. **Deux modèles de contenu incompatibles à fusionner** : 9 clients réels + 3 sandbox en contenu 100% codé en dur (aucune donnée structurée à migrer automatiquement, il faudra extraire le contenu du HTML à la main ou par script ad hoc) vs 2 clients réels + 2 sandbox en JSON structuré (migration directe possible vers un schéma unique). Aucun script de conversion gén. 1 → gén. 2 n'existe.
2. **Faille de sécurité concrète et active** : `france/preparateur-cavaliers/yannick-hebras/_data/admin.php` (site client réel en production) permet de lire/écrire tout le contenu du site sans authentification, à une URL prévisible. Idem pour tous les dossiers-slugs obscurcis (`K7mP2x`, `HhfYihN`, `dNNmh8`, `HsiR6F`, `Jjf7JK`) : sécurité par obscurité uniquement, `Access-Control-Allow-Origin: *` en plus. Le futur back-office multi-tenant doit imposer une authentification réelle dès le départ — ce n'est pas négociable pour la migration.
3. **Fuite d'information serveur** : `_cms/v1/save.php` renvoie un diagnostic serveur (version PHP, droits fichiers, `open_basedir`) sans authentification.
4. **Absence de convention stable pour les listes** : les prestations/avis/FAQ/spécialités sont représentées par des clés numérotées à plat (`prest_01_nom`, `faq_02_quest`) plutôt que par de vrais tableaux JSON. Toute normalisation vers un schéma unique devra décider d'un vrai type tableau (`prestations: [...]`) et écrire un script de migration pour chaque génération/version (v1, v2, v3, gén. 1 codée en dur, jerome-loze, yannick-hebras, jloze/cms_portfolio avec portfolio).
5. **Champs redondants ou à consolider** : téléphone (`ent_tel` humain vs `tel:` URI dans `cta_lien`/`ent_lien_rdv`), adresse (`ent_cp_ville` combiné vs `ent_cp`+`ent_ville` séparés, `_2` secondaire quasi jamais utilisé correctement), paires lien/libellé à deux conventions (`_lien/_label` vs `_texte/_url`), `ent_nom_commercial` utilisé de façon incohérente.
6. **Champs hors-sujet non nettoyés** : `ent_adeli`/`ent_rpps` (réservés aux professions de santé réglementées) présents vides dans tous les fichiers y compris pour un avocat ou un ferronnier — signe que le template n'a jamais été spécialisé par métier ; à traiter comme des champs optionnels par verticale métier dans le futur schéma, pas comme des champs globaux.
7. **Deux notions de « nom du client » à unifier** : `prenom`+`nom` séparés (contenu) vs `client` en chaîne unique (suivi) — le futur modèle Laravel (probablement une table `clients`/`tenants`) devra choisir une seule représentation et migrer les deux familles vers elle.
8. **Convention de valeur vide non uniforme** : `""`, texte littéral (`"Auteur anonyme"`), ou booléen dédié (`prest_01_active`, abandonné après v1) pour signaler « pas de valeur »/« section inactive » — à remplacer par un `null` ou une absence de clé cohérente dans le futur schéma.
9. **Nommage de fichiers et dossiers non auto-descriptif** : `data-client.json` et `data.json` sont réutilisés à l'identique dans des dizaines de dossiers ; les dossiers-slugs de suivi (`K7mP2x`…) n'ont aucun rapport avec le client. Toute la traçabilité repose sur le chemin du dossier — fragile en cas de réorganisation, et incompatible avec un stockage centralisé (base de données) où ces fichiers physiques disparaîtront.
10. **Isotope comme dette technique** : chargé partout mais utilisé nulle part sauf 2 sandbox — à ne pas reconduire dans le nouveau template de base ; seul le futur template « portfolio » (si cette fonctionnalité est conservée) en aura besoin.
11. **Fichiers de sauvegarde manuels non structurés** : plusieurs `_back*.json`/`_back_YYYYMMDD*.php` traînent dans les dossiers `_data/` (ex. `sandbox/jloze`, `sandbox/cms_portfolio`) — ce sont des sauvegardes ad hoc sans rotation ni horodatage cohérent, à ne pas reproduire (le futur système aura besoin d'un vrai historique/versioning en base plutôt que des copies de fichiers).

---

## 7. template_2.0 — analyse et comparaison avec `_cms/v3`

`ancien-code/template_2.0_(cms + suivi prestations)/` est un site complet basé sur la même arborescence que `_cms/v3` (`index.php`, `prestation.php`, `mentions-legales.php`, `rdv-valide.php`, `inc/`, `_data/`, `assets/`, `forms/`) plus un dossier de suivi `dossier_suivi_prestations_a_renommer/` — le même nom de gabarit générique que celui utilisé dans `_cms/v2`. Nouveauté de structure : deux paires de fichiers `inc/header.php`/`inc/header_old.php` et `inc/footer.php`/`inc/footer_old.php` cohabitent (les anciennes versions ont été conservées à côté des nouvelles au lieu d'être supprimées), ainsi que deux fichiers de données de référence non utilisés par le site lui-même : `_data/data-clientJOZE.json` et `_data/data-clientYHEBRAS.json` (copies d'exemples client, gardées comme modèles).

### 7.1. Chargement JSON (`_config.php`) — inchangé

`_data/_config.php` est **strictement identique, octet pour octet**, à celui de `_cms/v3` : même chargement (`json_decode(file_get_contents(__DIR__ . '/data-client.json'), true) ?? []`), mêmes trois helpers `t()`/`raw()`/`on()`. Aucune évolution du mécanisme de chargement.

En revanche, `header.php`/`footer.php` ont été corrigés pour éviter une collision de variables PHP entre includes : les anciennes versions (`header_old.php`, `footer_old.php`) utilisaient toutes deux une boucle `for ($n = 1; ...)` avec une variable `$p` partagée — comme `_config.php` déclare ses variables en global et que plusieurs `inc/*.php` sont inclus sur la même page, ces noms génériques pouvaient se télescoper. Les nouvelles versions préfixent chaque variable de boucle par le fichier (`$nav_n`/`$nav_p` dans `header.php`, `$ftr_n`/`$ftr_p` dans `footer.php`) :
```php
// header_old.php / footer_old.php (v3)
for ($n = 1; ; $n++): $p = sprintf('prest_%02d', $n); ...

// header.php (template_2.0)
for ($nav_n = 1; ; $nav_n++): $nav_p = sprintf('prest_%02d', $nav_n); ...
```
C'est une vraie correction technique, pas juste un renommage cosmétique.

### 7.2. Schéma JSON — clés disparues, aucune clé nouvellement ajoutée

Comparaison de `_data/data-client.json` (contenu réel : Jérôme Loze) avec le schéma documenté en section 2.a pour `_cms/v3` :

| Constat | Détail |
|---|---|
| **Clés disparues** | `cta_lien`, `cta_label` (remplacées par la réutilisation de `ent_lien_rdv`, déjà présent depuis v1/v2) ; `lien_partenaire_01..03_texte`/`_url` (section « Partenaires » retirée du CMS et du schéma) ; `prest_XX_citation`, `prest_XX_citation_auteur` (citation par prestation retirée) |
| **Clés nouvelles** | Aucune. `template_2.0` est un sous-ensemble strict du schéma v3, pas une extension. |
| **`ent_specialite_N`** | Toujours présent, mais figé à exactement 5 slots (`ent_specialite_1` à `_5`, y compris vides) au lieu de la boucle `for` à nombre variable de v3 — cohérent avec le formulaire admin, qui n'affiche plus que 5 champs fixes (voir 7.3) |

Conséquence directe : **`inc/cta-button.php` a changé de logique**, pas seulement de données. En v3, le bouton CTA était entièrement piloté par le JSON (lien + libellé + icône déduite automatiquement du protocole du lien) :
```php
// v3
$cta_lien  = $d['cta_lien']  ?? '';
$cta_label = $d['cta_label'] ?? '';
if (strpos($cta_lien, 'tel:') === 0) { $cta_icone = 'bi-telephone'; }
```
En template_2.0, le bouton est redevenu partiellement statique : libellé « Prendre RDV » et icône `bi-calendar-date` codés en dur, seul le lien reste dynamique — et il réutilise `ent_lien_rdv` :
```php
// template_2.0
href="<?= t('ent_lien_rdv') ?>"
<span><i class="bi bi-calendar-date"></i>&nbsp Prendre RDV</span>
```
C'est une régression fonctionnelle (le libellé/l'icône ne sont plus personnalisables par client) mais un gain de cohérence de schéma : cela supprime la double convention `_lien/_label` vs `_texte/_url` relevée comme incohérence en section 6, point 5 de cet audit, en revenant à la clé `ent_lien_rdv` déjà existante dans tout le corpus.

Point de vigilance : `_data/data-clientYHEBRAS.json`, laissé dans le dossier comme fichier de référence, est une copie **inchangée** de la version `_cms/v2` — il contient donc encore `cta_lien`, `lien_partenaire_*` et les citations par prestation, des clés que le CMS et les templates de template_2.0 ne savent plus éditer ni afficher correctement. Si ce fichier était utilisé comme point de départ pour un nouveau client, il faudrait le nettoyer à la main.

### 7.3. CMS d'édition (`_data/admin.php` + `save.php`) — formulaire réduit, sécurité inchangée

`save.php` est **strictement identique** à celui de v3 (`diff` ne rapporte aucune différence) : même lecture du corps JSON brut, même écriture par `file_put_contents`, **toujours aucune authentification**. Recherche de `password|session_start|$_SESSION|login` dans `_data/` : aucun résultat, comme dans toutes les versions précédentes. Le chemin reste `/_data/admin.php`, toujours prévisible et non protégé.

`admin.php` en revanche a été simplifié (735 lignes en v3 → 671 lignes en template_2.0) :
- La section « Bouton CTA » (configuration `cta_lien`/`cta_label`) a été **retirée du formulaire**.
- La section « Partenaires » (`lien_partenaire_01..03`) a été **retirée du formulaire**.
- Le bloc « Citation (optionnelle) » par prestation a été **retiré**.
- La boucle dynamique sur les spécialités (`for ($n = 1; ; $n++) ... if ($n > 1 && vide) break;`) a été remplacée par **5 champs fixes** (`Spécialité 1` à `5`) :
```php
// v3 : nombre variable de champs
<?php for ($n = 1; ; $n++): $sk = "ent_specialite_{$n}"; if ($n > 1 && ($data[$sk] ?? '') === '') break; ?>

// template_2.0 : 5 champs fixes
<?php field('ent_specialite_1', 'Spécialité 1', $data, 'input') ?>
<?php field('ent_specialite_2', 'Spécialité 2', $data, 'input') ?>
```
- Petite amélioration UX sur le libellé des slots de prestation vides : au lieu d'un champ vide muet, template_2.0 affiche désormais `Prestation {n} (nouvelle)` tant qu'aucun nom n'est saisi.

Le formulaire est donc plus simple et plus prévisible (moins de boucles dynamiques fragiles), au prix d'une perte de flexibilité (nombre de spécialités et champs CTA/partenaires figés). **Aucune amélioration de sécurité.**

### 7.4. Suivi de prestation — évolution réelle du modèle `steps`

`save.php` du suivi est identique à celui de v3 (toujours sans authentification, toujours `Access-Control-Allow-Origin: *` implicite via l'absence de restriction). En revanche `admin.html`/`suivi-prestations.html` ont évolué :

- **Renommage orienté client** : le titre passe de `Admin · lesiteduliberal.fr` à `Mon espace · lesiteduliberal.fr` — le panneau est désormais présenté comme un espace de suivi pour le client, pas un back-office technique.
- **Étapes scindées en sous-parties indépendantes** : v3 modélisait 10 étapes séquentielles simples (`STEP_KEYS` implicite `1..10`, toutes à l'état `"none"` dans le gabarit vierge). template_2.0 introduit un vrai découpage en paires liées :
```js
const STEP_KEYS = ['1a','1b','2','3a','3b','4','5','6','7','8','9','10'];
const PAIRS = { '1': ['1a','1b'], '3': ['3a','3b'] };
```
  avec un état indépendant pour chaque sous-étape (ex. « Interview — partie 1 » / « Interview — partie 2 », « Revue des contenus — partie 1 » / « partie 2 »). C'est une évolution réelle du modèle de données de suivi, pas juste un habillage visuel.
- Cette convention `1a/1b/3a/3b` était déjà observée dans des fichiers de suivi **déployés en production** (`K7mP2x`, `HhfYihN`, `dNNmh8`…, cf. section 2.b de cet audit), alors qu'elle est absente du gabarit vierge de `_cms/v3/_suivi/data.json` (qui n'a que des clés `"1".."10"`). Autrement dit : **le gabarit `_suivi` de v3 était déjà obsolète par rapport à ce qui tournait réellement en production**, et template_2.0 met le gabarit à jour pour refléter l'évolution déjà en place chez les clients.
- **Point de vigilance (hygiène, pas sécurité)** : `dossier_suivi_prestations_a_renommer/data.json` dans template_2.0 ne contient pas un gabarit vierge mais les **données réelles et complétées de Caroline Poplawec** (`"client": "Caroline POPLAWEC"`, la plupart des étapes à `"done"`). Un développeur dupliquant ce dossier pour démarrer le suivi d'un nouveau client livrerait par erreur les données d'un autre client s'il oublie de réinitialiser `data.json`.

### 7.5. Itération ou refonte ?

**`template_2.0` est une itération de plus dans la lignée v1 → v2 → v3**, pas une refonte d'architecture. Le mécanisme de fond (JSON plat + `_config.php` + `t()/raw()/on()` + clés numérotées pour les listes) est rigoureusement inchangé, et le schéma de contenu est un **sous-ensemble** de celui de v3 (aucune clé nouvelle, plusieurs retirées). Le fichier `arborescence_site.txt` retrouvé dans ce dossier fait d'ailleurs explicitement référence à `jerome-loze` — ce template est en réalité le template déjà déployé pour le client réel `carcassonne/ferronnier-metallier/jerome-loze`, repackagé comme point de départ générique pour les prochains sites (même routing `prestation.php?slug=`, mêmes fichiers `inc/*`).

Pour la migration Laravel à venir, il convient donc de le traiter comme **la version la plus aboutie de la génération 2** (à privilégier comme référence de départ plutôt que v1/v2/v3), mais **sans repartir de son schéma JSON as-is** : il faudra plutôt combiner le schéma le plus complet déjà documenté (celui de `yannick-hebras`/v3, avant les retraits de template_2.0) avec les corrections de cohérence apportées ici (`ent_lien_rdv` unifié, disparition de la double convention CTA) et avec l'évolution du suivi de prestation (`1a/1b/3a/3b`) qui, elle, doit être conservée.

### 7.6. Synthèse des améliorations et problèmes

**Améliorations notables :**
- Correction d'une collision potentielle de variables PHP entre includes (`$n`/`$p` → préfixes par fichier).
- Suppression de la double convention de nommage lien/libellé (`cta_lien/cta_label` vs `ent_lien_rdv`), au profit d'une clé déjà existante — réduit une incohérence déjà relevée en section 6.
- Mise à jour du gabarit de suivi de prestation pour refléter le modèle `1a/1b/3a/3b` réellement utilisé en production, que l'ancien gabarit v3 ne reflétait plus.
- Formulaire CMS simplifié (moins de boucles dynamiques fragiles, meilleur libellé des slots vides).

**Problèmes persistants (non corrigés) :**
- **Toujours aucune authentification** sur `_data/admin.php`/`save.php` ni sur le suivi de prestation — le problème de sécurité le plus important de tout l'audit (section 6, point 2) n'a pas été traité dans cette itération.
- Fichiers obsolètes conservés à côté des nouveaux (`header_old.php`, `footer_old.php`) au lieu d'être supprimés ou archivés proprement.
- Fichiers de référence (`data-clientYHEBRAS.json`) non synchronisés avec le schéma réduit du CMS actuel.

**Problème nouveau introduit :**
- Le gabarit de suivi de prestation à dupliquer par client (`dossier_suivi_prestations_a_renommer/data.json`) contient les données réelles d'un client existant au lieu d'un gabarit vierge — risque de fuite de données d'un client vers un autre par copier-coller inattentif.

---

## 8. Identités clients confirmées, comparaison jloze/cms_portfolio, et domaines réels

### 8.1. Identités clients confirmées

| Dossier | Client confirmé |
|---|---|
| `sandbox/slibour` | Sophie Libourel, diététicienne (Narbonne) |
| `sandbox/breix` | Benjamin Reix, psychologue (Narbonne) |
| `sandbox/jgrall` | Justine Grall, psychopraticienne (Narbonne) |
| `sandbox/cms_portfolio` | Version de travail du site de Jérôme Loze (même client que `carcassonne/ferronnier-metallier/jerome-loze` et `sandbox/jloze`) |

### 8.2. `sandbox/jloze` vs `sandbox/cms_portfolio` — comparaison précise

Tout le code (templates et outillage) est **strictement identique** entre les deux dossiers : `diff` ne rapporte aucune différence sur `index.php`, `prestation.php`, l'ensemble de `inc/*.php` (y compris `inc/portfolio.php`), et l'ensemble de `_data/_config.php`, `_data/admin.php`, `_data/save.php`. Les deux dossiers partagent aussi exactement les mêmes fichiers de sauvegarde historiques dans `_data/` (`data-client-back20260601.json`, `admin_back20260610_1625.php`, `data-client _back20260614-1456.json`, `data-client _back20260614-1529.json`), avec des horodatages identiques à la seconde — preuve que les deux dossiers sont issus d'un **fork d'un même historique** à un instant donné (14 juin, ~15h41).

La seule différence réside dans le contenu de `_data/data-client.json` (module portfolio) et dans un fichier annexe :

| Élément | `cms_portfolio` | `jloze` |
|---|---|---|
| Dernière modification de `data-client.json` | 14 juin, 16h24 (juste après le fork) | **27 juin, 16h40** (13 jours plus tard) |
| `prest_01_folio_01` | Titre générique « Lance », description « Une décoration faite sur mesure » | Titre finalisé « *"La Sentinelle"* », description marketing complète avec tarif (« 590€ — Pièce unique forgée à la main... ») |
| `prest_01_folio_03` / `_07` | Titres/descriptions génériques placeholder | Titres et descriptions finalisées avec tarifs (390€, 490€) |
| `mail.html` (gabarit d'e-mail, 676 lignes) | Absent | Présent |

**Conclusion : `sandbox/jloze` est la version la plus complète et la plus récente**, et doit servir de source de référence pour ce client. `sandbox/cms_portfolio` est un fork resté au stade brouillon (placeholders non finalisés), abandonné après le 14 juin au profit du travail continué dans `jloze`. Les deux dossiers ne sont donc pas équivalents : ce n'est pas une redondance sans conséquence, l'un est strictement en retard sur l'autre.

### 8.3. Domaines réels identifiés

Recherche croisée sur `<link rel="canonical">`, `og:url`, le champ `url` du JSON-LD, et `ancien-code/sitemaps/` (qui ne référence que le domaine `lesiteduliberal.fr` lui-même, aucun domaine client). Les scripts `scriptsGTM/*.php` ne contiennent que l'URL générique `googletagmanager.com`, aucune information de domaine par client.

| Client | Domaine(s) trouvé(s) | Remarque |
|---|---|---|
| aix-en-provence/avocat/caroline-poplawec | `caroline-poplawec.fr` | Cohérent sur toutes les pages (canonical + og:url) |
| carcassonne/ferronnier-metallier/jerome-loze | `jerome-loze.fr` | Cohérent |
| france/preparateur-cavaliers/yannick-hebras | `yannick-hebras.fr` | Cohérent |
| narbonne/osteopathe/david-zerbib | `david-zerbib.fr` | Cohérent (le domaine `perfactive.fr` également présent est une plateforme de prise de RDV tierce, pas le domaine du client) |
| narbonne/dieteticienne/jessica-poux | `jessica-poux.fr` | Cohérent |
| narbonne/medium-magnetiseur/virginie-rouzeau | `virginie-rouzeau.fr` | Cohérent |
| narbonne/osteopathe/edouard-le-gourieres | **Aucun domaine propre** | Le `canonical` pointe vers `lesiteduliberal.fr/narbonne/osteopathe/edouard-le-gourieres/...` (hébergé en sous-chemin de l'agence) ; `og:url` contient `edouard-le-gourieres.fr` **sans protocole** (`<meta property="og:url" content="edouard-le-gourieres.fr">`) — valeur non fonctionnelle telle quelle, mais qui révèle le nom de domaine prévu |
| narbonne/osteopathe/paul-prevot | `paulosteopathe.fr` | Cohérent (le dossier contient aussi `robots.txt`, `llm.txt`, `llm-full.txt` — fichiers destinés aux robots d'indexation classiques et aux crawlers IA) |
| narbonne/psychologue/luella-barlow | `luella-barlow.fr` | Cohérent |
| narbonne/psychopraticien/marie-eve-millet | `marie-eve-millet.fr` | Cohérent |
| perpignan/psychopraticien/marjorie-marti | `marjorie-marti.fr` (et `www.marjorie-marti.fr`) | Cohérent |
| sandbox/slibour | `sophie-libourel.fr` | Valeur trouvée **sans protocole** (`href="sophie-libourel.fr/..."` au lieu de `https://sophie-libourel.fr/...`) sur les pages de prestations ; `mentions-legales.php` et `rdv-valide.php` ont un canonical vide — schéma d'URL non finalisé |
| sandbox/breix | **Aucun trouvé** | `canonical` vaut `#` ou `""` sur toutes les pages ; le JSON-LD a un champ `"url": ""` vide (cf. section 1 de cet audit) — site jamais rattaché à un nom de domaine dans cette version |
| sandbox/jgrall | **Aucun domaine définitif**, mais une URL de préproduction identifiée | `canonical` vaut `#`/vide comme pour `breix`, mais le JSON-LD contient `"url": "https://justine-grall-therapeute-narbonne.bellepresence.com"` — un sous-domaine de préproduction sur le domaine de l'agence (`bellepresence.com`), pas un domaine propre au client |
| sandbox/lbarlow | `luella-barlow.fr` — **identique** au domaine de `narbonne/psychologue/luella-barlow` | Voir 8.4 : versions quasi identiques, `sandbox/lbarlow` est un état antérieur |

### 8.4. `sandbox/lbarlow` vs `narbonne/psychologue/luella-barlow`

Les deux dossiers portent le même domaine (`luella-barlow.fr`) et un contenu à plus de 99,9 % identique (`index.php` : une seule ligne de différence de texte, sur la description d'un public cible). Seule différence structurelle : `sandbox/lbarlow/index.php` est daté du 17 avril, `narbonne/psychologue/luella-barlow/index.php` du 25 avril — **`sandbox/lbarlow` est l'état de travail antérieur, avant promotion du site vers son emplacement définitif `narbonne/psychologue/luella-barlow`**. Ce n'est pas un client distinct ; pour la migration, seul `narbonne/psychologue/luella-barlow` (la version la plus récente) doit être retenu comme référence.

### 8.5. Vérification d'exhaustivité — dossiers non couverts par l'audit initial

Un nouveau balayage complet d'`ancien-code/` (profondeur 4) confirme qu'**aucun client supplémentaire** n'a été omis parmi les dossiers organisés par ville/métier ou dans `sandbox/`. En revanche, plusieurs dossiers **non-clients** ou **doublons historiques** n'avaient pas été mentionnés dans les sections 1 à 7 et méritent d'être signalés pour être complets :

| Dossier | Nature |
|---|---|
| `ancien-code/avis/jerome-loze/` (`avis.php`, `config.php`) | Petit outil séparé de collecte/affichage d'avis clients pour Jérôme Loze — sans rapport avec le CMS de contenu ni le suivi de prestation déjà documentés |
| `ancien-code/contrats/` (`accept.php`, `site-web/admin.php`, `site-web/contrat-template.html`, `site-web/test.php`) | Outil interne de gestion de contrats pour l'agence elle-même (signature/acceptation de devis), pas un site client |
| `ancien-code/_suivi/` (racine) | Encore une instance générique du gabarit de suivi de prestation (`admin.html`/`data.json`/`save.php`/`suivi-prestations.html`), en plus de `_cms/v3/_suivi/` déjà documenté en section 3 |
| `ancien-code/narbonne/psychologue/luella-barlow_back/` | Sauvegarde d'un état **antérieur** du site (12 avril, anciens slugs d'URL type `luella-barlow.php`/`metier.php`/`psychologue-narbonne.php`), supplantée par le dossier `luella-barlow/` actuel (25 avril) — pas un client distinct |
| `ancien-code/narbonne/osteopathe/paul-prevot - Copie 20260627/` | Sauvegarde d'un état **antérieur** du site (`index.php` du 19 avril, moins de pages de prestations que le dossier principal), le dossier `paul-prevot/` actuel (27 juin) étant la version courante — pas un client distinct |

Aucun de ces dossiers ne change la liste des 17 sites clients déjà recensée en section 4 ; ils confirment simplement qu'il existe, en plus des sites, des outils internes annexes (avis, contrats) et des sauvegardes ponctuelles non nettoyées (`_back`, `- Copie AAAAMMJJ`) à ne pas confondre avec de nouveaux clients lors de la migration.
