{"id":277,"date":"2026-05-13T14:00:33","date_gmt":"2026-05-13T17:00:33","guid":{"rendered":"http:\/\/neodoor.neo\/?p=277"},"modified":"2026-05-21T09:03:31","modified_gmt":"2026-05-21T12:03:31","slug":"duplicate-page","status":"publish","type":"post","link":"https:\/\/www.portanova.com.br\/notebook\/duplicate-page\/","title":{"rendered":"[MU] Duplicate Page"},"content":{"rendered":"<h2>Aula: Duplicar P\u00e1ginas\/Posts com um Clique \u2013 Substituindo o Plugin Duplicate Page<\/h2>\n<p><strong>Objetivo:<\/strong> Substituir o plugin <code>duplicate-page<\/code> por um MU-Plugin leve, que adiciona um link <strong>&#8220;Duplicate&#8221;<\/strong> (ou &#8220;Clonar&#8221;) ao lado de cada post, p\u00e1gina ou custom post type, permitindo duplicar o conte\u00fado com um \u00fanico clique, mantendo metadados, taxonomias e status de rascunho.<\/p>\n<p><strong>Contexto:<\/strong> O plugin original \u00e9 \u00fatil para criar c\u00f3pias r\u00e1pidas sem precisar de a\u00e7\u00f5es em massa. No entanto, \u00e9 mais um plugin externo que pode ser substitu\u00eddo por poucas linhas de c\u00f3digo, ganhando performance e reduzindo depend\u00eancias. Nosso MU-Plugin far\u00e1 exatamente isso: adicionar uma a\u00e7\u00e3o customizada na linha de cada post.<\/p>\n<h3>Funcionalidades implementadas no nosso MU-Plugin<\/h3>\n<ul>\n<li>Link <strong>&#8220;Duplicate&#8221; (Clonar)<\/strong> nos posts, p\u00e1ginas e CPTs configur\u00e1veis.<\/li>\n<li>Redirecionamento autom\u00e1tico para a lista de posts ap\u00f3s a duplica\u00e7\u00e3o, com mensagem de sucesso.<\/li>\n<li>C\u00f3pia completa de todos os metadados (<code>post_meta<\/code>), inclusive os que come\u00e7am com underscore (usados por Elementor, ACF, etc.).<\/li>\n<li>C\u00f3pia de taxonomias (categorias, tags, termos personalizados).<\/li>\n<li>Novo post criado como <strong>rascunho<\/strong>, com t\u00edtulo original acrescido de &#8221; \u2013 C\u00f3pia&#8221;.<\/li>\n<li>Verifica\u00e7\u00e3o de permiss\u00e3o (usu\u00e1rio precisa ter capacidade de editar posts).<\/li>\n<li>Prote\u00e7\u00e3o contra ataques CSRF (nonce).<\/li>\n<li>Notifica\u00e7\u00e3o verde exibida no admin ap\u00f3s a duplica\u00e7\u00e3o.<\/li>\n<\/ul>\n<h3>C\u00f3digo do MU-Plugin<\/h3>\n<p>Crie a pasta <code>wp-content\/mu-plugins\/neodoor-duplicate-page\/<\/code> e dentro dela o arquivo <code>neodoor-duplicate-page.php<\/code> com o conte\u00fado abaixo:<\/p>\n<pre><code>&lt;?php\n\/**\n * Plugin Name:  Neodoor Duplicate Page\n * Description:  Adiciona link \"Duplicate\" (Clonar) na lista de posts, p\u00e1ginas e CPTs para duplicar conte\u00fado rapidamente.\n * Version:      1.0\n * Author:       Reinaldo (Neodoor.neo)\n *\/\n\nif ( ! defined( 'ABSPATH' ) ) {\n    exit;\n}\n\nclass Neodoor_Duplicate_Page {\n\n    private static $instance = null;\n\n    public static function get_instance() {\n        if ( null === self::$instance ) {\n            self::$instance = new self();\n        }\n        return self::$instance;\n    }\n\n    private function __construct() {\n        add_action( 'admin_action_neodoor_duplicate_post', [ $this, 'handle_duplicate_action' ] );\n        add_filter( 'post_row_actions', [ $this, 'add_duplicate_link' ], 10, 2 );\n        add_filter( 'page_row_actions', [ $this, 'add_duplicate_link' ], 10, 2 );\n        add_action( 'admin_notices', [ $this, 'show_duplicate_notice' ] );\n        \/\/ Para CPTs, o mesmo filtro funciona (post_row_actions cobre todos os tipos)\n    }\n\n    \/**\n     * Adiciona o link \"Duplicate\" na coluna de a\u00e7\u00f5es da lista administrativa\n     *\/\n    public function add_duplicate_link( $actions, $post ) {\n        \/\/ Verifica se o usu\u00e1rio tem permiss\u00e3o para editar posts\n        if ( ! current_user_can( 'edit_posts' ) ) {\n            return $actions;\n        }\n\n        \/\/ Gera URL de duplica\u00e7\u00e3o com nonce para seguran\u00e7a\n        $url = wp_nonce_url(\n            admin_url( 'admin.php?action=neodoor_duplicate_post&amp;post=' . $post-&gt;ID ),\n            'neodoor_duplicate_post_' . $post-&gt;ID,\n            'neodoor_nonce'\n        );\n\n        $actions['neodoor_duplicate'] = '&lt;a href=\"' . esc_url( $url ) . '\" title=\"Clonar este item\"&gt;Duplicar&lt;\/a&gt;';\n        return $actions;\n    }\n\n    \/**\n     * Processa a a\u00e7\u00e3o de duplica\u00e7\u00e3o\n     *\/\n    public function handle_duplicate_action() {\n        \/\/ Verifica se o post ID foi informado\n        if ( empty( $_GET['post'] ) ) {\n            wp_die( 'ID do post n\u00e3o informado.' );\n        }\n\n        $post_id = intval( $_GET['post'] );\n        $nonce   = isset( $_GET['neodoor_nonce'] ) ? sanitize_text_field( $_GET['neodoor_nonce'] ) : '';\n\n        \/\/ Verifica nonce\n        if ( ! wp_verify_nonce( $nonce, 'neodoor_duplicate_post_' . $post_id ) ) {\n            wp_die( 'Erro de seguran\u00e7a: nonce inv\u00e1lido. Recarregue a p\u00e1gina e tente novamente.' );\n        }\n\n        \/\/ Verifica permiss\u00e3o\n        if ( ! current_user_can( 'edit_posts' ) ) {\n            wp_die( 'Voc\u00ea n\u00e3o tem permiss\u00e3o para duplicar conte\u00fados.' );\n        }\n\n        \/\/ Executa a duplica\u00e7\u00e3o\n        $new_post_id = $this-&gt;duplicate_post( $post_id );\n        if ( ! $new_post_id || is_wp_error( $new_post_id ) ) {\n            wp_die( 'Erro ao duplicar o post. Verifique o log de erros.' );\n        }\n\n        \/\/ Redireciona de volta para a lista de posts do tipo correspondente, com mensagem de sucesso\n        $post_type = get_post_type( $post_id );\n        $redirect_url = add_query_arg(\n            [ 'post_type' =&gt; $post_type, 'neodoor_duplicated' =&gt; $new_post_id ],\n            admin_url( 'edit.php' )\n        );\n        wp_safe_redirect( $redirect_url );\n        exit;\n    }\n\n    \/**\n     * Fun\u00e7\u00e3o principal de duplica\u00e7\u00e3o\n     * @param int $post_id ID do post original\n     * @return int|false ID do novo post ou false em caso de erro\n     *\/\n    private function duplicate_post( $post_id ) {\n        $post = get_post( $post_id );\n        if ( ! $post ) {\n            return false;\n        }\n\n        \/\/ Prepara o array para o novo post (como rascunho)\n        $new_post = [\n            'post_title'   =&gt; $post-&gt;post_title . ' \u2013 C\u00f3pia',\n            'post_content' =&gt; $post-&gt;post_content,\n            'post_excerpt' =&gt; $post-&gt;post_excerpt,\n            'post_status'  =&gt; 'draft',\n            'post_type'    =&gt; $post-&gt;post_type,\n            'post_author'  =&gt; get_current_user_id(),\n        ];\n\n        \/\/ Insere o novo post\n        $new_post_id = wp_insert_post( $new_post, true );\n        if ( is_wp_error( $new_post_id ) ) {\n            return false;\n        }\n\n        \/\/ Copia metadados (post_meta) \u2013 inclusive os com underscore\n        global $wpdb;\n        $meta_data = $wpdb-&gt;get_results( $wpdb-&gt;prepare(\n            \"SELECT meta_key, meta_value FROM {$wpdb-&gt;postmeta} WHERE post_id = %d\",\n            $post_id\n        ) );\n        foreach ( $meta_data as $meta ) {\n            add_post_meta( $new_post_id, $meta-&gt;meta_key, maybe_unserialize( $meta-&gt;meta_value ) );\n        }\n\n        \/\/ Copia taxonomias (categorias, tags, etc.)\n        $taxonomies = get_post_taxonomies( $post_id );\n        foreach ( $taxonomies as $taxonomy ) {\n            $terms = wp_get_object_terms( $post_id, $taxonomy, [ 'fields' =&gt; 'ids' ] );\n            if ( ! is_wp_error( $terms ) &amp;&amp; ! empty( $terms ) ) {\n                wp_set_object_terms( $new_post_id, $terms, $taxonomy );\n            }\n        }\n\n        return $new_post_id;\n    }\n\n    \/**\n     * Exibe notifica\u00e7\u00e3o de sucesso ap\u00f3s duplica\u00e7\u00e3o\n     *\/\n    public function show_duplicate_notice() {\n        if ( isset( $_GET['neodoor_duplicated'] ) ) {\n            $post_id = intval( $_GET['neodoor_duplicated'] );\n            $post_title = get_the_title( $post_id );\n            echo '&lt;div class=\"notice notice-success is-dismissible\"&gt;&lt;p&gt;' .\n                 esc_html( sprintf( 'Item duplicado com sucesso: \"%s\" (rascunho).', $post_title ) ) .\n                 '&lt;\/p&gt;&lt;\/div&gt;';\n        }\n    }\n}\n\nNeodoor_Duplicate_Page::get_instance();\n<\/code><\/pre>\n<h3>Como testar na pr\u00e1tica<\/h3>\n<ol>\n<li>Ap\u00f3s criar o MU-Plugin, acesse <strong>Posts \u2192 Todos os posts<\/strong> ou <strong>P\u00e1ginas \u2192 Todas as p\u00e1ginas<\/strong>.<\/li>\n<li>Passe o mouse sobre qualquer t\u00edtulo de post\/p\u00e1gina.<\/li>\n<li>Voc\u00ea ver\u00e1 o link <strong>&#8220;Duplicar&#8221;<\/strong> ao lado de Editar, Lixeira, Visualizar, etc.<\/li>\n<li>Clique em <strong>&#8220;Duplicar&#8221;<\/strong>.<\/li>\n<li>Uma nova p\u00e1gina ser\u00e1 recarregada e voc\u00ea ver\u00e1 uma notifica\u00e7\u00e3o verde: <em>&#8220;Item duplicado com sucesso: &#8216;T\u00edtulo do Post \u2013 C\u00f3pia&#8217; (rascunho)&#8221;<\/em>.<\/li>\n<li>O novo post aparecer\u00e1 na lista com status <strong>Rascunho<\/strong> e t\u00edtulo original acrescido de &#8221; \u2013 C\u00f3pia&#8221;.<\/li>\n<\/ol>\n<h4>Exemplo visual (antes\/depois)<\/h4>\n<pre><code>Lista de posts (antes):\n+------------------------------------------------+\n| T\u00edtulo Exemplo          | Editar | Lixeira   |\n+------------------------------------------------+\n\nAp\u00f3s passar o mouse:\n+------------------------------------------------+\n| T\u00edtulo Exemplo          | Editar | Duplicar | Lixeira |\n+------------------------------------------------+\n\nClique em Duplicar \u2192 notifica\u00e7\u00e3o verde:\n\u2705 Item duplicado com sucesso: \"T\u00edtulo Exemplo \u2013 C\u00f3pia\" (rascunho).\n<\/code><\/pre>\n<h3>Suporte para Custom Post Types (CPTs)<\/h3>\n<p>O filtro <code>post_row_actions<\/code> funciona automaticamente para todos os tipos de post que usam a lista padr\u00e3o do WordPress (incluindo CPTs como <code>produto<\/code>, <code>aula<\/code>, etc.). Nenhuma configura\u00e7\u00e3o extra \u00e9 necess\u00e1ria. Se quiser restringir a apenas alguns tipos, adicione uma verifica\u00e7\u00e3o no m\u00e9todo <code>add_duplicate_link<\/code>:<\/p>\n<pre><code>$allowed_types = [ 'post', 'page', 'meu_cpt' ];\nif ( ! in_array( $post-&gt;post_type, $allowed_types ) ) {\n    return $actions;\n}\n<\/code><\/pre>\n<h3>Personaliza\u00e7\u00f5es poss\u00edveis<\/h3>\n<ul>\n<li><strong>Alterar o texto do link:<\/strong> Mude <code>'Duplicar'<\/code> no array de actions para o texto desejado (ex: &#8220;Clonar&#8221;, &#8220;Copiar&#8221;).<\/li>\n<li><strong>Alterar o sufixo do t\u00edtulo:<\/strong> Na linha <code>'post_title'   =&gt; $post-&gt;post_title . ' \u2013 C\u00f3pia'<\/code>, modifique o sufixo.<\/li>\n<li><strong>Copiar como publicado:<\/strong> Troque <code>'post_status'  =&gt; 'draft'<\/code> por <code>'publish'<\/code>. Cuidado: isso tornar\u00e1 a c\u00f3pia vis\u00edvel imediatamente.<\/li>\n<li><strong>Adicionar confirma\u00e7\u00e3o antes de duplicar:<\/strong> Inclua um pequeno trecho JavaScript no admin:<\/li>\n<\/ul>\n<pre><code>add_action( 'admin_footer', function() {\n    ?&gt;\n    &lt;script&gt;\n    jQuery(document).ready(function($) {\n        $('a[href*=\"action=neodoor_duplicate_post\"]').click(function(e) {\n            if ( ! confirm('Tem certeza que deseja duplicar este item?') ) {\n                e.preventDefault();\n            }\n        });\n    });\n    &lt;\/script&gt;\n    &lt;?php\n} );\n<\/code><\/pre>\n<h3>Compara\u00e7\u00e3o com o plugin original<\/h3>\n<ul>\n<li><strong>Sem configura\u00e7\u00f5es desnecess\u00e1rias:<\/strong> O plugin original tem telas de op\u00e7\u00f5es, vers\u00e3o pro, an\u00fancios. Nosso MU-Plugin \u00e9 direto ao ponto.<\/li>\n<li><strong>Leve e r\u00e1pido:<\/strong> Cerca de 120 linhas de c\u00f3digo, sem CSS ou JS extras.<\/li>\n<li><strong>Sem rastreadores ou chamadas externas:<\/strong> C\u00f3digo 100% limpo, sem conex\u00f5es a terceiros.<\/li>\n<li><strong>Totalmente compat\u00edvel com Elementor, ACF, Yoast e outros:<\/strong> Copia todos os metadados, incluindo os que come\u00e7am com underscore.<\/li>\n<\/ul>\n<h3>Observa\u00e7\u00f5es finais<\/h3>\n<ul>\n<li><strong>Permiss\u00f5es:<\/strong> O link s\u00f3 aparece para usu\u00e1rios que podem editar posts (normalmente administradores, editores e autores).<\/li>\n<li><strong>Seguran\u00e7a:<\/strong> O nonce garante que a a\u00e7\u00e3o s\u00f3 possa ser executada uma vez por requisi\u00e7\u00e3o leg\u00edtima.<\/li>\n<li><strong>Performance:<\/strong> Duplicar um post com muitos metadados (ex: p\u00e1ginas do Elementor) pode levar alguns segundos, mas funciona bem.<\/li>\n<li><strong>Redirecionamento inteligente:<\/strong> Ap\u00f3s duplicar, voc\u00ea \u00e9 levado de volta \u00e0 lista de posts do mesmo tipo (post, p\u00e1gina, CPT), mantendo o contexto.<\/li>\n<\/ul>\n<h3>Poss\u00edveis problemas e solu\u00e7\u00f5es<\/h3>\n<ul>\n<li><strong>O link &#8220;Duplicar&#8221; n\u00e3o aparece:<\/strong> Verifique se voc\u00ea est\u00e1 logado com um papel que tenha permiss\u00e3o <code>edit_posts<\/code> (administrador ou editor).<\/li>\n<li><strong>Erro de nonce inv\u00e1lido:<\/strong> Tente recarregar a p\u00e1gina de listagem e clique novamente. Nonces expiram ap\u00f3s 12-24 horas.<\/li>\n<li><strong>N\u00e3o copia imagens destacadas:<\/strong> O c\u00f3digo copia todos os metadados, incluindo <code>_thumbnail_id<\/code>, portanto a imagem destacada \u00e9 preservada. Se n\u00e3o estiver funcionando, verifique se o campo <code>_thumbnail_id<\/code> foi copiado corretamente.<\/li>\n<\/ul>\n<h3>Registro no caderno<\/h3>\n<p>Data: <!--?php echo date('d\/m\/Y'); ?--><\/p>\n<p>Funcionalidade: Duplica\u00e7\u00e3o com um clique na lista administrativa<\/p>\n<p>Plugins substitu\u00eddos: duplicate-page (tamb\u00e9m conhecido como &#8220;Duplicate Page&#8221; de mndpsingh287)<\/p>\n<p>Tempo de implementa\u00e7\u00e3o: ~10 minutos (cria\u00e7\u00e3o do arquivo e testes)<\/p>\n<p>Observa\u00e7\u00e3o: Para remover o link de tipos espec\u00edficos, utilize a verifica\u00e7\u00e3o com <code>in_array<\/code> conforme exemplo acima.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Objetivo: Substituir o plugin duplicate-page por um MU-Plugin leve, que adiciona um link &#8220;Duplicate&#8221; (ou &#8220;Clonar&#8221;) ao lado de cada post, p\u00e1gina ou custom post type, permitindo duplicar o conte\u00fado com um \u00fanico clique, mantendo metadados, taxonomias e status de rascunho.<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[2,4],"tags":[],"class_list":["post-277","post","type-post","status-publish","format-standard","hentry","category-lessons","category-pluginsmu"],"_links":{"self":[{"href":"https:\/\/www.portanova.com.br\/notebook\/wp-json\/wp\/v2\/posts\/277","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.portanova.com.br\/notebook\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.portanova.com.br\/notebook\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.portanova.com.br\/notebook\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.portanova.com.br\/notebook\/wp-json\/wp\/v2\/comments?post=277"}],"version-history":[{"count":0,"href":"https:\/\/www.portanova.com.br\/notebook\/wp-json\/wp\/v2\/posts\/277\/revisions"}],"wp:attachment":[{"href":"https:\/\/www.portanova.com.br\/notebook\/wp-json\/wp\/v2\/media?parent=277"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.portanova.com.br\/notebook\/wp-json\/wp\/v2\/categories?post=277"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.portanova.com.br\/notebook\/wp-json\/wp\/v2\/tags?post=277"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}