Aula: Duplicar Páginas/Posts com um Clique – Substituindo o Plugin Duplicate Page
Objetivo: Substituir o plugin duplicate-page por um MU-Plugin leve, que adiciona um link “Duplicate” (ou “Clonar”) ao lado de cada post, página ou custom post type, permitindo duplicar o conteúdo com um único clique, mantendo metadados, taxonomias e status de rascunho.
Contexto: O plugin original é útil para criar cópias rápidas sem precisar de ações em massa. No entanto, é mais um plugin externo que pode ser substituído por poucas linhas de código, ganhando performance e reduzindo dependências. Nosso MU-Plugin fará exatamente isso: adicionar uma ação customizada na linha de cada post.
Funcionalidades implementadas no nosso MU-Plugin
- Link “Duplicate” (Clonar) nos posts, páginas e CPTs configuráveis.
- Redirecionamento automático para a lista de posts após a duplicação, com mensagem de sucesso.
- Cópia completa de todos os metadados (
post_meta), inclusive os que começam com underscore (usados por Elementor, ACF, etc.). - Cópia de taxonomias (categorias, tags, termos personalizados).
- Novo post criado como rascunho, com título original acrescido de ” – Cópia”.
- Verificação de permissão (usuário precisa ter capacidade de editar posts).
- Proteção contra ataques CSRF (nonce).
- Notificação verde exibida no admin após a duplicação.
Código do MU-Plugin
Crie a pasta wp-content/mu-plugins/neodoor-duplicate-page/ e dentro dela o arquivo neodoor-duplicate-page.php com o conteúdo abaixo:
<?php
/**
* Plugin Name: Neodoor Duplicate Page
* Description: Adiciona link "Duplicate" (Clonar) na lista de posts, páginas e CPTs para duplicar conteúdo rapidamente.
* Version: 1.0
* Author: Reinaldo (Neodoor.neo)
*/
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
class Neodoor_Duplicate_Page {
private static $instance = null;
public static function get_instance() {
if ( null === self::$instance ) {
self::$instance = new self();
}
return self::$instance;
}
private function __construct() {
add_action( 'admin_action_neodoor_duplicate_post', [ $this, 'handle_duplicate_action' ] );
add_filter( 'post_row_actions', [ $this, 'add_duplicate_link' ], 10, 2 );
add_filter( 'page_row_actions', [ $this, 'add_duplicate_link' ], 10, 2 );
add_action( 'admin_notices', [ $this, 'show_duplicate_notice' ] );
// Para CPTs, o mesmo filtro funciona (post_row_actions cobre todos os tipos)
}
/**
* Adiciona o link "Duplicate" na coluna de ações da lista administrativa
*/
public function add_duplicate_link( $actions, $post ) {
// Verifica se o usuário tem permissão para editar posts
if ( ! current_user_can( 'edit_posts' ) ) {
return $actions;
}
// Gera URL de duplicação com nonce para segurança
$url = wp_nonce_url(
admin_url( 'admin.php?action=neodoor_duplicate_post&post=' . $post->ID ),
'neodoor_duplicate_post_' . $post->ID,
'neodoor_nonce'
);
$actions['neodoor_duplicate'] = '<a href="' . esc_url( $url ) . '" title="Clonar este item">Duplicar</a>';
return $actions;
}
/**
* Processa a ação de duplicação
*/
public function handle_duplicate_action() {
// Verifica se o post ID foi informado
if ( empty( $_GET['post'] ) ) {
wp_die( 'ID do post não informado.' );
}
$post_id = intval( $_GET['post'] );
$nonce = isset( $_GET['neodoor_nonce'] ) ? sanitize_text_field( $_GET['neodoor_nonce'] ) : '';
// Verifica nonce
if ( ! wp_verify_nonce( $nonce, 'neodoor_duplicate_post_' . $post_id ) ) {
wp_die( 'Erro de segurança: nonce inválido. Recarregue a página e tente novamente.' );
}
// Verifica permissão
if ( ! current_user_can( 'edit_posts' ) ) {
wp_die( 'Você não tem permissão para duplicar conteúdos.' );
}
// Executa a duplicação
$new_post_id = $this->duplicate_post( $post_id );
if ( ! $new_post_id || is_wp_error( $new_post_id ) ) {
wp_die( 'Erro ao duplicar o post. Verifique o log de erros.' );
}
// Redireciona de volta para a lista de posts do tipo correspondente, com mensagem de sucesso
$post_type = get_post_type( $post_id );
$redirect_url = add_query_arg(
[ 'post_type' => $post_type, 'neodoor_duplicated' => $new_post_id ],
admin_url( 'edit.php' )
);
wp_safe_redirect( $redirect_url );
exit;
}
/**
* Função principal de duplicação
* @param int $post_id ID do post original
* @return int|false ID do novo post ou false em caso de erro
*/
private function duplicate_post( $post_id ) {
$post = get_post( $post_id );
if ( ! $post ) {
return false;
}
// Prepara o array para o novo post (como rascunho)
$new_post = [
'post_title' => $post->post_title . ' – Cópia',
'post_content' => $post->post_content,
'post_excerpt' => $post->post_excerpt,
'post_status' => 'draft',
'post_type' => $post->post_type,
'post_author' => get_current_user_id(),
];
// Insere o novo post
$new_post_id = wp_insert_post( $new_post, true );
if ( is_wp_error( $new_post_id ) ) {
return false;
}
// Copia metadados (post_meta) – inclusive os com underscore
global $wpdb;
$meta_data = $wpdb->get_results( $wpdb->prepare(
"SELECT meta_key, meta_value FROM {$wpdb->postmeta} WHERE post_id = %d",
$post_id
) );
foreach ( $meta_data as $meta ) {
add_post_meta( $new_post_id, $meta->meta_key, maybe_unserialize( $meta->meta_value ) );
}
// Copia taxonomias (categorias, tags, etc.)
$taxonomies = get_post_taxonomies( $post_id );
foreach ( $taxonomies as $taxonomy ) {
$terms = wp_get_object_terms( $post_id, $taxonomy, [ 'fields' => 'ids' ] );
if ( ! is_wp_error( $terms ) && ! empty( $terms ) ) {
wp_set_object_terms( $new_post_id, $terms, $taxonomy );
}
}
return $new_post_id;
}
/**
* Exibe notificação de sucesso após duplicação
*/
public function show_duplicate_notice() {
if ( isset( $_GET['neodoor_duplicated'] ) ) {
$post_id = intval( $_GET['neodoor_duplicated'] );
$post_title = get_the_title( $post_id );
echo '<div class="notice notice-success is-dismissible"><p>' .
esc_html( sprintf( 'Item duplicado com sucesso: "%s" (rascunho).', $post_title ) ) .
'</p></div>';
}
}
}
Neodoor_Duplicate_Page::get_instance();
Como testar na prática
- Após criar o MU-Plugin, acesse Posts → Todos os posts ou Páginas → Todas as páginas.
- Passe o mouse sobre qualquer título de post/página.
- Você verá o link “Duplicar” ao lado de Editar, Lixeira, Visualizar, etc.
- Clique em “Duplicar”.
- Uma nova página será recarregada e você verá uma notificação verde: “Item duplicado com sucesso: ‘Título do Post – Cópia’ (rascunho)”.
- O novo post aparecerá na lista com status Rascunho e título original acrescido de ” – Cópia”.
Exemplo visual (antes/depois)
Lista de posts (antes):
+------------------------------------------------+
| Título Exemplo | Editar | Lixeira |
+------------------------------------------------+
Após passar o mouse:
+------------------------------------------------+
| Título Exemplo | Editar | Duplicar | Lixeira |
+------------------------------------------------+
Clique em Duplicar → notificação verde:
✅ Item duplicado com sucesso: "Título Exemplo – Cópia" (rascunho).
Suporte para Custom Post Types (CPTs)
O filtro post_row_actions funciona automaticamente para todos os tipos de post que usam a lista padrão do WordPress (incluindo CPTs como produto, aula, etc.). Nenhuma configuração extra é necessária. Se quiser restringir a apenas alguns tipos, adicione uma verificação no método add_duplicate_link:
$allowed_types = [ 'post', 'page', 'meu_cpt' ];
if ( ! in_array( $post->post_type, $allowed_types ) ) {
return $actions;
}
Personalizações possíveis
- Alterar o texto do link: Mude
'Duplicar'no array de actions para o texto desejado (ex: “Clonar”, “Copiar”). - Alterar o sufixo do título: Na linha
'post_title' => $post->post_title . ' – Cópia', modifique o sufixo. - Copiar como publicado: Troque
'post_status' => 'draft'por'publish'. Cuidado: isso tornará a cópia visível imediatamente. - Adicionar confirmação antes de duplicar: Inclua um pequeno trecho JavaScript no admin:
add_action( 'admin_footer', function() {
?>
<script>
jQuery(document).ready(function($) {
$('a[href*="action=neodoor_duplicate_post"]').click(function(e) {
if ( ! confirm('Tem certeza que deseja duplicar este item?') ) {
e.preventDefault();
}
});
});
</script>
<?php
} );
Comparação com o plugin original
- Sem configurações desnecessárias: O plugin original tem telas de opções, versão pro, anúncios. Nosso MU-Plugin é direto ao ponto.
- Leve e rápido: Cerca de 120 linhas de código, sem CSS ou JS extras.
- Sem rastreadores ou chamadas externas: Código 100% limpo, sem conexões a terceiros.
- Totalmente compatível com Elementor, ACF, Yoast e outros: Copia todos os metadados, incluindo os que começam com underscore.
Observações finais
- Permissões: O link só aparece para usuários que podem editar posts (normalmente administradores, editores e autores).
- Segurança: O nonce garante que a ação só possa ser executada uma vez por requisição legítima.
- Performance: Duplicar um post com muitos metadados (ex: páginas do Elementor) pode levar alguns segundos, mas funciona bem.
- Redirecionamento inteligente: Após duplicar, você é levado de volta à lista de posts do mesmo tipo (post, página, CPT), mantendo o contexto.
Possíveis problemas e soluções
- O link “Duplicar” não aparece: Verifique se você está logado com um papel que tenha permissão
edit_posts(administrador ou editor). - Erro de nonce inválido: Tente recarregar a página de listagem e clique novamente. Nonces expiram após 12-24 horas.
- Não copia imagens destacadas: O código copia todos os metadados, incluindo
_thumbnail_id, portanto a imagem destacada é preservada. Se não estiver funcionando, verifique se o campo_thumbnail_idfoi copiado corretamente.
Registro no caderno
Data:
Funcionalidade: Duplicação com um clique na lista administrativa
Plugins substituídos: duplicate-page (também conhecido como “Duplicate Page” de mndpsingh287)
Tempo de implementação: ~10 minutos (criação do arquivo e testes)
Observação: Para remover o link de tipos específicos, utilize a verificação com in_array conforme exemplo acima.