Un client qui publie des fiches produits très proches les unes des autres — même structure, mêmes champs, seul le texte change — finit toujours par demander la même chose : un bouton « Dupliquer ». Les extensions dédiées existent, mais elles ajoutent souvent des réglages inutiles pour un besoin aussi ciblé. Un snippet maison, greffé sur la liste des articles, fait le travail en une trentaine de lignes.
La recette repose sur trois ingrédients : un lien d’action ajouté dans la liste des contenus, une fonction accrochée à admin_action_{action} qui reconstruit le contenu avec wp_insert_post(), et une copie systématique des métadonnées et des taxonomies associées.
Ajouter le lien « Dupliquer » dans la liste des articles
Le filtre post_row_actions permet d’ajouter une action personnalisée à côté de « Modifier » et « Corbeille » dans la liste des articles. Le lien pointe vers une URL contenant l’identifiant du contenu et un nonce de sécurité.
function catalogue_lien_dupliquer( $actions, $post ) {
if ( ! current_user_can( 'edit_posts' ) ) {
return $actions;
}
$url = wp_nonce_url(
admin_url( 'admin.php?action=catalogue_dupliquer&post=' . $post->ID ),
'catalogue_dupliquer_' . $post->ID
);
$actions['dupliquer'] = '<a href="' . esc_url( $url ) . '">Dupliquer</a>';
return $actions;
}
add_filter( 'post_row_actions', 'catalogue_lien_dupliquer', 10, 2 );
Reconstruire le contenu avec wp_insert_post
La fonction accrochée à admin_action_catalogue_dupliquer récupère l’article d’origine avec get_post(), puis recrée un nouvel article avec les mêmes champs, en préfixant le titre pour distinguer la copie de l’original.

function catalogue_dupliquer_contenu() {
$post_id = isset( $_GET['post'] ) ? absint( $_GET['post'] ) : 0;
check_admin_referer( 'catalogue_dupliquer_' . $post_id );
$original = get_post( $post_id );
if ( ! $original || ! current_user_can( 'edit_post', $post_id ) ) {
wp_die( 'Contenu introuvable ou accès refusé.' );
}
$nouvel_id = wp_insert_post( array(
'post_title' => $original->post_title . ' (copie)',
'post_content' => $original->post_content,
'post_excerpt' => $original->post_excerpt,
'post_status' => 'draft',
'post_type' => $original->post_type,
'post_author' => get_current_user_id(),
) );
if ( is_wp_error( $nouvel_id ) ) {
wp_die( 'Erreur lors de la duplication.' );
}
catalogue_copier_metas_et_taxonomies( $post_id, $nouvel_id );
wp_safe_redirect( admin_url( 'edit.php?post_type=' . $original->post_type ) );
exit;
}
add_action( 'admin_action_catalogue_dupliquer', 'catalogue_dupliquer_contenu' );
Copier les métadonnées et les taxonomies
Le contenu principal ne suffit pas : les champs personnalisés (prix, référence produit, caractéristiques) et les taxonomies (catégories, étiquettes, taxonomies personnalisées) doivent suivre. WordPress fournit des fonctions dédiées pour chacun.
function catalogue_copier_metas_et_taxonomies( $source_id, $cible_id ) {
// Métadonnées
$metas = get_post_meta( $source_id );
foreach ( $metas as $cle => $valeurs ) {
foreach ( $valeurs as $valeur ) {
add_post_meta( $cible_id, $cle, maybe_unserialize( $valeur ) );
}
}
// Taxonomies
$taxonomies = get_object_taxonomies( get_post_type( $source_id ) );
foreach ( $taxonomies as $taxonomie ) {
$termes = wp_get_object_terms( $source_id, $taxonomie, array( 'fields' => 'ids' ) );
wp_set_object_terms( $cible_id, $termes, $taxonomie );
}
}
L’image mise en avant mérite un traitement à part : c’est une simple métadonnée (_thumbnail_id), donc elle est déjà copiée par la boucle ci-dessus. Aucun code supplémentaire n’est nécessaire pour elle.
Éviter les pièges classiques
- Toujours vérifier
current_user_can( 'edit_post', $post_id )avant la duplication, pas seulementedit_postsen général. - Utiliser
check_admin_referer()pour valider le nonce, sans quoi n’importe quel lien externe pourrait déclencher une duplication. - Créer la copie en statut
draftplutôt qu’en reprenant le statut d’origine, pour éviter de publier accidentellement du contenu en double.
Sur les CPT avec des champs répétables, je préfère toujours tester la duplication sur un contenu réellement complexe avant de la livrer : c’est là que les métadonnées sérialisées imbriquées font parfois des surprises si
maybe_unserialize()est oublié.
En résumé
Trois briques standards de l’API WordPress — post_row_actions, wp_insert_post() et les fonctions de copie de metas et de taxonomies — suffisent à reproduire l’essentiel d’une extension de duplication, sans dépendance supplémentaire. Le nonce et la vérification de capacité restent les deux points à ne jamais sauter, même sur un snippet qui semble anodin.