Adicionar validação e tratamento de erros ao salvar campos personalizados?

27

Eu tenho uma função que define um campo personalizado em um tipo de postagem. Digamos que o campo seja "subtítulo".

Quando a postagem é salva, desejo validar a entrada e exibir uma mensagem de erro na tela de edição da postagem, se necessário. Algo como:

// Handle post updating
function wpse_update_post_custom_values($post_id, $post) {

    // Do some checking...
    if($_POST['subhead'] != 'value i expect') {

        // Add an error here
        $errors->add('oops', 'There was an error.');

    }

    return $errors;

} 
add_action('save_post','wpse_update_post_custom_values',1,2);

Estou tentando vincular isso à ação save_post, mas não consigo descobrir como lidar com erros. Não parece haver um objeto de erro passado para a função e, se eu criar meu próprio objeto WP_Error e o devolver, ele não será respeitado por qualquer mecanismo que solte erros na página de pós-edição.

Atualmente, tenho uma mensagem de erro na página dentro da minha caixa de meta personalizada, mas isso é menos do que o ideal - eu prefiro ter um grande erro vermelho, alto, como o WP normalmente exibe.

Alguma ideia?

ATUALIZAR:

Com base na resposta de @Denis, tentei algumas coisas diferentes. O armazenamento de erros como global não funcionou, porque o Wordpress faz um redirecionamento durante o processo save_post, que mata o global antes que você possa exibi-lo.

Acabei armazenando-os em um meta-campo. O problema é que você precisa limpá-las ou elas não desaparecerão quando você navegar para outra página, então tive que adicionar outra função anexada ao admin_footer que apenas apaga os erros.

Eu não esperava que o tratamento de erros para algo tão comum (atualizando postagens) fosse tão desajeitado. Estou perdendo algo óbvio ou essa é a melhor abordagem?

// Handle post updating
function wpse_5102_update_post_custom_values($post_id, $post) {

    // To keep the errors in
    $errors = false;

    // Do some validation...
    if($_POST['subhead'] != 'value i expect') {

        // Add an error here
        $errors .= 'whoops...there was an error.';

    }

    update_option('my_admin_errors', $errors);

    return;

} 
add_action('save_post','wpse_5102_update_post_custom_values',1,2);


// Display any errors
function wpse_5102_admin_notice_handler() {

    $errors = get_option('my_admin_errors');

    if($errors) {

        echo '<div class="error"><p>' . $errors . '</p></div>';

    }   

}
add_action( 'admin_notices', 'wpse_5102_admin_notice_handler' );


// Clear any errors
function wpse_5102__clear_errors() {

    update_option('my_admin_errors', false);

}
add_action( 'admin_footer', 'wpse_5102_clear_errors' );
MathSmath
fonte
Boa pergunta. Acho que você pode se livrar do admin_footergancho se limpar os erros no final da função do manipulador de avisos. Simplifica as coisas um pouco.
Geert
Como você está lidando com o preenchimento novamente dos campos do formulário (com os possíveis dados inválidos)?
Geert
Eu tenho uma pergunta básica. Que arquivo php Wordpress está presente?
@ Karen Isso seria em um arquivo de plug-in personalizado ou no seu functions.php.
MathSmath
Talvez eu esteja perdendo algo óbvio, mas seria um pouco mais eficiente executar update_option('my_admin_errors', false);imediatamente após a instrução if no final de wpse_5102_admin_notice_handler()?
Andrew Odri

Respostas:

6

Armazene erros em sua classe ou como global, possivelmente em um transitório ou meta, e exiba-os em avisos de administrador em solicitações POST. O WP não possui nenhum manipulador de mensagens flash.

Denis de Bernardy
fonte
Obrigado por me apontar nessa direção! Acabei usando uma meta para armazenar erros, porque tive problemas ao tentar fazer isso como uma propriedade global ou. Estou atualizando minha resposta agora para explicar como estou fazendo isso ... por favor, deixe-me saber se este é o tipo de coisa que você estava sugerindo ou se existe uma maneira melhor de não estar entendendo.
MathSmath
Esse tipo de coisa, sim. Talvez armazene-o em uma variável de sessão, pensando melhor. Isso, para permitir que vários autores editem postagens ao mesmo tempo. :-) Além disso, acredito que não é possível armazenar false em uma opção. Armazene uma sequência vazia.
Denis de Bernardy
6

Sugiro usar sessões, pois isso não criará efeitos estranhos quando dois usuários editarem ao mesmo tempo. Então é isso que eu faço:

As sessões não são iniciadas pelo wordpress. Então você precisa iniciar uma sessão no seu plugin, functions.php ou mesmo wp-config.php:

if (!session_id())
  session_start();

Ao salvar a postagem, anexe erros e avisos à sessão:

function my_save_post($post_id, $post) {
   if($something_went_wrong) {
     //Append error notice if something went wrong
     $_SESSION['my_admin_notices'] .= '<div class="error"><p>This or that went wrong</p></div>';
     return false; //might stop processing here
   }
   if($somthing_to_notice) {  //i.e. successful saving
     //Append notice if something went wrong
     $_SESSION['my_admin_notices'] .= '<div class="updated"><p>Post updated</p></div>';
   }

   return true;
} 
add_action('save_post','my_save_post');

Imprima avisos e erros e limpe as mensagens na sessão:

function my_admin_notices(){
  if(!empty($_SESSION['my_admin_notices'])) print  $_SESSION['my_admin_notices'];
  unset ($_SESSION['my_admin_notices']);
}
add_action( 'admin_notices', 'my_admin_notices' );
davidn
fonte
. correção para a versão sessão: na primeira vez de usar a variável de sessão não use = only = se você ligar a depuração, você pode verificar o porquê ...
3
Também tenho feito isso, mas se você lançar um plug-in para um público amplo como esse, as pessoas acabarão odiando você por isso. O Wordpress não instancia sessões, porque foi projetado para ser sem estado e não precisa delas, e algumas configurações estranhas de servidor o quebram. Use a API de transientes - codex.wordpress.org/Transients_API em vez de sessões e você manterá a compatibilidade. Só pensei que valia a pena sinalizar uma razão pela qual não fazer isso aqui.
pospi
@pospi, isso parece ter problemas semelhantes ao uso original das funções get_option e update_option. Então, acho que a solução seria anexar o ID do usuário atual à chave?
Gazillion
Sim, isso funcionaria totalmente! Contanto que você adicione algo para identificar exclusivamente o usuário, evitará que mensagens sejam confundidas entre usuários logados (:
pospi 28/10/2013
5

Baseado em pospi 's sugestão para uso transientes , eu vim com o seguinte. O único problema é que não há gancho para colocar a mensagem abaixo de h2onde outras mensagens vão, então eu tive que fazer um hack do jQuery para chegar lá.

Primeiro, salve a mensagem de erro durante o manipulador save_post(ou similar). Eu concedo um tempo de vida curto de 60 segundos, portanto, existe apenas o tempo suficiente para o redirecionamento acontecer.

if($has_error)
{
  set_transient( "acme_plugin_error_msg_$post_id", $error_msg, 60 );
}

Em seguida, basta recuperar a mensagem de erro na próxima página carregada e exibi-la. Também o apago para que não seja exibido duas vezes.

add_action('admin_notices', 'acme_plugin_show_messages');

function acme_plugin_show_messages()
{
  global $post;
  if ( false !== ( $msg = get_transient( "acme_plugin_error_msg_{$post->ID}" ) ) && $msg) {
    delete_transient( "acme_plugin_error_msg_{$post->ID}" );
    echo "<div id=\"acme-plugin-message\" class=\"error below-h2\"><p>$msg</p></div>";
  }
}

Como é admin_noticesacionado antes que o conteúdo da página principal seja gerado, o aviso não é para onde as outras mensagens de pós-edição vão, então eu tive que usar esse jQuery para movê-lo para lá:

jQuery('h2').after(jQuery('#acme-plugin-message'));

Como o ID da postagem faz parte do nome transitório, isso deve funcionar na maioria dos ambientes multiusuário, exceto quando vários usuários estão editando simultaneamente a mesma postagem.

Joshua Coady
fonte
Você poderia elaborar "Como o ID da postagem faz parte do nome transitório"? Criei uma classe para lidar com mensagens de erro usando essa técnica, mas exijo que meu construtor passe um ID do usuário. A API transitória usa o user_id ao fazer o hash da chave? (Eu pergunto porque o códice não parece mencionar isso)
Gazillion
Não, mas você pode adicioná-lo manualmente. No código que eu postei acima, o nome do transitório é acme_plugin_error_msg_POSTID. Você pode apenas adicionar o ID do usuário a esse gosto acme_plugin_error_msg_POSTID_USERID.
Joshua Coady
2

Quando save_postexecutado, ele já salvou a postagem no banco de dados.

Analisando o código principal do WordPress, mais especificamente no wp-includes/post.php 's update_post()função, não há built-in maneira de interceptar um pedido antes de ser salvo no banco de dados.

No entanto, podemos conectar pre_post_updatee usar header()e get_post_edit_link()impedir que a postagem seja salva.

<?php

/**
*   Performs validation before saving/inserting custom post type
*/
function custom_post_site_save($post_id, $post_data) {
    // If this is just a revision, don't do anything.
    if (wp_is_post_revision($post_id))
        return;

    if ($post_data['post_type'] == 'my_custom_post_type') {
        // Deny post titles with less than 5 characters
        if (strlen($post_data['post_title'] < 5)) {
            header('Location: '.get_edit_post_link($post_id, 'redirect'));
            exit;
        }
    }
}
add_action( 'pre_post_update', 'custom_post_site_save', 10, 2);

Se você notificar o usuário sobre o erro, verifique esta lista: https://gist.github.com/Luc45/09f2f9d0c0e574c0285051b288a0f935

Lucas Bustamante
fonte
Obrigado por isso, lida perfeitamente com a validação, seja publicando pela primeira vez ou atualizando a postagem. Você acabou de me poupar muito tempo e esforço.
Zade
1

Por que você não valida seu campo com a ajuda de algum Javascript? Eu acho que essa seria a melhor abordagem para isso.

Horttcore
fonte
Obrigado pela sugestão! O que deixei de fora da questão (por uma questão de simplicidade) é que estou tentando lidar com erros de upload de arquivos, portanto, ele precisa ser do lado do servidor. Obrigado pela sugestão!
MathSmath
A validação de javascript não impede alguns ataques, a validação no servidor é a única segura. Além disso, o wordpress oferece algumas boas ferramentas para validar dados do usuário. Mas você está certo se basta verificar alguns valores antes de enviar dados para o servidor, você pode economizar algum tempo em baixa servidor ^^
nderambure
1

Tentando usar o script acima, encontrei um problema estranho. Duas mensagens são mostradas na tela de edição, após a atualização posterior. Um está mostrando o estado do conteúdo do salvamento anterior e outro do atual. Por exemplo, se eu salvar a postagem corretamente e depois cometer um erro, a primeira é "erro" e a segunda é "ok" - embora elas sejam geradas ao mesmo tempo. Se eu alterar o script e anexar apenas uma mensagem (por exemplo, "erro"), inicie uma atualização com "erro" e depois outra com "ok", a mensagem "erro" permanece (é exibida pela segunda vez). Devo salvar com "ok" mais uma vez para me livrar dele. Realmente não sei o que há de errado, testei-o em três servidores locais diferentes e há o mesmo problema em cada um deles.

jlub
fonte
Fiz mais alguns testes da segunda versão mais simples do script que mencionei acima e parece que se a mensagem de "erro" estiver realmente anexada à matriz da sessão, ela será exibida na tela de edição. Se não houver mensagem (tudo está "ok") e a mensagem anterior foi um erro, ela aparece na tela. O que é estranho, é gerado na hora de salvar (não em cache) - verifiquei usando date () no corpo da mensagem de erro. Estou totalmente confuso agora.
jlub
Ok, caso alguém mais esteja arrancando o cabelo da cabeça - o sistema de revisões do Wordpress foi o problema (provavelmente algum tipo de bug?). Eu desativei e agora está tudo bem.
0

Eu escrevi um plug-in que adiciona um tratamento de erro em flash às telas de pós-edição e evita a publicação de postagens até que os campos obrigatórios sejam preenchidos:

https://github.com/interconnectit/required-fields

Ele permite a obrigatoriedade de qualquer campo de postagem, mas você pode usar a API fornecida para tornar os campos personalizados necessários também com uma mensagem de erro personalizável e uma função de validação. O padrão é verificar se o campo está vazio ou não.

sanchothefat
fonte
Não hesite em adicionar problemas no github, se você os encontrar. Também preciso documentar um pouco melhor a API, pois existem alguns filtros adicionais que você pode usar.
21712 Sanchothefat