# register_post_status : des statuts de workflow au-delà de publié

> Un studio de design veut qu'un article reste invisible du public mais clairement identifiable par le client comme « en validation », avec son propre libellé et son propre compteur dans l'administration.

- Auteur : WordPress Développement
- Publié le : 2023-10-06
- Mis à jour le : 2026-09-30
- Catégorie : Extensions
- URL : https://www.wpmoderne.fr/extensions/register-post-status-statuts-workflow-au-dela-publie/

## L’essentiel

- Un statut personnalisé s'affiche dans le menu déroulant d'état comme les statuts natifs
- Le compteur de l'onglet de filtre en haut de liste nécessite un argument dédié
- Les capacités de lecture doivent être pensées séparément pour un client externe

Un studio de design produit des fiches de recommandations pour des clients externes, stockées comme un CPT `recommandation` dans WordPress. Le processus de travail comporte une étape intermédiaire cruciale : une fois rédigée par le designer, une fiche doit être visible par le client pour validation, sans être publique, et surtout sans se confondre visuellement avec un simple brouillon interne dans la liste d'administration. Le statut « Brouillon » natif ne porte pas cette nuance : il ne dit rien sur qui doit agir ensuite ni sur l'état réel du processus de validation.

La réponse à ce besoin ne passe pas par une métadonnée personnalisée affichée à côté du statut natif, une solution bancale qui demanderait de dupliquer la logique d'affichage un peu partout dans l'administration. La bonne réponse est de déclarer un véritable statut de publication personnalisé, avec `register_post_status()`, qui s'intègre alors nativement au même menu déroulant que « Publié » ou « Brouillon ».

## Déclarer le statut

```
add_action( 'init', function() {
    register_post_status( 'en_validation_client', array(
        'label'                     => _x( 'En validation client', 'statut de publication', 'studio-design' ),
        'public'                    => false,
        'internal'                  => false,
        'protected'                 => true,
        'private'                   => false,
        'show_in_admin_status_list' => true,
        'show_in_admin_all_list'    => true,
        'label_count'               => _n_noop(
            'En validation client <span class="count">(%s)</span>',
            'En validation client <span class="count">(%s)</span>',
            'studio-design'
        ),
    ) );
} );
```

Chaque argument porte une nuance précise qu'il vaut la peine de détailler. `public` à `false` garantit que ce statut ne rend jamais la fiche accessible publiquement, même en connaissant son URL directe. `protected` à `true` indique que ce contenu nécessite une capacité d'édition pour être consulté dans l'administration, ce qui exclut un simple abonné du site mais permet à un client disposant d'un rôle adapté d'y accéder, à condition que ce rôle porte la capacité appropriée. `show_in_admin_status_list` et `show_in_admin_all_list` déterminent respectivement l'apparition du statut dans le menu déroulant d'édition d'une fiche, et son inclusion dans l'onglet « Tous » de la liste d'administration.

## Ajouter le statut au menu déroulant de l'écran d'édition

> L'essentiel à retenir : Un statut personnalisé s'affiche dans le menu déroulant d'état comme les statuts natifs ; Le compteur de l'onglet de filtre en haut de liste nécessite un argument dédié ; Les capacités de lecture doivent être pensées séparément pour un client externe

Déclarer le statut avec `register_post_status()` ne suffit pas à le faire apparaître automatiquement dans le menu déroulant visible lors de l'édition d'une fiche : WordPress affiche ce menu à partir d'une liste fixe de statuts natifs, et il faut explicitement y ajouter les statuts personnalisés via un script JavaScript injecté sur l'écran d'édition, une limitation connue de longue date dans le cœur de WordPress :

```
add_action( 'admin_footer-post.php', 'studio_design_statut_menu_classique' );
add_action( 'admin_footer-post-new.php', 'studio_design_statut_menu_classique' );

function studio_design_statut_menu_classique() {
	global $post;

	if ( ! $post || 'recommandation' !== $post->post_type ) {
		return;
	}

	$libelle = __( 'En validation client', 'studio-design' );
	$actif   = ( 'en_validation_client' === $post->post_status );
	?>
	<script>
	jQuery( function ( $ ) {
		$( '#post_status' ).append(
			$( '<option>', { value: 'en_validation_client', text: <?php echo wp_json_encode( $libelle ); ?> } )
		);
		<?php if ( $actif ) : ?>
		$( '#post_status' ).val( 'en_validation_client' );
		$( '#post-status-display' ).text( <?php echo wp_json_encode( $libelle ); ?> );
		<?php endif; ?>
	} );
	</script>
	<?php
}
```

Ce script ne vise que l'éditeur classique, où le bloc « Publier » contient un menu déroulant `#post_status`. Il ajoute l'option, la sélectionne si la fiche est déjà dans cet état et met à jour le libellé affiché. Les libellés passent par `wp_json_encode()` pour être injectés sans risque dans le JavaScript. Sur l'écran de l'éditeur de blocs, l'élément `#post_status` n'existe pas et le script n'a simplement aucun effet.

## Dans l'éditeur de blocs

L'éditeur de blocs n'affiche pas le menu des états d'une fiche de la même façon : il expose un panneau « Résumé » avec des états natifs. Pour y proposer le statut personnalisé, on ajoute un champ dans ce panneau avec `PluginPostStatusInfo`, qui modifie l'attribut `status` de la fiche en cours d'édition :

```
const { registerPlugin } = wp.plugins;
const { PluginPostStatusInfo } = wp.editPost;
const { SelectControl } = wp.components;
const { useSelect, useDispatch } = wp.data;
const { createElement: el } = wp.element;
const { __ } = wp.i18n;

function SelecteurStatut() {
	const statut = useSelect(
		( select ) => select( 'core/editor' ).getEditedPostAttribute( 'status' ),
		[]
	);
	const { editPost } = useDispatch( 'core/editor' );

	if ( 'draft' !== statut && 'en_validation_client' !== statut ) {
		return null;
	}

	return el(
		PluginPostStatusInfo,
		{},
		el( SelectControl, {
			label: __( 'Validation', 'studio-design' ),
			value: statut,
			options: [
				{ label: __( 'Brouillon', 'studio-design' ), value: 'draft' },
				{ label: __( 'En validation client', 'studio-design' ), value: 'en_validation_client' },
			],
			onChange: ( valeur ) => editPost( { status: valeur } ),
		} )
	);
}

registerPlugin( 'studio-design-statut', { render: SelecteurStatut } );
```

Le champ ne s'affiche que pour une fiche en brouillon ou en validation : proposer ce choix à une fiche déjà publiée la ferait repasser dans un état non public par simple clic. Le script se charge uniquement pour le type de contenu concerné :

```
add_action( 'enqueue_block_editor_assets', function () {
	if ( 'recommandation' !== get_post_type() ) {
		return;
	}

	wp_enqueue_script(
		'studio-design-statut',
		plugins_url( 'statut.js', __FILE__ ),
		array( 'wp-plugins', 'wp-edit-post', 'wp-components', 'wp-data', 'wp-element', 'wp-i18n' ),
		'1.0.0',
		true
	);
} );
```

Le type de contenu doit être exposé dans l'API REST (`show_in_rest`) pour être édité dans l'éditeur de blocs. Un statut déclaré avec `internal` à `false` est en principe accepté par cette API ; testez l'enregistrement dans votre version avant de le promettre au client.

## Le compteur de l'onglet de filtre

La liste d'administration affiche en haut des onglets de filtre : « Tous (12) », « Publiés (4) », et désormais « En validation client (3) ». Le nombre vient de l'argument `label_count`, déjà présent dans la déclaration plus haut. Il attend un couple de chaînes singulier et pluriel, produit par `_n_noop()`, où `%s` marque l'emplacement du nombre. Sans ce marqueur, l'onglet s'affiche sans compteur ; c'est l'oubli le plus courant. Avec `show_in_admin_status_list` à `true`, l'onglet existe ; avec `show_in_admin_all_list` à `true`, les fiches restent visibles dans « Tous ».

Pour afficher aussi l'état dans la colonne du titre, le filtre `display_post_states` reçoit la liste des mentions et la fiche concernée, et permet d'ajouter une mention : utile quand l'onglet actif n'est pas celui du statut.

## Prévenir le client à chaque changement d'état

Un statut de workflow n'a de valeur que si quelqu'un est averti. Le crochet `transition_post_status` reçoit le nouvel état, l'ancien et la fiche, ce qui suffit pour envoyer un message uniquement lors du passage en validation :

```
add_action( 'transition_post_status', 'studio_design_notifier_client', 10, 3 );

function studio_design_notifier_client( $nouveau, $ancien, $post ) {
	if ( 'recommandation' !== $post->post_type
		|| $nouveau === $ancien
		|| 'en_validation_client' !== $nouveau ) {
		return;
	}

	$adresse = get_post_meta( $post->ID, '_email_client', true );
	if ( ! is_email( $adresse ) ) {
		return;
	}

	wp_mail(
		$adresse,
		sprintf( __( 'Fiche à valider : %s', 'studio-design' ), $post->post_title ),
		sprintf(
			__( "Une fiche attend votre validation.\n\nOuvrir la fiche : %s", 'studio-design' ),
			get_edit_post_link( $post->ID, 'raw' )
		)
	);
}
```

## Qui peut lire une fiche en validation ?

C'est le troisième point de l'encadré, et le plus souvent négligé. Un statut `protected` n'est pas public : sur l'adresse de la fiche, un visiteur anonyme reçoit une erreur 404, et un utilisateur connecté n'y accède que s'il possède la capacité d'édition correspondante. Un client qui a simplement le rôle « Abonné » verra donc, lui aussi, une page introuvable. Deux approches existent. La première donne au client un rôle dédié, avec les capacités d'un type de contenu à droits propres (`capability_type` personnalisé) limitées à ses propres fiches. La seconde construit une page de consultation qui vérifie elle-même le droit d'accès, sans ouvrir l'administration. Dans les deux cas, ne comptez pas sur le statut seul pour protéger ou pour ouvrir l'accès, et vérifiez dans votre version comment une requête qui cible explicitement ce statut se comporte pour un visiteur non connecté.

## Pièges à connaître

- **Désactiver l'extension rend les fiches orphelines.** Un statut qui n'est plus déclaré n'apparaît plus dans les onglets : les fiches existent toujours en base, mais deviennent difficiles à retrouver.
- **Un statut n'est pas une permission.** Changer l'état d'une fiche ne change aucun droit de lecture ou d'écriture tant que vous n'avez pas écrit la règle correspondante.
- **Les identifiants de statut sont limités.** La colonne `post_status` tient sur vingt caractères : `en_validation_client` en compte vingt, la limite exacte ; choisissez des identifiants courts.
- **Le libellé doit rester traduisible.** Passez-le par `_x()` ou `__()` avec un domaine de traduction, comme dans la déclaration.
- **Le retour à un état précédent doit rester possible.** Prévoyez le cas où le client refuse : quel statut reprend la fiche ?

> Un état de workflow vaut ce que valent les règles qui l'accompagnent : qui le pose, qui le lit, qui est prévenu.

## Conclusion

Déclarer le statut avec `register_post_status()` ne prend que quelques lignes ; la valeur se trouve dans ce qui l'entoure : le menu qui le propose dans chaque éditeur, le compteur que permet `label_count`, la notification au client et surtout la règle de lecture. Traitez ces quatre morceaux ensemble et votre studio obtiendra un vrai circuit de validation, visible dans l'administration comme n'importe quel état natif, sans métadonnée parallèle à maintenir.
