# Bulk actions personnalisées dans les listes d’administration

> Ajouter une action groupée « Marquer comme archivé » à côté de « Modifier » et « Mettre à la corbeille », du menu déroulant jusqu'au traitement sécurisé côté serveur.

- Auteur : Clément Hadrot
- Publié le : 2021-06-08
- Mis à jour le : 2021-06-08
- Catégorie : Extensions
- URL : https://wpmoderne.dev.wordpress-developpement.fr/extensions/bulk-actions-personnalisees-listes-administration/

## L’essentiel

- Deux filtres suffisent, un pour le menu, un pour le traitement
- Le nonce de bulk action est déjà géré par WordPress, pas besoin d'en créer un
- Toujours rediriger après traitement pour éviter une resoumission

Une agence de presse gère un CPT « communiqué » avec plusieurs centaines d'entrées. L'équipe éditoriale demande une action groupée pour marquer plusieurs communiqués comme « archivés » d'un coup, sans passer par chaque fiche individuellement. WordPress propose nativement ce mécanisme pour toutes les listes basées sur `WP_List_Table`, y compris l'écran standard des articles ou d'un type de contenu personnalisé, via deux filtres dynamiques dont le nom dépend de l'identifiant de l'écran courant.

Voici comment ajouter une action groupée complète, du menu déroulant jusqu'à la mise à jour en base, en respectant les conventions de sécurité attendues par le cœur de WordPress.

## Étape 1 : ajouter l'option au menu déroulant

Le filtre `bulk_actions-{$screen_id}` permet d'ajouter une entrée au menu déroulant « Actions groupées » présent en haut et en bas de la liste. Pour un CPT nommé `communique`, l'identifiant d'écran correspondant est `edit-communique` :

```
add_filter( 'bulk_actions-edit-communique', function( $actions ) {
    $actions['archiver_communique'] = __( 'Marquer comme archivé', 'agence-presse' );
    return $actions;
} );
```

La clé du tableau, `archiver_communique`, est la valeur qui sera transmise dans l'URL au moment du traitement ; elle doit être unique et suffisamment explicite pour ne pas entrer en collision avec une action existante d'une autre extension.

## Étape 2 : traiter l'action de façon sécurisée

> L'essentiel à retenir : Deux filtres suffisent, un pour le menu, un pour le traitement ; Le nonce de bulk action est déjà géré par WordPress, pas besoin d'en créer un ; Toujours rediriger après traitement pour éviter une resoumission

Le filtre `handle_bulk_actions-{$screen_id}` reçoit l'URL de redirection courante, le nom de l'action demandée, et la liste des identifiants sélectionnés par l'utilisateur. C'est ici, et uniquement ici, que le traitement doit avoir lieu, après vérification des permissions :

```
add_filter( 'handle_bulk_actions-edit-communique', function( $redirection, $action, $post_ids ) {
    if ( 'archiver_communique' !== $action ) {
        return $redirection;
    }

    $nombre_traite = 0;

    foreach ( $post_ids as $post_id ) {
        if ( ! current_user_can( 'edit_post', $post_id ) ) {
            continue;
        }

        update_post_meta( $post_id, '_statut_communique', 'archive' );
        $nombre_traite++;
    }

    $redirection = add_query_arg( 'communiques_archives', $nombre_traite, $redirection );

    return $redirection;
}, 10, 3 );
```

Le point important à retenir ici : WordPress a déjà vérifié le nonce de l'action groupée avant même d'appeler ce filtre, dans le traitement natif de `WP_List_Table`. Il n'est donc pas nécessaire, et même contre-productif, de rejouer une vérification de nonce manuelle à cet endroit. En revanche, la vérification des capacités avec `current_user_can()` reste entièrement à la charge du développeur, hook par hook, article par article, puisque WordPress ne peut pas savoir a priori quelles permissions sont pertinentes pour une action métier personnalisée.

### Pourquoi passer par add_query_arg plutôt que par une redirection directe

- Le paramètre ajouté à l'URL de redirection permet d'afficher ensuite un message de confirmation adapté au nombre réel d'éléments traités.
- Cette redirection après traitement suit le motif « Post/Redirect/Get », qui évite qu'un rechargement de page ne déclenche une nouvelle exécution de l'action groupée.

## Étape 3 : afficher un message de confirmation

```
add_action( 'admin_notices', function() {
    if ( ! empty( $_REQUEST['communiques_archives'] ) ) {
        $nombre = (int) $_REQUEST['communiques_archives'];
        printf(
            '%s',
            sprintf(
                _n(
                    '%d communiqué a été archivé.',
                    '%d communiqués ont été archivés.',
                    $nombre,
                    'agence-presse'
                ),
                $nombre
            )
        );
    }
} );
```

L'usage de `_n()` ici gère correctement le pluriel français, un détail souvent négligé qui donne pourtant une impression de soin bien supérieure à l'utilisateur final.

## Pièges observés en production

- Oublier de vérifier `current_user_can()` par article, en supposant à tort que la capacité globale de l'écran suffit à couvrir chaque élément individuellement, ce qui pose problème avec des permissions granulaires par auteur.
- Traiter des volumes de plusieurs milliers d'identifiants d'un coup dans ce filtre, ce qui peut dépasser le temps d'exécution maximal du serveur ; au-delà de quelques centaines d'éléments, une action groupée doit déléguer à une tâche asynchrone plutôt que de tout traiter en une seule requête HTTP.
- Nommer une action groupée avec une clé trop générique comme `archiver`, qui peut entrer en collision avec une autre extension déclarant la même clé sur le même écran.

> Une action groupée qui ne vérifie pas les permissions article par article n'est pas un gain de temps, c'est une porte dérobée pratique.

## En résumé

Les deux filtres `bulk_actions-{$screen}` et `handle_bulk_actions-{$screen}` couvrent l'essentiel du besoin sans passer par la construction manuelle d'un `WP_List_Table` complet. Le nonce est déjà pris en charge par le cœur, ce qui laisse au développeur la responsabilité claire des permissions et de la robustesse du traitement en cas de sélection volumineuse.
