Avec la structure de permaliens /%category%/%postname%/, la règle (.+?)/([^/]+)(?:/([0-9]+))?/?$ lit un dernier segment numérique comme un numéro de page : l'article répond 404 tout en restant listé partout. Deux chemins produisaient un tel slug : - un titre vide à la première publication — le cœur retombe alors sur l'ID du post et ne régénère plus jamais le slug ensuite, même titre corrigé ; c'est ce que provoquait post-title-required.php, qui laisse le post s'écrire en publish avant d'annuler son statut ; - un titre légitimement numérique (« 1917 »). - inc/post-slug-guard.php : filtre wp_unique_post_slug limité à post/page, suffixe les slugs purement numériques (1917 → 1917-2). Les nav_menu_item et attachments, dont les slugs numériques sont normaux, ne sont pas touchés. - pods-save-error-handler.php : l'annulation de statut vide aussi post_name quand le titre est vide, pour qu'il se régénère depuis le vrai titre. Les deux articles déjà touchés en prod restent à re-sluguer à la main. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011w7RzAHv4r1Yahnm2NwDDG
70 lines
2.3 KiB
PHP
70 lines
2.3 KiB
PHP
<?php
|
|
/**
|
|
* Garde-fou sur les slugs d'articles et de pages.
|
|
*
|
|
* La structure de permaliens du site est /%category%/%postname%/. La règle de
|
|
* réécriture qui sert les articles est :
|
|
*
|
|
* (.+?)/([^/]+)(?:/([0-9]+))?/?$ => category_name=$1&name=$2&page=$3
|
|
*
|
|
* Le dernier groupe est un numéro de page (pagination interne d'un article via
|
|
* <!--nextpage-->). Un slug purement numérique est donc avalé comme numéro de
|
|
* page : l'article devient irroutable et répond 404, alors qu'il continue de
|
|
* s'afficher dans toutes les listes.
|
|
*
|
|
* Deux chemins produisent un tel slug :
|
|
* - un titre vide au moment de la première publication — le cœur retombe alors
|
|
* sur l'ID du post (wp-includes/post.php, wp_insert_post : sanitize_title(
|
|
* $post_title, $post_id)) et ne régénère plus jamais le slug ensuite ;
|
|
* - un titre légitimement numérique (« 1917 »).
|
|
*
|
|
* Ce module traite le second cas à la source ; le premier est traité en amont
|
|
* dans inc/pods-save-error-handler.php (le slug figé est vidé au moment où le
|
|
* statut est annulé, pour qu'il se régénère depuis le vrai titre).
|
|
*
|
|
* Portée volontairement limitée à post/page : les nav_menu_item et les
|
|
* attachments ont légitimement des slugs numériques et ne passent pas par
|
|
* cette règle de réécriture.
|
|
*/
|
|
|
|
/**
|
|
* Un slug purement numérique est-il à corriger pour ce type de contenu ?
|
|
* Fonction pure (testable) : la requête d'unicité vit dans le filtre.
|
|
*/
|
|
function thalim_slug_needs_guard( string $slug, string $post_type ): bool {
|
|
if ( ! in_array( $post_type, [ 'post', 'page' ], true ) ) {
|
|
return false;
|
|
}
|
|
return $slug !== '' && ctype_digit( $slug );
|
|
}
|
|
|
|
/**
|
|
* Suffixe les slugs purement numériques (-2, -3, …), en suivant la convention
|
|
* du cœur pour les slugs en conflit.
|
|
*/
|
|
add_filter( 'wp_unique_post_slug', function ( $slug, $post_id, $post_status, $post_type ) {
|
|
$slug = (string) $slug;
|
|
|
|
if ( ! thalim_slug_needs_guard( $slug, (string) $post_type ) ) {
|
|
return $slug;
|
|
}
|
|
|
|
global $wpdb;
|
|
|
|
$suffix = 2;
|
|
do {
|
|
$alt = $slug . '-' . $suffix;
|
|
$exists = (int) $wpdb->get_var( $wpdb->prepare(
|
|
"SELECT ID FROM {$wpdb->posts}
|
|
WHERE post_name = %s AND post_type = %s AND ID != %d
|
|
LIMIT 1",
|
|
$alt,
|
|
$post_type,
|
|
(int) $post_id
|
|
) );
|
|
$suffix++;
|
|
} while ( $exists && $suffix < 100 );
|
|
|
|
return $alt;
|
|
}, 10, 4 );
|