Étape 5 SEO : description auto (titre redondant, entités HTML) + URL d'aperçu incorrecte #5

Closed
opened 2026-05-16 08:52:00 +00:00 by cedricAbonnel · 0 comments
Owner

Contexte

L'étape 5 du wizard (/edit/<uuid>/5) présente trois problèmes dans la description SEO auto-générée et dans l'aperçu moteur de recherche.


Problème 1 — Le titre de l'article est repris dans la description

Les moteurs de recherche affichent le <title> et la <meta name="description"> séparément. Si la description commence par le titre, les ~155 caractères disponibles sont gaspillés et Google tronque souvent ce doublon.

Cela survient quand le contenu Markdown débute par une phrase identique ou proche du titre (cas fréquent).

Correction

Après génération du texte brut, supprimer le titre en tête de description (insensible à la casse) :

$_plain = trim((string)preg_replace('/\s+/', ' ',
    html_entity_decode(strip_tags($_pd->text((string)($draft['content'] ?? ''))),
        ENT_QUOTES | ENT_HTML5, 'UTF-8')
));
$_titleClean = trim($draft['title'] ?? '');
if ($_titleClean !== '' && stripos($_plain, $_titleClean) === 0) {
    $_plain = ltrim(substr($_plain, strlen($_titleClean)));
}
$autoSeoDesc = mb_strimwidth($_plain, 0, 155, '…');

Problème 2 — Entités HTML dans la description (&amp;, &nbsp;…)

La génération actuelle (public/index.php, lignes ~658, ~1026, ~1051) :

$autoSeoDesc = mb_strimwidth(
    trim((string)preg_replace('/\s+/', ' ',
        strip_tags($_pd->text((string)($draft['content'] ?? '')))
    )),
    0, 155, '…'
);

Parsedown::text() produit du HTML. strip_tags() retire les balises mais laisse les entités HTML brutes : &amp;, &nbsp;, &lt;, &#39;… Ces entités apparaissent telles quelles dans la balise <meta name="description"> et sont affichées crûment dans les résultats Google.

Exemple : AT&T dans le contenu → AT&amp;T dans la description SEO.

Correction

Ajouter html_entity_decode() après strip_tags() (inclus dans la correction du problème 1 ci-dessus).


Problème 3 — URL d'aperçu affiche un mauvais slug

Dans templates/wizard/step5.php, l'URL d'aperçu est initialisée côté serveur :

// ligne 42
<?= htmlspecialchars($_base . '/post/' . ($postSlug ?? '')) ?>

Puis post_confirm.js tente de la mettre à jour dynamiquement en écoutant #confirm-slug :

var slug = document.getElementById('confirm-slug').value.trim();
document.getElementById('preview-url').textContent = baseUrl + slug;

Mais #confirm-slug est défini dans post_confirm.php (step 6), pas dans step5. En mode édition, l'élément est absent → getElementById retourne null → l'URL d'aperçu reste figée sur $postSlug (slug du brouillon), qui peut différer du slug publié.

Correction

Dans step5.php, rendre l'URL d'aperçu non-interactive (elle est en lecture seule à cette étape) : supprimer la dépendance JS sur #confirm-slug en n'incluant pas post_confirm.js à l'étape 5, ou en affichant simplement le slug actuel de l'article (pas du brouillon) :

// Utiliser le slug publié ($article['slug']) en priorité sur celui du brouillon
$postSlug = $article['slug'] ?? ($draft['slug'] ?? '');

Et dans le #pc-data, ne pas passer data-base-url si #confirm-slug est absent, pour éviter que updatePreview() écrase l'URL avec une chaîne vide.


Fichiers concernés

Fichier Lignes
public/index.php ~658, ~1026, ~1051 (génération de $autoSeoDesc)
templates/wizard/step5.php ligne 42 (URL d'aperçu), ligne 141 (inclusion de post_confirm.js)
public/assets/js/post_confirm.js updatePreview() (guard si #confirm-slug absent)

Critères d'acceptation

  • La description auto ne commence plus par le titre de l'article
  • Aucune entité HTML (&amp;, &nbsp;…) dans la description générée
  • Les trois occurrences de $autoSeoDesc dans index.php sont corrigées
  • L'URL d'aperçu SEO affiche le bon slug (slug publié, pas celui du brouillon)
  • Pas d'erreur JS si #confirm-slug est absent
  • Testé sur http://abonnel-wiki.acegrp.lan/edit/e9ba3af5-1370-4bc7-962c-68f33b6f6bfe/5
## Contexte L'étape 5 du wizard (`/edit/<uuid>/5`) présente trois problèmes dans la description SEO auto-générée et dans l'aperçu moteur de recherche. --- ## Problème 1 — Le titre de l'article est repris dans la description Les moteurs de recherche affichent le `<title>` et la `<meta name="description">` séparément. Si la description commence par le titre, les ~155 caractères disponibles sont gaspillés et Google tronque souvent ce doublon. Cela survient quand le contenu Markdown débute par une phrase identique ou proche du titre (cas fréquent). ### Correction Après génération du texte brut, supprimer le titre en tête de description (insensible à la casse) : ```php $_plain = trim((string)preg_replace('/\s+/', ' ', html_entity_decode(strip_tags($_pd->text((string)($draft['content'] ?? ''))), ENT_QUOTES | ENT_HTML5, 'UTF-8') )); $_titleClean = trim($draft['title'] ?? ''); if ($_titleClean !== '' && stripos($_plain, $_titleClean) === 0) { $_plain = ltrim(substr($_plain, strlen($_titleClean))); } $autoSeoDesc = mb_strimwidth($_plain, 0, 155, '…'); ``` --- ## Problème 2 — Entités HTML dans la description (`&amp;`, `&nbsp;`…) La génération actuelle (`public/index.php`, lignes ~658, ~1026, ~1051) : ```php $autoSeoDesc = mb_strimwidth( trim((string)preg_replace('/\s+/', ' ', strip_tags($_pd->text((string)($draft['content'] ?? ''))) )), 0, 155, '…' ); ``` `Parsedown::text()` produit du HTML. `strip_tags()` retire les balises mais **laisse les entités HTML brutes** : `&amp;`, `&nbsp;`, `&lt;`, `&#39;`… Ces entités apparaissent telles quelles dans la balise `<meta name="description">` et sont affichées crûment dans les résultats Google. Exemple : `AT&T` dans le contenu → `AT&amp;T` dans la description SEO. ### Correction Ajouter `html_entity_decode()` après `strip_tags()` (inclus dans la correction du problème 1 ci-dessus). --- ## Problème 3 — URL d'aperçu affiche un mauvais slug Dans `templates/wizard/step5.php`, l'URL d'aperçu est initialisée côté serveur : ```php // ligne 42 <?= htmlspecialchars($_base . '/post/' . ($postSlug ?? '')) ?> ``` Puis `post_confirm.js` tente de la mettre à jour dynamiquement en écoutant `#confirm-slug` : ```js var slug = document.getElementById('confirm-slug').value.trim(); document.getElementById('preview-url').textContent = baseUrl + slug; ``` Mais `#confirm-slug` est défini dans `post_confirm.php` (step 6), **pas dans step5**. En mode édition, l'élément est absent → `getElementById` retourne `null` → l'URL d'aperçu reste figée sur `$postSlug` (slug du brouillon), qui peut différer du slug publié. ### Correction Dans `step5.php`, rendre l'URL d'aperçu non-interactive (elle est en lecture seule à cette étape) : supprimer la dépendance JS sur `#confirm-slug` en n'incluant pas `post_confirm.js` à l'étape 5, ou en affichant simplement le slug actuel de l'article (pas du brouillon) : ```php // Utiliser le slug publié ($article['slug']) en priorité sur celui du brouillon $postSlug = $article['slug'] ?? ($draft['slug'] ?? ''); ``` Et dans le `#pc-data`, ne pas passer `data-base-url` si `#confirm-slug` est absent, pour éviter que `updatePreview()` écrase l'URL avec une chaîne vide. --- ## Fichiers concernés | Fichier | Lignes | |---|---| | `public/index.php` | ~658, ~1026, ~1051 (génération de `$autoSeoDesc`) | | `templates/wizard/step5.php` | ligne 42 (URL d'aperçu), ligne 141 (inclusion de `post_confirm.js`) | | `public/assets/js/post_confirm.js` | `updatePreview()` (guard si `#confirm-slug` absent) | --- ## Critères d'acceptation - [ ] La description auto ne commence plus par le titre de l'article - [ ] Aucune entité HTML (`&amp;`, `&nbsp;`…) dans la description générée - [ ] Les trois occurrences de `$autoSeoDesc` dans `index.php` sont corrigées - [ ] L'URL d'aperçu SEO affiche le bon slug (slug publié, pas celui du brouillon) - [ ] Pas d'erreur JS si `#confirm-slug` est absent - [ ] Testé sur http://abonnel-wiki.acegrp.lan/edit/e9ba3af5-1370-4bc7-962c-68f33b6f6bfe/5
Sign in to join this conversation.
No Label
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: cedricAbonnel/abonnel-www#5