¿Alguna vez has deseado poder duplicar una entrada, página o tipo de contenido personalizado (CPT) en WordPress con un solo clic? Es una funcionalidad increíblemente útil, especialmente si trabajas con plantillas, contenido recurrente o simplemente quieres hacer pruebas sin afectar el original. Aunque WordPress no incluye esta opción de forma nativa, es sorprendentemente sencillo añadirla con un pequeño fragmento de código.
En este artículo, te mostraremos cómo implementar una solución robusta y segura que te permitirá duplicar entradas y páginas en WordPress de manera eficiente, manteniendo toda la información relevante y creando una copia en estado de borrador, lista para tus ediciones.
¿Por qué duplicar entradas en WordPress?
Duplicar contenido es una práctica común para muchos usuarios y desarrolladores de WordPress. Aquí algunas razones clave:
- Ahorro de tiempo: Evita recrear el mismo diseño o estructura desde cero.
- Creación de plantillas: Diseña un post o página base y duplícalo para nuevos contenidos.
- Pruebas y modificaciones: Realiza cambios en una copia sin afectar la versión publicada.
- Contenido similar: Si tienes series de artículos o productos con características similares, duplicar acelera el proceso.
El código para duplicar posts en WordPress
El siguiente código PHP se puede añadir a tu archivo functions.php
del tema hijo, o, la forma recomendada, es usar un plugin como Code Snippets. Esto garantiza que la funcionalidad no se pierda al actualizar el tema.
function duplicate_post_as_draft() {
global $wpdb;
// Verify the nonce for security
$nonce_action = 'duplicate_post_as_draft';
$nonce_name = 'duplicate_nonce';
if (!isset($_GET[$nonce_name]) || !wp_verify_nonce($_GET[$nonce_name], $nonce_action)) {
wp_die(esc_html__('Security check failed.', 'wpturbo'));
}
// Check if the 'post' parameter is set in either GET or POST request
$post_id = filter_input(INPUT_GET, 'post', FILTER_SANITIZE_NUMBER_INT) ?: filter_input(INPUT_POST, 'post', FILTER_SANITIZE_NUMBER_INT);
if (!$post_id) {
wp_die(esc_html__('No post to duplicate has been supplied!', 'wpturbo'));
}
// Check if the post exists
$post = get_post($post_id);
if (!$post) {
wp_die(esc_html(sprintf(__('Post creation failed, could not find original post: %s', 'wpturbo'), $post_id)));
}
$current_user = wp_get_current_user();
$new_post_author = $current_user->ID;
$args = [
"comment_status" => $post->comment_status,
"ping_status" => $post->ping_status,
"post_author" => $new_post_author,
"post_content" => $post->post_content,
"post_excerpt" => $post->post_excerpt,
"post_name" => $post->post_name,
"post_parent" => $post->post_parent,
"post_password" => $post->post_password,
"post_status" => "draft",
"post_title" => $post->post_title . " (Copy)",
"post_type" => $post->post_type,
"to_ping" => $post->to_ping,
"menu_order" => $post->menu_order
];
$new_post_id = wp_insert_post($args);
$taxonomies = get_object_taxonomies($post->post_type);
foreach ($taxonomies as $taxonomy) {
$post_terms = wp_get_object_terms($post_id, $taxonomy, ["fields" => "slugs"]);
wp_set_object_terms($new_post_id, $post_terms, $taxonomy, false);
}
$post_meta_infos = $wpdb->get_results(
$wpdb->prepare("SELECT meta_key, meta_value FROM $wpdb->postmeta WHERE post_id = %d", $post_id)
);
if (count($post_meta_infos) != 0) {
foreach ($post_meta_infos as $meta_info) {
$meta_key = $meta_info->meta_key;
$meta_value = sanitize_meta($meta_info->meta_key, $meta_info->meta_value, "post");
$wpdb->insert($wpdb->postmeta, [
"post_id" => $new_post_id,
"meta_key" => $meta_key,
"meta_value" => $meta_value
]);
}
}
// Redirect to the post list screen and show a success message
$redirect_url = admin_url("edit.php?post_type=" . $post->post_type);
wp_redirect(add_query_arg("message", "101", $redirect_url));
exit();
}
add_action("admin_action_duplicate_post_as_draft", "duplicate_post_as_draft");
function duplicate_post_link($actions, $post) {
if (current_user_can('edit_posts')) {
$actions['duplicate'] = '<a href="' .
wp_nonce_url(
admin_url("admin.php?action=duplicate_post_as_draft&post=" . $post->ID),
'duplicate_post_as_draft',
'duplicate_nonce'
) .
'" title="' . esc_attr__('Duplicar este ítem', 'wpturbo') .
'" rel="permalink">' . esc_html__('Duplicar', 'wpturbo') . '</a>';
}
return $actions;
}
add_filter("post_row_actions", "duplicate_post_link", 10, 2);
add_filter("page_row_actions", "duplicate_post_link", 10, 2);
function apply_duplicate_post_link_to_cpts() {
$post_types = get_post_types(["public" => true], "names");
foreach ($post_types as $post_type) {
add_filter("{$post_type}_row_actions", "duplicate_post_link", 10, 2);
}
}
add_action("admin_init", "apply_duplicate_post_link_to_cpts");
function show_duplicate_admin_notice() {
if (isset($_GET['message']) && $_GET['message'] === '101') {
echo '<div class="notice notice-success is-dismissible"><p>' . esc_html('Post duplicated successfully.') . '</p></div>';
}
}
add_action('admin_notices', 'show_duplicate_admin_notice');
Cómo funciona el código
Este snippet hace varias cosas importantes:
- Función
duplicate_post_as_draft()
:- Seguridad: Incluye una verificación de
nonce
para proteger contra ataques CSRF. - Validación: Comprueba que se haya proporcionado un ID de post válido.
- Creación del nuevo post: Utiliza
wp_insert_post()
para crear una nueva entrada con el contenido, extracto, estado de comentarios, etc., del post original. El nuevo post se crea con el título original más «(Copy)» y en estado de borrador. - Taxonomías y Meta Datos: Copia todas las categorías, etiquetas y otros términos de taxonomía, así como todos los metadatos asociados al post original. Esto es crucial para mantener la integridad del contenido.
- Redirección: Una vez duplicado, el usuario es redirigido a la pantalla de listado de posts, mostrando un mensaje de éxito.
- Seguridad: Incluye una verificación de
- Función
duplicate_post_link()
:- Esta función añade el enlace «Duplicar» a las acciones de fila que aparecen cuando pasas el cursor sobre una entrada o página en el listado del administrador de WordPress.
- Verifica que el usuario tenga los permisos adecuados (
edit_posts
) antes de mostrar el enlace.
- Aplicación a Tipos de Contenido Personalizados (CPTs):
- La función
apply_duplicate_post_link_to_cpts()
se encarga de aplicar el filtro_row_actions
a todos los tipos de contenido público registrados en tu sitio de WordPress. Esto significa que no solo funcionará para posts y páginas, sino también para tus CPTs, lo que lo hace una solución muy versátil.
- La función
- Mensaje de Éxito:
show_duplicate_admin_notice()
muestra una pequeña notificación verde en la parte superior del panel de administración confirmando que la duplicación se realizó correctamente.
Pasos para implementar el código
- Instala el plugin Code Snippets (recomendado): Si aún no lo tienes, búscalo en el directorio de plugins de WordPress e instálalo.
- Crea un nuevo Snippet: Ve a «Snippets» -> «Add New».
- Pega el código: Dale un título descriptivo como «Duplicar Post, Página y CPT» y pega todo el código proporcionado en el área de código.
- Guarda y activa: Asegúrate de que el snippet esté configurado para ejecutarse en el «Frontend y Backend» o «Run Everywhere» y actívalo.
Una vez activado, dirígete a la lista de tus entradas, páginas o cualquier CPT. Al pasar el cursor sobre un elemento, verás la nueva opción «Duplicar». Haz clic en ella, y voilà, tendrás una copia en borrador lista para trabajar.
Consideraciones finales
Este código es una solución simple y efectiva para añadir la funcionalidad de duplicar contenido en WordPress. Es ideal para usuarios que no quieren depender de un plugin completo solo para esta característica, o para desarrolladores que desean una integración más controlada.
Siempre es una buena práctica hacer una copia de seguridad de tu sitio antes de añadir cualquier código personalizado.