Como salvar os status de metaboxes abertos / fechados e ocultos / mostrados salvos por postagem?

9

Meu problema real é um pouco complexo, então tentarei abstraí-lo e mantê-lo simples.

Estou trabalhando em um aplicativo personalizado baseado no WordPress. Registrei um tipo de postagem personalizado, vamos chamá-lo de "pessoas", onde armazeno informações sobre ... pessoas.

O CPT oferece suporte apenas aos campos padrão de título e conteúdo de postagem, mas existem algumas metaboxes para armazenar informações pessoais (pense em meu aplicativo como um catálogo de endereços).

Portanto, existe um metabox para armazenar informações pessoais, um para armazenar informações de redes sociais e outro para armazenar informações relacionadas ao trabalho, ou seja, se essa pessoa é para mim um cliente, um fornecedor, se temos créditos ou débitos ...

Simplifiquei aqui, mas há uma quantidade consistente de metaboxes, digamos 12.

Meu problema é que, algumas pessoas para as quais eu quero armazenar informações são apenas contatos aleatórios e quero armazenar apenas informações pessoais, outras são amigas e quero armazenar informações pessoais e informações de redes sociais, outras são clientes ou fornecedores e eu deseja armazenar informações relacionadas ao trabalho.

Se, ao editar uma postagem, eu oculto (através do menu de opções da tela ) ou fecho qualquer metabox, não preciso, quando abro outra postagem onde preciso delas, preciso mostrar ou abri-las novamente. Isso porque os metaboxes posição / status / pedido são salvos por usuário como metadados do usuário .

Se você imagina que em algumas postagens eu preciso de 2 metaboxes, em 10 e 5, você entende que isso é irritante porque manter todos eles mostrados / abertos torna a tela de edição baixa acessível (a barra de rolagem parece interminável), e às vezes as informações que eu procuro são no final da página, após um monte de metaboxes sem informações ...

Questão:

É possível salvar metaboxes de posição / status / ordem por postagem para um tipo de postagem específico?


PS: Eu sei que alguns js / jQuery podem resolver o problema, mas, se possível, evitaria soluções javascript.

gmazzap
fonte

Respostas:

8

O problema principal:

O principal problema aqui é que, nas chamadas de fechamento , ocultação e pedido do ajax, não há ID de postagem enviado com a carga útil. Aqui estão dois exemplos de dados de formulário:

1) action:closed-postboxes
closed:formatdiv,tagsdiv-post_tag,trackbacksdiv,authordiv
hidden:slugdiv
closedpostboxesnonce:8723ee108f
page:post

2) action:meta-box-order
_ajax_nonce:b6b48d2d16
page_columns:2
page:post
order[side]:submitdiv,formatdiv,categorydiv,tagsdiv-post_tag,postimagediv
order[normal]:postexcerpt,postcustom,trackbacksdiv,commentsdiv,authordiv
order[advanced]:

Poderíamos contornar isso usando outra chamada ajax personalizada.

É claro que poderíamos simplesmente nos save_postconectar e modificar os dados cada vez que a postagem é salva. Mas essa não é a experiência normal da interface do usuário; portanto, isso não é considerado aqui

Há outra solução não elegante disponível com PHP, descrita aqui abaixo:

Uma solução não Javascript:

A questão é onde armazenar os dados? Como metadados do usuário , poste metadados ou talvez em uma tabela personalizada?

Aqui, os armazenamos como metadados do usuário e tomamos o fechamento das metad boxes pós como exemplo.

Quando o closedpostboxes_postmeta-valor é atualizado, também o salvamos no closedpostboxes_post_{post_id}meta-valor.

Em seguida, seqüestramos a busca de closedpostboxes_postsubstituí-lo pelo meta-valor correspondente, com base no ID do usuário e no ID da postagem.

a) Atualização durante a closed-postboxesação do ajax:

Podemos buscar o ID da postagem, wp_get_referer()usando a url_to_postid()função útil . Eu conheci essa função "engraçada" pela primeira vez depois de ler a resposta de @s_ha_dum , há alguns meses ;-) Infelizmente, a função não reconhece ?post=123variáveis ​​GET, mas podemos fazer um pequeno truque apenas alterando-a p=123para contorná-la.

Podemos nos conectar updated_user_meta, que é acionado logo após a closedpostboxes_postatualização dos metadados do usuário :

add_action( 'updated_user_meta',                           
    function ( $meta_id, $object_id, $meta_key, $_meta_value )
    {
        $post_id = url_to_postid( str_replace( 'post=', 'p=', wp_get_referer() ) );
        if( 'closedpostboxes_post' === $meta_key && $post_id > 0 )
            update_user_meta( 
                $object_id, 
                'closedpostboxes_post_' . $post_id, 
                $_meta_value 
            );
    }
, 10, 4 );

b) Buscando dados:

Podemos conectar o get_user_option_closedpostboxes_postgancho para modificar os dados buscados na closedpostboxes_postmeta do usuário:

add_filter( 'get_user_option_closedpostboxes_post',
    function ( $result, $option, $user )
    {
        $post_id = filter_input( INPUT_GET, 'post', FILTER_SANITIZE_NUMBER_INT );
        $newresult = get_user_option( 'closedpostboxes_post_'. $post_id , $user->ID );
        return ( $newresult ) ? $newresult : $result;
    }
, 10, 3 );

Também podemos pensar no caso em que não há post closedpostboxes_post_{post_id}disponível disponível. Portanto, ele usará as últimas configurações salvas de closedpostboxes_post. Talvez você queira ter tudo aberto ou fechado, nesse caso padrão. Seria fácil modificar esse comportamento.

Para outros tipos de postagem personalizados, podemos usar o closedpostboxes_{post_type}gancho correspondente .

O mesmo deve ser possível para a ordenação e ocultação de metaboxes com a meta metaboxhidden_{post_type}e do meta-box-order_{post_data}usuário.

ps: desculpe-me por esta resposta de fim de semana muito longa, pois ela sempre deve ser curta e alegre ;-)

Birgire
fonte
Ótimo +1. N / P para resposta longa, eu não esperaria respostas curtas. Para ser sincero, não esperava nada no fim de semana :) Duas coisas de que gostei muito: primeiro: a idéia de armazenar dados por usuário e por postagem: minha idéia era armazenar na meta meta, mas dessa forma todas os usuários terão o mesmo status. 2º a idéia a ser usada 'get_user_option_*_post'para fazer o WP reconhecer dados personalizados. Só acho que eu não gosto muito é o uso do wp_get_refererque realmente em $_SERVERvar que é não é realmente confiável , mas eu acho que tenho uma idéia para superar o "principal problema";)
gmazzap
Obrigado, acho que depende do número de usuários e postagens onde seria melhor armazenar os dados. Talvez esses dados devam ter algum TTL e ser apagados, por exemplo, uma vez por mês? Sim, eu concordo com você sobre o wp_get_referer()método, por isso o chamei de uma solução PHP não elegante ;-) Pensei em armazenar o ID da postagem atual de cada usuário, mas isso não funciona se um usuário estiver editando dois ou mais postagens no navegador. Ansiosos de ouvir sobre a sua ideia sobre o "principal problema" Aproveite o fim de semana ;-)
birgire
Após 43 dias, um voto positivo me lembrou de responder a isso. Obrigado novamente por sua resposta.
gmazzap
6

Conforme apontado por birgire em sua resposta , o WordPress usa o AJAX para atualizar o status de metaboxes e os dados passados ​​na solicitação do AJAX não incluem o ID da postagem, o que dificulta a atualização do status das caixas por postagem.

Depois de encontrar a ação AJAX usada pelo WordPress 'closed-postboxes', procurei essa sequência na pasta admin js para descobrir como o WordPress faz a solicitação AJAX.

Descobri que isso acontece postbox.jsna linha # 118 .

Parece assim:

save_state : function(page) {
  var closed = $('.postbox').filter('.closed').map(function() {
      return this.id;
    }).get().join(',');
  var hidden = $('.postbox').filter(':hidden').map(function() {
      return this.id;
    }).get().join(',');
  $.post(ajaxurl, {
    action: 'closed-postboxes',
    closed: closed,
    hidden: hidden,
    closedpostboxesnonce: jQuery('#closedpostboxesnonce').val(),
    page: page
  });
}

Essencialmente, o WordPress analisa itens do DOM com a classe 'caixa postal' e a classe 'fechada' e cria uma lista separada por vírgula de seus IDs. O mesmo é feito para itens DOM ocultos com a classe 'caixa postal'.

Então, meu pensamento era: eu posso criar um metabox falso que tenha as classes certas e oculto, definindo seu ID para conter o ID da postagem e, dessa forma, eu posso recuperá-lo em uma solicitação AJAX.

Isto é o que eu fiz:

add_action( 'dbx_post_sidebar', function() {
    global $post;
    if ( $post->post_type === 'mycpt' ) {
        $id = $post->ID;
        $f = '<span id="fakebox_pid_%d" class="postbox closed" style="display:none;"></span>';
        printf( $f, $id );
    }
});

Dessa maneira, criei uma metabox que está sempre fechada e sempre oculta, para que o WordPress envie seu ID como $_POSTvar na solicitação AJAX e, uma vez que o ID da caixa falsa contenha o ID da postagem de maneira previsível, sou capaz de reconhecê-la.

Depois disso, observei como o WordPress executa a tarefa AJAX.

Na admin-ajax.phplinha 72 , o WordPress adere 'wp_ajax_closed-postboxes'à prioridade 1.

Portanto, para agir antes do WordPress, eu poderia conectar a mesma ação à prioridade 0.

add_action( 'wp_ajax_closed-postboxes', function() {

    // check if we are in right post type: WordPress passes it in 'page' post var
    $page = filter_input( INPUT_POST, 'page', FILTER_SANITIZE_STRING );
    if ( $page !== 'mycpt' ) return;

    // get post data
    $data = filter_input_array( INPUT_POST, array(
        'closed' => array( 'filter' => FILTER_SANITIZE_STRING ),
        'hidden' => array( 'filter' => FILTER_SANITIZE_STRING )
    ) );

    // search among closed boxes for the "fake" one, and return if not found
    $look_for_fake = array_filter( explode( ',', $data[ 'closed' ] ), function( $id ) {
         return strpos( $id, 'fakebox_pid_' ) === 0;
    } );
    if ( empty( $look_for_fake ) ) return;

    $post_id = str_replace( 'fakebox_pid_', '', $look_for_fake[0] );
    $user_id = get_current_user_id();

    // remove fake id from values
    $closed = implode(',', array_diff( explode(',', $data['closed'] ), $look_for_fake ) );
    $hidden = implode(',', array_diff( explode(',', $data['hidden'] ), $look_for_fake ) );

    // save metabox status on a per-post and per-user basis in a post meta
    update_post_meta( $post_id, "_mycpt_closed_boxes_{user_id}", $closed );
    update_post_meta( $post_id, "_mycpt_hidden_boxes_{user_id}", $hidden );

}, 0 );

Tendo os dados salvos em uma meta de postagem, foi possível filtrar get_user_option_closedpostboxes_mycpte get_user_option_metaboxhidden_mycpt(ambas as variações do get_user_option_{$option}filtro) forçar as opções de carregamento do WordPress a partir da meta de postagem:

add_filter( 'get_user_option_closedpostboxes_mycpt', function ( $result, $key, $user ) {
    global $post;
    $meta = get_post_meta( $post->ID, "_mycpt_closed_boxes_{$user->ID}", TRUE );
    if ( ! empty( $meta ) ) {
        $result = $meta;
    }
    return $result;
}, 10, 3 );

e

add_filter( 'get_user_option_metaboxhidden_mycpt', function ( $result, $key, $user ) {
    global $post;
    $meta = get_post_meta( $post->ID, "_mycpt_hidden_boxes_{$user->ID}", TRUE );
    if ( ! empty( $meta ) ) {
        $result = $meta;
    }
    return $result;
}, 10, 3 );
gmazzap
fonte
Que grande ideia usando um METABOX escondido com o relevante informações +1
birgire
graças @birgire e Obrigado novamente por seu A, a idéia de salvar os dados em ambos por usuário e por post base é toda sua :)
gmazzap