{"id":284,"date":"2026-05-13T15:00:15","date_gmt":"2026-05-13T18:00:15","guid":{"rendered":"http:\/\/neodoor.neo\/?p=284"},"modified":"2026-05-21T09:03:42","modified_gmt":"2026-05-21T12:03:42","slug":"advanced-export-for-wp-wpmu","status":"publish","type":"post","link":"https:\/\/www.portanova.com.br\/notebook\/advanced-export-for-wp-wpmu\/","title":{"rendered":"[MU] Advanced Export for WP WPMU"},"content":{"rendered":"<h2>Aula: Exporta\u00e7\u00e3o Avan\u00e7ada para CSV com MU-Plugin \u2013 Substituindo o Advanced Export for WP WPMU<\/h2>\n<p><strong>Objetivo:<\/strong> Substituir o plugin <code>advanced-export-for-wp-wpmu<\/code> por um MU-Plugin enxuto que permite exportar posts, p\u00e1ginas e custom post types para um arquivo CSV com os campos selecion\u00e1veis, diretamente da tela de administra\u00e7\u00e3o.<\/p>\n<p><strong>Contexto:<\/strong> O plugin original oferece exporta\u00e7\u00e3o flex\u00edvel para CSV\/XML, mas \u00e9 desnecessariamente complexo para necessidades b\u00e1sicas. Vamos criar uma solu\u00e7\u00e3o que adiciona uma nova a\u00e7\u00e3o em massa &#8220;Export to CSV&#8221; na lista de posts, permitindo baixar um arquivo CSV com os dados dos posts selecionados, com suporte a metadados e taxonomias.<\/p>\n<h3>Funcionalidades implementadas no nosso MU-Plugin<\/h3>\n<ul>\n<li>A\u00e7\u00e3o em massa <strong>&#8220;Export to CSV&#8221;<\/strong> na lista de posts, p\u00e1ginas e CPTs.<\/li>\n<li>Exporta\u00e7\u00e3o dos campos padr\u00e3o: ID, T\u00edtulo, Slug, Data, Autor, Status, Tipo, Link.<\/li>\n<li>Inclus\u00e3o opcional de metadados (campos personalizados) \u2013 configur\u00e1vel via array.<\/li>\n<li>Inclus\u00e3o opcional de taxonomias (categorias, tags, etc.) \u2013 configur\u00e1vel.<\/li>\n<li>Download autom\u00e1tico do arquivo CSV com nome no padr\u00e3o <code>export-posts-YYYY-MM-DD.csv<\/code>.<\/li>\n<li>Delimitador CSV padr\u00e3o (ponto e v\u00edrgula) para compatibilidade com Excel PT-BR.<\/li>\n<li>Codifica\u00e7\u00e3o UTF-8 com BOM para acentos corretos.<\/li>\n<li>Verifica\u00e7\u00e3o de permiss\u00e3o (usu\u00e1rio precisa ter capacidade de exportar).<\/li>\n<li>Tratamento seguro de dados (escapamento para CSV).<\/li>\n<\/ul>\n<h3>C\u00f3digo do MU-Plugin<\/h3>\n<p>Crie a pasta <code>wp-content\/mu-plugins\/neodoor-advanced-export\/<\/code> e dentro dela o arquivo <code>neodoor-advanced-export.php<\/code> com o conte\u00fado abaixo:<\/p>\n<pre><code>&lt;?php\n\/**\n * Plugin Name:  Neodoor Advanced Export\n * Description:  Adiciona a\u00e7\u00e3o em massa \"Export to CSV\" na lista de posts para exportar dados selecionados.\n * Version:      1.0\n * Author:       Reinaldo (Neodoor.neo)\n *\/\n\nif ( ! defined( 'ABSPATH' ) ) {\n    exit;\n}\n\nclass Neodoor_Advanced_Export {\n\n    private static $instance = null;\n\n    \/\/ Configura\u00e7\u00e3o dos campos a serem exportados\n    private $export_fields = [\n        'ID'         =&gt; 'ID',\n        'post_title' =&gt; 'T\u00edtulo',\n        'post_name'  =&gt; 'Slug',\n        'post_date'  =&gt; 'Data',\n        'post_author'=&gt; 'Autor',\n        'post_status'=&gt; 'Status',\n        'post_type'  =&gt; 'Tipo',\n        'guid'       =&gt; 'Link',\n    ];\n\n    \/\/ Metadados adicionais a incluir (meta_key =&gt; r\u00f3tulo)\n    private $meta_fields = [\n        \/\/ '_yoast_wpseo_title' =&gt; 'SEO Title',  \/\/ Exemplo descomente se usar Yoast\n        \/\/ '_thumbnail_id'      =&gt; 'Imagem ID',   \/\/ ID da imagem destacada\n    ];\n\n    \/\/ Taxonomias adicionais a incluir (taxonomia =&gt; r\u00f3tulo)\n    private $taxonomy_fields = [\n        'category' =&gt; 'Categorias',\n        'post_tag' =&gt; 'Tags',\n    ];\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_filter( 'bulk_actions-edit-post', [ $this, 'add_bulk_export_action' ] );\n        add_filter( 'bulk_actions-edit-page', [ $this, 'add_bulk_export_action' ] );\n        add_filter( 'handle_bulk_actions-edit-post', [ $this, 'handle_bulk_export' ], 10, 3 );\n        add_filter( 'handle_bulk_actions-edit-page', [ $this, 'handle_bulk_export' ], 10, 3 );\n        \/\/ Para CPTs din\u00e2micos \u2013 dispara para todos os tipos que usam edit.php\n        add_filter( 'bulk_actions-edit-product', [ $this, 'add_bulk_export_action' ] );\n        add_filter( 'handle_bulk_actions-edit-product', [ $this, 'handle_bulk_export' ], 10, 3 );\n    }\n\n    \/**\n     * Adiciona a a\u00e7\u00e3o \"Export to CSV\" no dropdown de a\u00e7\u00f5es em massa\n     *\/\n    public function add_bulk_export_action( $bulk_actions ) {\n        $bulk_actions['neodoor_csv_export'] = __( 'Export to CSV', 'neodoor-export' );\n        return $bulk_actions;\n    }\n\n    \/**\n     * Processa a exporta\u00e7\u00e3o quando a a\u00e7\u00e3o \u00e9 acionada\n     *\/\n    public function handle_bulk_export( $redirect_to, $doaction, $post_ids ) {\n        if ( $doaction !== 'neodoor_csv_export' ) {\n            return $redirect_to;\n        }\n\n        \/\/ Verifica permiss\u00e3o (apenas quem pode editar pode exportar)\n        if ( ! current_user_can( 'edit_posts' ) ) {\n            wp_die( 'Voc\u00ea n\u00e3o tem permiss\u00e3o para exportar conte\u00fado.' );\n        }\n\n        \/\/ Verifica nonce para seguran\u00e7a (o mesmo usado em bulk actions)\n        check_admin_referer( 'bulk-posts' );\n\n        \/\/ Prepara os dados para o CSV\n        $csv_data = [];\n        $headers = [];\n\n        \/\/ Monta cabe\u00e7alhos a partir dos campos configurados\n        foreach ( $this-&gt;export_fields as $field =&gt; $label ) {\n            $headers[] = $label;\n        }\n        foreach ( $this-&gt;meta_fields as $meta_key =&gt; $label ) {\n            $headers[] = $label;\n        }\n        foreach ( $this-&gt;taxonomy_fields as $tax =&gt; $label ) {\n            $headers[] = $label;\n        }\n\n        $csv_data[] = $headers;\n\n        \/\/ Processa cada post selecionado\n        foreach ( $post_ids as $post_id ) {\n            $post = get_post( $post_id );\n            if ( ! $post ) {\n                continue;\n            }\n\n            $row = [];\n\n            \/\/ Campos padr\u00e3o\n            foreach ( $this-&gt;export_fields as $field =&gt; $label ) {\n                $value = '';\n                switch ( $field ) {\n                    case 'ID':\n                        $value = $post-&gt;ID;\n                        break;\n                    case 'post_author':\n                        $author = get_userdata( $post-&gt;post_author );\n                        $value = $author ? $author-&gt;display_name : $post-&gt;post_author;\n                        break;\n                    default:\n                        $value = $post-&gt;$field ?? '';\n                }\n                $row[] = $value;\n            }\n\n            \/\/ Metadados\n            foreach ( $this-&gt;meta_fields as $meta_key =&gt; $label ) {\n                $meta_value = get_post_meta( $post-&gt;ID, $meta_key, true );\n                if ( is_array( $meta_value ) ) {\n                    $meta_value = maybe_serialize( $meta_value );\n                }\n                $row[] = $meta_value;\n            }\n\n            \/\/ Taxonomias\n            foreach ( $this-&gt;taxonomy_fields as $tax =&gt; $label ) {\n                $terms = wp_get_post_terms( $post-&gt;ID, $tax, [ 'fields' =&gt; 'names' ] );\n                $value = is_wp_error( $terms ) ? '' : implode( ', ', $terms );\n                $row[] = $value;\n            }\n\n            $csv_data[] = $row;\n        }\n\n        \/\/ Gera o arquivo CSV para download\n        $this-&gt;send_csv_download( $csv_data );\n\n        \/\/ Ap\u00f3s enviar o arquivo, o script termina (exit dentro da fun\u00e7\u00e3o)\n        return $redirect_to;\n    }\n\n    \/**\n     * Envia o arquivo CSV para download\n     * @param array $data Dados formatados (headers + linhas)\n     *\/\n    private function send_csv_download( $data ) {\n        \/\/ Nome do arquivo com data atual\n        $filename = 'export-posts-' . date( 'Y-m-d' ) . '.csv';\n\n        \/\/ Cabe\u00e7alhos HTTP para for\u00e7ar download\n        header( 'Content-Type: text\/csv; charset=utf-8' );\n        header( 'Content-Disposition: attachment; filename=\"' . $filename . '\"' );\n        header( 'Pragma: no-cache' );\n        header( 'Expires: 0' );\n\n        \/\/ Abre a sa\u00edda diretamente\n        $output = fopen( 'php:\/\/output', 'w' );\n\n        \/\/ Adiciona BOM para UTF-8 (acentos no Excel)\n        fprintf( $output, chr( 0xEF ) . chr( 0xBB ) . chr( 0xBF ) );\n\n        \/\/ Escreve cada linha usando ponto e v\u00edrgula como delimitador (padr\u00e3o PT-BR)\n        foreach ( $data as $row ) {\n            fputcsv( $output, $row, ';', '\"' );\n        }\n\n        fclose( $output );\n        exit; \/\/ Encerra a execu\u00e7\u00e3o para n\u00e3o carregar o WordPress novamente\n    }\n}\n\nNeodoor_Advanced_Export::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>Selecione um ou mais posts usando as caixas de sele\u00e7\u00e3o.<\/li>\n<li>No dropdown <strong>&#8220;A\u00e7\u00f5es em massa&#8221;<\/strong>, escolha <strong>&#8220;Export to CSV&#8221;<\/strong>.<\/li>\n<li>Clique em <strong>&#8220;Aplicar&#8221;<\/strong>.<\/li>\n<li>O navegador iniciar\u00e1 o download de um arquivo chamado <code>export-posts-YYYY-MM-DD.csv<\/code>.<\/li>\n<li>Abra o arquivo no Excel, LibreOffice ou editor de texto para ver os dados exportados.<\/li>\n<\/ol>\n<h4>Exemplo visual do CSV gerado (Excel PT-BR)<\/h4>\n<pre><code>ID;T\u00edtulo;Slug;Data;Autor;Status;Tipo;Link;Categorias;Tags\n1;Ol\u00e1 mundo;ola-mundo;2024-01-01 12:00:00;admin;publish;post;https:\/\/...;Sem categoria;destaque\n3;P\u00e1gina Exemplo;pagina-exemplo;2024-01-02 10:00:00;admin;publish;page;https:\/\/...;;\n<\/code><\/pre>\n<h3>Configura\u00e7\u00e3o dos campos exportados<\/h3>\n<p>Voc\u00ea pode personalizar os campos editando as propriedades da classe:<\/p>\n<ul>\n<li><strong><code>$export_fields<\/code><\/strong>: Campos padr\u00e3o do post. Formato: <code>'campo_db' =&gt; 'R\u00f3tulo no CSV'<\/code>. Os campos suportados s\u00e3o qualquer coluna da tabela <code>wp_posts<\/code>.<\/li>\n<li><strong><code>$meta_fields<\/code><\/strong>: Metadados (campos personalizados). Exemplo: <code>'_thumbnail_id' =&gt; 'Imagem Destacada ID'<\/code>.<\/li>\n<li><strong><code>$taxonomy_fields<\/code><\/strong>: Taxonomias registradas. Exemplo: <code>'category' =&gt; 'Categorias'<\/code>, <code>'post_tag' =&gt; 'Tags'<\/code>.<\/li>\n<\/ul>\n<p>Para adicionar campos personalizados comuns, descomente as linhas de exemplo:<\/p>\n<pre><code>private $meta_fields = [\n    '_yoast_wpseo_title' =&gt; 'SEO Title',           \/\/ Yoast SEO\n    '_elementor_edit_mode' =&gt; 'Elementor Editado', \/\/ Elementor\n];\n<\/code><\/pre>\n<h3>Personaliza\u00e7\u00f5es avan\u00e7adas<\/h3>\n<ul>\n<li><strong>Alterar delimitador do CSV:<\/strong> No m\u00e9todo <code>send_csv_download<\/code>, troque <code>fputcsv( $output, $row, ';', '\"' );<\/code> \u2013 mude o <code>';'<\/code> para <code>','<\/code> se preferir v\u00edrgula (Excel americano).<\/li>\n<li><strong>Adicionar filtro por data ou status:<\/strong> Antes de processar os <code>$post_ids<\/code>, voc\u00ea pode adicionar valida\u00e7\u00f5es adicionais.<\/li>\n<li><strong>Exportar todos os posts de um tipo sem sele\u00e7\u00e3o:<\/strong> Para exportar todos os posts (n\u00e3o apenas os selecionados), voc\u00ea pode adicionar outra a\u00e7\u00e3o em massa ou uma p\u00e1gina de exporta\u00e7\u00e3o separada.<\/li>\n<li><strong>Incluir imagens em base64:<\/strong> N\u00e3o recomendado \u2013 deixe para exporta\u00e7\u00f5es mais complexas com plugins especializados.<\/li>\n<\/ul>\n<h3>Suporte para Custom Post Types (CPTs)<\/h3>\n<p>Para adicionar a exporta\u00e7\u00e3o a um CPT espec\u00edfico (ex: <code>produto<\/code>), voc\u00ea precisa adicionar duas linhas de filtro (j\u00e1 inclu\u00eddas como exemplo para <code>product<\/code>):<\/p>\n<pre><code>add_filter( 'bulk_actions-edit-&lt;slug_do_cpt&gt;', [ $this, 'add_bulk_export_action' ] );\nadd_filter( 'handle_bulk_actions-edit-&lt;slug_do_cpt&gt;', [ $this, 'handle_bulk_export' ], 10, 3 );\n<\/code><\/pre>\n<p>Substitua <code>&lt;slug_do_cpt&gt;<\/code> pelo slug real. Voc\u00ea pode adicionar quantas linhas forem necess\u00e1rias.<\/p>\n<h3>Compara\u00e7\u00e3o com o plugin original<\/h3>\n<ul>\n<li><strong>Mais leve:<\/strong> Cerca de 150 linhas de c\u00f3digo, sem telas de configura\u00e7\u00e3o complexas.<\/li>\n<li><strong>Sem depend\u00eancias:<\/strong> N\u00e3o requer bibliotecas externas de CSV.<\/li>\n<li><strong>Foco em CSV:<\/strong> O plugin original oferece XML e outros formatos; se precisar de XML, a solu\u00e7\u00e3o pode ser estendida.<\/li>\n<li><strong>Totalmente configur\u00e1vel via c\u00f3digo:<\/strong> Voc\u00ea decide exatamente quais campos exportar, sem interfaces confusas.<\/li>\n<li><strong>Excel-friendly:<\/strong> Delimitador ponto e v\u00edrgula e BOM UTF-8 garantem acentos corretos no Excel PT-BR.<\/li>\n<\/ul>\n<h3>Poss\u00edveis problemas e solu\u00e7\u00f5es<\/h3>\n<ul>\n<li><strong>Arquivo CSV vazio ou sem dados:<\/strong> Verifique se voc\u00ea selecionou pelo menos um post antes de aplicar a a\u00e7\u00e3o.<\/li>\n<li><strong>Acentos quebrados no Excel:<\/strong> O c\u00f3digo j\u00e1 inclui BOM UTF-8 (<code>chr(0xEF).chr(0xBB).chr(0xBF)<\/code>). Se ainda assim os acentos falharem, tente abrir o CSV no Excel usando &#8220;Dados \u2192 De Texto&#8221; e escolha UTF-8 como codifica\u00e7\u00e3o.<\/li>\n<li><strong>Erro de permiss\u00e3o:<\/strong> Certifique-se de que o usu\u00e1rio tem capacidade <code>edit_posts<\/code> (administradores e editores t\u00eam).<\/li>\n<li><strong>Exporta\u00e7\u00e3o muito lenta com muitos posts:<\/strong> Para centenas ou milhares de posts, o PHP pode atingir o tempo limite (max_execution_time). Nesse caso, aumente o limite no <code>php.ini<\/code> ou processe em lotes.<\/li>\n<\/ul>\n<h3>Observa\u00e7\u00f5es finais<\/h3>\n<ul>\n<li><strong>Seguran\u00e7a:<\/strong> A exporta\u00e7\u00e3o s\u00f3 est\u00e1 dispon\u00edvel para usu\u00e1rios autenticados com permiss\u00e3o para editar posts.<\/li>\n<li><strong>Performance:<\/strong> A exporta\u00e7\u00e3o ocorre em tempo real (sem buffer), ent\u00e3o o arquivo \u00e9 baixado imediatamente sem ocupar disco no servidor.<\/li>\n<li><strong>Limita\u00e7\u00e3o:<\/strong> O plugin n\u00e3o exporta coment\u00e1rios, m\u00eddia anexada ou outros objetos complexos. Para exporta\u00e7\u00e3o completa do site, use a ferramenta nativa do WordPress (Ferramentas \u2192 Exportar) ou plugins como All-in-One WP Migration.<\/li>\n<li><strong>Extensibilidade:<\/strong> Voc\u00ea pode facilmente adicionar novos campos ou modificar os existentes editando os arrays de configura\u00e7\u00e3o.<\/li>\n<\/ul>\n<h3>Registro no caderno<\/h3>\n<p>Data: <\/p>\n<p>Funcionalidade: Exporta\u00e7\u00e3o seletiva de posts para CSV via a\u00e7\u00e3o em massa<\/p>\n<p>Plugins substitu\u00eddos: advanced-export-for-wp-wpmu (e similares)<\/p>\n<p>Tempo de implementa\u00e7\u00e3o: ~15 minutos (cria\u00e7\u00e3o e testes)<\/p>\n<p>Observa\u00e7\u00e3o: Para exportar todos os campos de uma vez, basta adicionar mais entradas nos arrays <code>$meta_fields<\/code> e <code>$taxonomy_fields<\/code>.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Aula: Exporta\u00e7\u00e3o Avan\u00e7ada para CSV com MU-Plugin \u2013 Substituindo o Advanced Export for WP WPMU Objetivo: Substituir o plugin advanced-export-for-wp-wpmu por um MU-Plugin enxuto que permite exportar posts, p\u00e1ginas e custom post types para um arquivo CSV com os campos selecion\u00e1veis, diretamente da tela de administra\u00e7\u00e3o. Contexto: O plugin original oferece exporta\u00e7\u00e3o flex\u00edvel para CSV\/XML, [&hellip;]<\/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-284","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\/284","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=284"}],"version-history":[{"count":0,"href":"https:\/\/www.portanova.com.br\/notebook\/wp-json\/wp\/v2\/posts\/284\/revisions"}],"wp:attachment":[{"href":"https:\/\/www.portanova.com.br\/notebook\/wp-json\/wp\/v2\/media?parent=284"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.portanova.com.br\/notebook\/wp-json\/wp\/v2\/categories?post=284"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.portanova.com.br\/notebook\/wp-json\/wp\/v2\/tags?post=284"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}