Idioma

Entrar em contacto

Plugin para WordPress · desde 2019 · aka WPBE

WOLF

Um editor ao estilo de folha de cálculo para editar em massa, filtrar, exportar e reverter posts, páginas e qualquer tipo de post personalizado.

Primeira versão
Instalações ativas
4000+
Classificação
4.7 / 5 (27)Ler avaliações
  • PHP
  • WP_Query (tax_query, meta_query)
  • Filtros posts_where
  • $wpdb + dbDelta
  • Transients
  • admin-ajax
  • jQuery
  • DataTables (modo servidor)
  • DOMDocument (exportação WXR)
  • fputcsv
Ver em wordpress.org wordpress.org

Sobre

O WOLF (antes WPBE) é o equivalente para posts do editor em massa BEAR do WooCommerce. Mostra posts de qualquer tipo de post registado numa tabela editável, permite filtrá-los por campos principais, taxonomias e meta, e depois alterar, eliminar ou exportar todo o conjunto filtrado numa só operação.

O difícil é que nada é fixo. Cada tipo de post traz as suas próprias taxonomias e chaves meta, os valores meta podem ser arrays serializados, listas de IDs de imagens ou flags com valores personalizados, e o conjunto de destino pode ser de milhares de posts em alojamento partilhado com limites curtos de tempo PHP.

O plugin responde com um registo de campos reconstruído por tipo de post, um filtro guardado no servidor e compilado numa única WP_Query, execuções em massa conduzidas pelo navegador em lotes de tamanho fixo, e um registo de histórico que guarda o valor anterior de cada campo antes de ser escrito, de modo que uma execução em massa também pode ser revertida em lotes.

A versão premium e a versão gratuita no wordpress.org partilham a mesma base de código; uma única flag liga os limites da versão gratuita.

WOLF · editor de artigos em massa

o que foi resolvido

ATIVOdesde 2019

Um editor para qualquer tipo de post

shipped · 2019

O que ofereceA equipa edita qualquer tipo de conteúdo numa tabela que se mantém rápida em sites grandes, com colunas configuradas por pessoa e por idioma.

Problema
Os posts, as páginas e os tipos de post personalizados têm taxonomias, chaves meta e campos principais aplicáveis diferentes. Uma só tabela tem de mostrar qualquer um deles e manter-se rápida em sites com muitos registos.
Solução

Um registo de campos é reconstruído para o tipo de post ativo e a tabela corre em modo servidor do DataTables sobre a WP_Query, de modo que cada pedido lê uma página de posts.

Stack
  • PHP
  • WP_Query
  • DataTables
  • admin-ajax
Tarefas · 5
Cada tipo de post traz o seu próprio conjunto de taxonomiasAs colunas de taxonomia são construídas a partir de get_object_taxonomies() para o tipo de post ativo

WPBE_SETTINGS::get_fields() junta a lista estática de campos de data/fields.php com uma coluna por taxonomia registada para o tipo de post atual, e aplica depois a ordem e os títulos das colunas guardados pelo utilizador. Os campos novos que surgem mais tarde (uma nova taxonomia, uma nova chave meta) são acrescentados em vez de se perderem.

As definições de campos levam allow_post_types e prohibit_post_types. wrap_field_val() devolve uma célula 'restricted' quando um campo não se aplica, por exemplo campos de estado e de comentários para anexos, tipo MIME apenas para anexos, fixo apenas para posts. Os anexos são consultados com o estado extra 'inherit'.

Os sites grandes não podem carregar todos os posts no navegadorDataTables no servidor: cada página, ordenação e pesquisa é uma WP_Query paginada

A tabela é configurada com serverSide: true e chama wpbe_get_posts com os parâmetros de paginação do DataTables. WPBE_POSTS::gets() converte start/length em paged/posts_per_page e passa à ordenação por meta_value_num quando a coluna de ordenação é um campo meta numérico ou de data.

As linhas por página estão limitadas a 100 com o comentário de código 'to avoid 500 error on weak servers'. A versão gratuita limita-as a 10.

O código do editor não deve abrandar o resto do wp-adminExtensões e modelos carregam-se apenas na página do editor e nas ações AJAX wpbe_*

init() executa primeiro uma verificação de papel e depois is_should_init(), que devolve true apenas para page=wpbe ou uma ação AJAX cujo nome contém 'wpbe_'. Nos outros ecrãs de administração são registados apenas a entrada de menu, o botão da barra de administração e os avisos.

As extensões seguem uma convenção de nomes (ext/<slug>/<slug>.php, classe WPBE_<SLUG>) e recebem o armazenamento, as definições e o modelo de posts através de init_vars(). O mesmo carregador apanha também extensões de terceiros de wp-content/wpbe_ext.

Cada utilizador precisa de colunas próprias, por tipo de post se necessárioOpções guardadas por ID de utilizador, opcionalmente separadas por tipo de post (definição premium)

As definições vivem na opção wpbe_options_<user_id>. Quando o interruptor 'separate settings' está ligado, o tipo de post é acrescentado à chave, e a lista de campos meta registados é dividida por tipo de post da mesma forma. O tipo de post ativo é lembrado por utilizador no armazenamento do plugin.

O interruptor está desativado na versão gratuita.

Em sites WPML, o editor tem de funcionar no idioma de administração atualIdioma WPML atual passado a cada consulta; lista de posts fixos mantida por idioma

Quando o SitePress está presente, o filtro wpbe_current_language devolve ICL_LANGUAGE_CODE, gets() chama $sitepress->switch_lang() antes de construir a WP_Query, e a opção de posts fixos é lida e escrita sob sticky_posts_<lang>.

Edição em massa, eliminação e exportação em lotes

shipped · 2019

O que ofereceAs alterações a milhares de posts terminam de forma fiável em alojamento comum, com progresso visível e a opção de parar a meio.

Problema
Editar milhares de posts num único pedido PHP esbarra em limites de tempo e de memória, e o utilizador precisa de progresso e de uma forma de parar a execução.
Solução

O navegador conduz a execução em lotes de IDs de posts de tamanho fixo; a própria operação é guardada uma vez no servidor sob uma bulk key aleatória.

Stack
  • PHP
  • jQuery
  • Transients
  • API de termos do WordPress
  • fputcsv
  • DOMDocument
Tarefas · 5
Um pedido PHP não consegue editar milhares de postsIDs resolvidos uma vez e depois enviados em lotes de 50 para um endpoint de trabalho sem estado

wpbe_bulk_posts_count guarda o formulário de edição em massa sob 'wpbe_bulk_<key>' (16 carateres aleatórios gerados no navegador) e devolve os IDs que correspondem ao filtro atual, usando fields=ids e no_found_rows. O navegador fatia essa lista em lotes de 50 e envia-os um após outro para wpbe_bulk_posts, atualizando uma barra de progresso e um indicador na barra de administração. Cancelar aborta o XHR pendente; wpbe_bulk_finish devolve o total contado.

A eliminação em massa usa o mesmo padrão com lotes de 10 e wp_trash_post(), pelo que os posts eliminados continuam recuperáveis a partir do lixo.

Depois de uma execução, o navegador redesenha apenas as linhas tocadas quando foram alterados quatro posts ou menos, e redesenha a página atual caso contrário.

Os campos de texto precisam de edições relativas ao valor atualAcrescentar no fim, no início, substituir (com ou sem distinção de maiúsculas), definir novo; macros por post

__process_text_data() lê o valor atual de cada post e aplica o comportamento escolhido. Os valores podem conter {TITLE}, {ID}, {MENU_ORDER} e {meta:key}, resolvidos por post, e macros de capitalização como {DO_STRING_UP} ou {DO_STRING_TITLE} que usam as funções mb_*, pelo que os títulos não latinos são tratados.

A meta numérica precisa de alterações relativas e de arredondamento ao estilo de preçosPor valor ou percentagem, fórmula com outra chave meta, intervalo aleatório, arredondamento a .05, .10 ou .x9

__process_number_data() suporta aumentar/diminuir por valor ou percentagem, depois combina opcionalmente o resultado com outro campo meta do mesmo post (+, -, *, /, com a divisão por zero substituída por 1) e com um número aleatório de um intervalo escalado por um fator decimal.

O arredondamento da fração a passos de .05 ou .10, ou a uma terminação como .99, acontece em update_page_field() no número de casas decimais configurado; os cêntimos são convertidos através de um passo inteiro porque o módulo de floats dá resultados errados.

As taxonomias hierárquicas e planas precisam de entradas de termos diferentesIDs de termos para taxonomias hierárquicas, slugs de termos um a um para as planas

O WordPress espera IDs para taxonomias hierárquicas e nomes ou slugs para as do tipo etiqueta. O worker da edição em massa verifica is_taxonomy_hierarchical(); para taxonomias planas converte cada ID num slug e, em 'replace', escreve o primeiro termo com append=false e os restantes com append=true. 'Remove' usa wp_remove_object_terms().

O hook do histórico é disparado manualmente antes das alterações de termos, porque este caminho contorna update_page_field().

As exportações de seleções grandes esbarram nos mesmos limitesCSV acrescentado por lote de 10 via fputcsv; XML escrito no formato WXR do WordPress

O passo de contagem escreve a linha de cabeçalho; cada lote acrescenta linhas com fputcsv() num ficheiro aberto em modo a+. O código nota que o WP_Filesystem não tem acréscimo linha a linha, e por isso se usam aqui as funções de ficheiro brutas.

A exportação XML constrói um documento RSS com os namespaces WXR 1.2 (wp:, content:, excerpt:), termos como <category domain nicename> e meta como wp:postmeta com CDATA, de modo que o ficheiro segue o formato do exportador do WordPress. Os nomes de ficheiros levam a data e o minuto da exportação, e os ficheiros com mais de dois dias são eliminados por uma verificação limitada por um transient.

Filtragem por campos, termos e meta

shipped · 2019

O que ofereceA equipa seleciona exatamente os posts que quer por campos, categorias, valores personalizados ou listas de IDs coladas, e as edições e exportações aplicam-se a essa mesma seleção.

Problema
A edição em massa e a exportação aplicam-se ao que o filtro encontrar, pelo que o filtro tem de transformar muitas condições de campos numa WP_Query correta e manter-se idêntico entre pedidos AJAX separados e separadores do navegador.
Solução

O estado do filtro é guardado no servidor sob uma chave aleatória por separador e compilado em cláusulas tax_query, meta_query e posts_where num hook usado por todas as consultas.

Stack
  • WP_Query
  • tax_query
  • meta_query
  • posts_where
  • MySQL REGEXP
Tarefas · 6
Os pedidos da tabela, da edição em massa e da exportação têm de ver o mesmo filtroFiltro guardado no servidor sob uma chave de 16 carateres gerada por separador do navegador

O navegador gera wpbe_filter_current_key quando um filtro é aplicado e envia-a com cada pedido; o comentário do código-fonte diz que isto permite a edição em massa por filtro em separadores diferentes. wpbe_apply_query_filter_data() lê o filtro guardado e é aplicada dentro de WPBE_POSTS::gets(), pelo que a lista de IDs da edição em massa é construída pelo mesmo código que a tabela visível.

Corresponder a todos os termos de vários exige uma cláusula tax_query por termoAND dividido numa cláusula tax_query por termo; EXISTS e NOT EXISTS acrescentados

Para o operador AND, cada termo selecionado torna-se a sua própria cláusula, unida pela relation AND exterior, que é a solução alternativa referida no comentário do código. Os outros operadores (IN, NOT IN) passam a lista de termos tal como está. include_children é controlado por um filtro em vez do valor por defeito do WordPress.

Os intervalos de meta numérica precisam de um cast para DECIMAL com escala explícitaBETWEEN com cast para DECIMAL(30,20); limites unilaterais e iguais tratados

O comentário do código dá o caso: com DECIMAL sem precisão, uma pesquisa de 100 a 150 devolvia também 99.xx. O filtro usa DECIMAL(30,20), passa a >= quando só 'from' está definido e a = quando os dois limites são iguais. A meta de datas passa primeiro por normalize_calendar_date().

Um valor meta vazio pode ser '' ou uma linha em falta'Empty' compila para value = '' OR NOT EXISTS; 'not empty' para value != ''

Os posts criados antes de uma chave meta ser introduzida não têm linha nenhuma, pelo que uma verificação de igualdade sozinha não os apanha. O comportamento 'empty' emite uma meta_query aninhada com relation OR.

A pesquisa de texto precisa de OR, AND e NOT sem SQL brutoPequena sintaxe (^ para OR, ~ para AND, ! para NOT) compilada para REGEXP preparado

posts_txt_where() converte a entrada para minúsculas, divide em ^ e constrói condições LOWER(column) REGEXP / NOT REGEXP com os modos begin, end, exact e not. Cada valor é associado por $wpdb->prepare() e a coluna tem de estar numa lista de permissões de cinco colunas de wp_posts.

Os utilizadores colam listas de IDs e intervalosEntrada como '12,40-55' expandida em post__in

Os itens separados por vírgulas são tomados como IDs; um item com hífen é expandido para o intervalo completo, independentemente da ordem. Os intervalos de datas e números em colunas de wp_posts (menu_order, post_parent, datas do post) passam por um filtro posts_where separado com uma lista de permissões de colunas.

Histórico e reversão

shipped · 2019

O que ofereceUma edição em massa errada pode ser desfeita num só passo, em vez de corrigir milhares de posts à mão.

Problema
Uma edição em massa errada em milhares de posts é difícil de desfazer: as revisões do WordPress não cobrem meta, termos nem a maioria dos campos principais, e a própria reversão pode ser tão grande como a edição.
Solução

Cada escrita de campo é precedida por uma linha de registo com o valor anterior; as linhas de uma execução em massa partilham a sua chave e são revertidas em lotes.

Stack
  • $wpdb
  • dbDelta
  • MySQL
  • jQuery
Tarefas · 5
Capturar o valor antigo de qualquer tipo de campo antes de mudarUm hook antes de cada escrita guarda o valor anterior em wp_wpbe_history

write() está associada a wpbe_before_update_page_field, que update_page_field() dispara antes de cada escrita; o caminho da taxonomia em massa, que escreve termos diretamente, dispara-o manualmente. Os valores de taxonomia são guardados como lista JSON de IDs de termos, a meta de arrays como JSON (com JSON_HEX_QUOT e JSON_HEX_TAG para o editor de meta serializada), os escalares tal como estão. Cada linha tem user_id e post_type, e a bulk key quando faz parte de uma execução.

Milhares de alterações têm de formar uma operação que se possa desfazerTabela de cabeça wpbe_history_bulk por execução: chaves, número de posts e estado

start_bulk() insere a linha de cabeça com a lista de campos editados como JSON; cada lote soma o seu tamanho por UPDATE posts_count = posts_count + %d; finish_bulk() marca a execução como concluída. Uma execução cancelada pelo utilizador mantém o estado por defeito 'terminated', pelo que a lista a mostra como parcial. Ambas as tabelas são criadas com dbDelta e o charset do site, indexadas por bulk_key e user_id, e eliminadas na desinstalação.

Reverter uma execução grande esbarra nos mesmos limites de tempoReverter em porções de 10 linhas por pedido; linha de cabeça removida no fim

O navegador pede a contagem de linhas da bulk key e chama depois wpbe_history_revert_bulk_portion repetidamente com limit 10 e uma barra de progresso. Cada linha revertida é eliminada, pelo que a porção seguinte pega nas restantes; a linha de cabeça é eliminada quando o contador atinge o total.

Uma reversão não deve registar-se a si própria como nova alteraçãoremove_all_actions() no hook de registo antes de escrever os valores antigos

revert() desliga o escritor do histórico, descodifica o valor guardado por tipo de campo (listas de IDs de termos, arrays meta JSON) e escreve-o de volta através do mesmo update_page_field() usado para as edições, pelo que save_post e edit_post disparam numa reversão como numa edição.

As edições individuais e as execuções em massa devem aparecer numa só linha temporalLinhas individuais e cabeças em massa fundidas e ordenadas por hora, por utilizador e tipo de post

get_history() lê as linhas individuais e as cabeças em massa do utilizador e tipo de post atuais, funde-as e ordena-as por hora, a mais recente primeiro, de modo que uma só lista mostra os dois tipos de alteração.

A versão gratuita mostra as duas entradas mais recentes e elimina as linhas individuais e cabeças em massa mais antigas quando a lista é aberta.

Campos meta e conteúdo estruturado

shipped · 2019

O que ofereceOs dados personalizados, como listas, flags e galerias de imagens, podem ser editados em segurança sem os estragar, mesmo quando a equipa não sabe como estão guardados.

Problema
Os tipos de post personalizados guardam a maior parte dos seus dados em postmeta, muitas vezes como arrays serializados, listas de IDs de imagens ou flags com valores específicos do site, que uma célula de texto simples corromperia.
Solução

As chaves meta são registadas com um tipo e uma vista de edição, e cada vista tem o seu próprio leitor, escritor, filtro e handler de edição em massa.

Stack
  • PHP
  • API de postmeta
  • jQuery
  • Data store do editor de blocos
Tarefas · 5
Os administradores não sabem que chaves meta um tipo de post usaChaves lidas de um post de amostra; as chaves registadas tornam-se colunas, filtros, campos de edição em massa

wpbe_meta_get_keys devolve as chaves meta de um ID de post introduzido pelo administrador. Uma chave registada é injetada através dos hooks wpbe_extend_fields, wpbe_filter_* e wpbe_bulk_*, pelo que aparece na tabela, no painel de filtros e no painel de edição em massa ao mesmo tempo. As chaves são aparadas mas não convertidas para minúsculas, porque chaves como _woocs_sale_price_USD distinguem maiúsculas de minúsculas.

A versão gratuita usa apenas os dois primeiros campos meta registados.

Os arrays serializados têm de ser editados sem quebrar a estruturaO construtor em popup envia arrays chave/valor; o servidor reconstrói arrays aninhados

O popup envia keys[]/values[] para arrays planos e aninhados e keys2[][]/values2[][] para listas de objetos. __process_jsoned_meta_data() reconstrói o array PHP, descodifica os valores que são JSON válido e recorre ao valor bruto para strings simples. update_post_meta() guarda-o depois serializado como o WordPress espera. O mesmo caminho é usado pelo worker da edição em massa.

As flags são guardadas como 'yes'/'no' ou outros pares específicos do siteAs regras de mapeamento 'key:on^off' traduzem entre o interruptor 1/0 e os valores guardados

over_switcher_swicher_to_val() e over_switcher_val_to_swicher() leem uma string de definições como 'dist:yes,stest:true' e convertem nos dois sentidos, para que a interface do interruptor funcione com valores escritos por outros plugins.

Galerias de imagens guardadas em meta como listas de IDs de anexosTipo de meta galeria guardado como lista separada por vírgulas de IDs de anexos

O popup da galeria envia um formulário codificado em URL; o handler deteta a codificação, analisa-o com parse_str(), sanitiza-o e guarda os IDs como string separada por vírgulas. O worker da edição em massa aceita o mesmo formato.

O conteúdo do editor de blocos não pode ser editado numa textarea simplesPost aberto no editor de blocos dentro de um iframe; conteúdo lido via wp.data

Opção 'Use gutenberg'. Para os tipos de post que usam o editor de blocos, o popup carrega post.php com wpbe_popup_editor=1; wp_editor_compatibility() oculta os controlos de publicação e da barra lateral nessa frame e expõe uma função que devolve wp.data.select('core/editor').getEditedPostContent(). A página-pai lê-o e guarda através da atualização normal de campo.

Controlo de acessos e reforço dos pedidos

shipped · 2023

O que ofereceSó os papéis de confiança podem usar o editor, os autores trabalham apenas com os seus próprios posts, e a equipa vê apenas os campos que pode alterar.

Problema
Uma ferramenta que pode reescrever qualquer campo de qualquer post em massa é um alvo de grande valor: cada endpoint AJAX tem de verificar quem chama, e a entrada dos filtros acaba perto do SQL.
Solução

Uma porta de papéis antes de qualquer endpoint ser registado, nonces nas ações de campos, edição em massa, ferramentas e definições, direitos ao nível do campo para papéis não administradores, e SQL preparado com listas de permissões de colunas.

Stack
  • Papéis do WordPress
  • Nonces
  • $wpdb->prepare
  • wp_kses
Tarefas · 5
Só os papéis de confiança podem chegar a qualquer endpoint do editorA verificação de papel em init() corre antes de qualquer handler wp_ajax_wpbe_* ser adicionado

can_manage_data() permite administradores, editores e papéis adicionados através do filtro wpbe_author_area_roles. Se falhar, init() devolve antes de as extensões carregarem, pelo que as ações AJAX não existem para esse utilizador. As atualizações de campos, a edição e a eliminação em massa, o painel de ferramentas (criar, duplicar, eliminar, termos), as definições meta, o formulário de opções, a lista do histórico e as reversões individuais verificam também um nonce.

Os editores não devem ver nem alterar todos os campos e tipos de postO administrador define a visibilidade dos campos e os tipos de post permitidos para papéis não administradores (premium)

Para papéis não administradores, get_fields() descarta as colunas ocultas, pelo que não são oferecidas na tabela nem no painel de edição em massa; update_page_field() verifica is_current_user_can_edit_field() e devolve 'forbidden' para um campo oculto; filter_post_types() restringe o seletor de tipo de post, e o construtor das definições move o utilizador para um tipo de post permitido. Na versão gratuita, as checkboxes de visibilidade são desenhadas desativadas.

Os autores devem trabalhar apenas com os seus próprios postsOs papéis da área de autor recebem author=self nas consultas do editor e não podem alterar post_author

Para os papéis adicionados através do filtro wpbe_author_area_roles, WPBE_AUTHOR_AREA acrescenta 'author' => ID do utilizador atual através do filtro de consulta usado pela tabela, pela lista de IDs da edição em massa e pela exportação, de modo que estas listas mostram apenas os posts do próprio utilizador. O seu callback wpbe_user_can_edit bloqueia post_author para estes papéis.

A entrada dos filtros e os dados de posts copiados acabam no SQLNomes de colunas a partir de listas de permissões, cada valor associado por $wpdb->prepare()

Os filtros de texto e de intervalo aceitam apenas as colunas de wp_posts listadas e associam os valores por prepare(). As consultas do histórico associam utilizador, tipo de post e bulk key, e constroem listas NOT IN a partir de marcadores de array_fill().

A duplicação copia os termos por slug e todo o postmeta exceto _wp_old_slug numa só instrução INSERT ... SELECT %d, %s, %s UNION ALL, cada linha construída com prepare(), e define depois a cópia como rascunho.

Os campos de conteúdo têm de manter embeds mas largar scriptswp_kses com a lista de permissões de posts mais uma lista estreita de atributos de iframe

wpbe_update_page_field() sanitiza arrays recursivamente com wp_kses e a lista de permissões de posts. Para os campos de conteúdo, excerto e texto rico, acrescenta iframe com um conjunto fixo de atributos para que os embeds de vídeo sobrevivam. Um filtro pode passar para sanitize_text_field() em sites mais estritos.

Números

wordpress.org
  • Instalações ativas 4000+ contador do wordpress.org.
  • Avaliações 27 Média de 4.7 em 5.
  • Anos de suporte 7 Desde 3 de setembro de 2019, última atualização em 12 de outubro de 2026.