Impede que o post seja publicado se os campos personalizados não forem preenchidos

14

Eu tenho um tipo de postagem personalizado Event que contém campos personalizados de data e hora de início e término (como metaboxes na tela de edição de postagens).

Eu gostaria de ter certeza de que um evento não pode ser publicado (ou agendado) sem as datas sendo preenchidas, pois isso causará problemas com os modelos que exibem os dados do evento (além do fato de que é um requisito necessário!). No entanto, gostaria de poder ter eventos de rascunho que não contenham uma data válida enquanto estiverem em preparação.

Eu estava pensando em ligar save_post para fazer a verificação, mas como posso evitar que a mudança de status aconteça?

EDIT1: Este é o gancho que estou usando agora para salvar o post_meta.

// Save the Metabox Data
function ep_eventposts_save_meta( $post_id, $post ) {

if ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE )
    return;

if ( !isset( $_POST['ep_eventposts_nonce'] ) )
    return;

if ( !wp_verify_nonce( $_POST['ep_eventposts_nonce'], plugin_basename( __FILE__ ) ) )
    return;

// Is the user allowed to edit the post or page?
if ( !current_user_can( 'edit_post', $post->ID ) )
    return;

// OK, we're authenticated: we need to find and save the data
// We'll put it into an array to make it easier to loop though

//debug
//print_r($_POST);

$metabox_ids = array( '_start', '_end' );

foreach ($metabox_ids as $key ) {
    $events_meta[$key . '_date'] = $_POST[$key . '_date'];
    $events_meta[$key . '_time'] = $_POST[$key . '_time'];
    $events_meta[$key . '_timestamp'] = $events_meta[$key . '_date'] . ' ' . $events_meta[$key . '_time'];
}

$events_meta['_location'] = $_POST['_location'];

if (array_key_exists('_end_timestamp', $_POST))
    $events_meta['_all_day'] = $_POST['_all_day'];

// Add values of $events_meta as custom fields

foreach ( $events_meta as $key => $value ) { // Cycle through the $events_meta array!
    if ( $post->post_type == 'revision' ) return; // Don't store custom data twice
    $value = implode( ',', (array)$value ); // If $value is an array, make it a CSV (unlikely)
    if ( get_post_meta( $post->ID, $key, FALSE ) ) { // If the custom field already has a value
        update_post_meta( $post->ID, $key, $value );
    } else { // If the custom field doesn't have a value
        add_post_meta( $post->ID, $key, $value );
    }
    if ( !$value ) 
                delete_post_meta( $post->ID, $key ); // Delete if blank
}

}

add_action( 'save_post', 'ep_eventposts_save_meta', 1, 2 );

EDIT2: e é isso que estou tentando usar para verificar os dados do post depois de salvá-los no banco de dados.

add_action( 'save_post', 'ep_eventposts_check_meta', 99, 2 );
function ep_eventposts_check_meta( $post_id, $post ) {
//check that metadata is complete when a post is published
//print_r($_POST);

if ( $_POST['post_status'] == 'publish' ) {

    $custom = get_post_custom($post_id);

    //make sure both dates are filled
    if ( !array_key_exists('_start_timestamp', $custom ) || !array_key_exists('_end_timestamp', $custom )) {
        $post->post_status = 'draft';
        wp_update_post($post);

    }
    //make sure start < end
    elseif ( $custom['_start_timestamp'] > $custom['_end_timestamp'] ) {
        $post->post_status = 'draft';
        wp_update_post($post);
    }
    else {
        return;
    }
}
}

O principal problema é um problema que foi realmente descrito em outra pergunta : usar wp_update_post() dentro de um save_post hook aciona um loop infinito.

EDIT3: achei uma maneira de fazer isso, conectando wp_insert_post_data em vez de save_post . O único problema é que agora o post_status é revertido, mas agora aparece uma mensagem enganosa dizendo "Publicar publicado" (adicionando &message=6 ao URL redirecionado), mas o status está definido como Rascunho.

add_filter( 'wp_insert_post_data', 'ep_eventposts_check_meta', 99, 2 );
function ep_eventposts_check_meta( $data, $postarr ) {
//check that metadata is complete when a post is published, otherwise revert to draft
if ( $data['post_type'] != 'event' ) {
    return $data;
}
if ( $postarr['post_status'] == 'publish' ) {
    $custom = get_post_custom($postarr['ID']);

    //make sure both dates are filled
    if ( !array_key_exists('_start_timestamp', $custom ) || !array_key_exists('_end_timestamp', $custom )) {
        $data['post_status'] = 'draft';
    }
    //make sure start < end
    elseif ( $custom['_start_timestamp'] > $custom['_end_timestamp'] ) {
        $data['post_status'] = 'draft';
    }
    //everything fine!
    else {
        return $data;
    }
}

return $data;
}
    
por englebip 12.02.2012 / 00:31

5 respostas

9

OK, finalmente é assim que acabei fazendo isso: uma chamada Ajax para uma função PHP que faz a verificação, meio que inspirada por esta resposta e usando uma dica inteligente de uma pergunta que eu perguntei no StackOverflow . Importante, eu me certifico de que somente quando queremos publicar a verificação é feita, para que um rascunho sempre pode ser salvo sem a verificação. Isso acabou sendo a solução mais fácil para impedir a publicação do post. Pode ajudar alguém, então eu escrevi aqui.

Primeiro, adicione o Javascript necessário:

//AJAX to validate event before publishing
//adapted from https://wordpress.stackexchange.com/questions/15546/dont-publish-custom-post-type-post-if-a-meta-data-field-isnt-valid
add_action('admin_enqueue_scripts-post.php', 'ep_load_jquery_js');   
add_action('admin_enqueue_scripts-post-new.php', 'ep_load_jquery_js');   
function ep_load_jquery_js(){
global $post;
if ( $post->post_type == 'event' ) {
    wp_enqueue_script('jquery');
}
}

add_action('admin_head-post.php','ep_publish_admin_hook');
add_action('admin_head-post-new.php','ep_publish_admin_hook');
function ep_publish_admin_hook(){
global $post;
if ( is_admin() && $post->post_type == 'event' ){
    ?>
    <script language="javascript" type="text/javascript">
        jQuery(document).ready(function() {
            jQuery('#publish').click(function() {
                if(jQuery(this).data("valid")) {
                    return true;
                }
                var form_data = jQuery('#post').serializeArray();
                var data = {
                    action: 'ep_pre_submit_validation',
                    security: '<?php echo wp_create_nonce( 'pre_publish_validation' ); ?>',
                    form_data: jQuery.param(form_data),
                };
                jQuery.post(ajaxurl, data, function(response) {
                    if (response.indexOf('true') > -1 || response == true) {
                        jQuery("#post").data("valid", true).submit();
                    } else {
                        alert("Error: " + response);
                        jQuery("#post").data("valid", false);

                    }
                    //hide loading icon, return Publish button to normal
                    jQuery('#ajax-loading').hide();
                    jQuery('#publish').removeClass('button-primary-disabled');
                    jQuery('#save-post').removeClass('button-disabled');
                });
                return false;
            });
        });
    </script>
    <?php
}
}

Em seguida, a função que lida com a verificação:

add_action('wp_ajax_ep_pre_submit_validation', 'ep_pre_submit_validation');
function ep_pre_submit_validation() {
//simple Security check
check_ajax_referer( 'pre_publish_validation', 'security' );

//convert the string of data received to an array
//from https://wordpress.stackexchange.com/a/26536/10406
parse_str( $_POST['form_data'], $vars );

//check that are actually trying to publish a post
if ( $vars['post_status'] == 'publish' || 
    (isset( $vars['original_publish'] ) && 
     in_array( $vars['original_publish'], array('Publish', 'Schedule', 'Update') ) ) ) {
    if ( empty( $vars['_start_date'] ) || empty( $vars['_end_date'] ) ) {
        _e('Both Start and End date need to be filled');
        die();
    }
    //make sure start < end
    elseif ( $vars['_start_date'] > $vars['_end_date'] ) {
        _e('Start date cannot be after End date');
        die();
    }
    //check time is also inputted in case of a non-all-day event
    elseif ( !isset($vars['_all_day'] ) ) {
        if ( empty($vars['_start_time'] ) || empty( $vars['_end_time'] ) ) {
            _e('Both Start time and End time need to be specified if the event is not an all-day event');
            die();              
        }
        elseif ( strtotime( $vars['_start_date']. ' ' .$vars['_start_time'] ) > strtotime( $vars['_end_date']. ' ' .$vars['_end_time'] ) ) {
            _e('Start date/time cannot be after End date/time');
            die();
        }
    }
}

//everything ok, allow submission
echo 'true';
die();
}

Esta função retorna true se tudo estiver bem, e submete o formulário para publicar a postagem pelo canal normal. Caso contrário, a função retornará uma mensagem de erro mostrada como alert() e o formulário não será enviado.

    
por englebip 17.02.2012 / 18:31
13

Como m0r7if3r apontou, não há nenhuma maneira de impedir que uma publicação seja publicada usando o save_post hook, já que quando o hook é disparado, o post já está salvo. O seguinte, no entanto, permitirá que você reverta o status sem usar wp_insert_post_data e sem causar um loop infinito.

O seguinte não foi testado, mas deve funcionar.

<?php
add_action('save_post', 'my_save_post');
function my_save_post($post_id) {
    if ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE )
         return;

    if ( !isset( $_POST['ep_eventposts_nonce'] ) )
         return;

    if ( !wp_verify_nonce( $_POST['ep_eventposts_nonce'], plugin_basename( __FILE__ ) ) )
         return;

    // Is the user allowed to edit the post or page?
     if ( !current_user_can( 'edit_post', $post->ID ) )
         return;

   // Now perform checks to validate your data. 
   // Note custom fields (different from data in custom metaboxes!) 
   // will already have been saved.
    $prevent_publish= false;//Set to true if data was invalid.
    if ($prevent_publish) {
        // unhook this function to prevent indefinite loop
        remove_action('save_post', 'my_save_post');

        // update the post to change post status
        wp_update_post(array('ID' => $post_id, 'post_status' => 'draft'));

        // re-hook this function again
        add_action('save_post', 'my_save_post');
    }
}
?>

Eu não verifiquei, mas olhando o código, a mensagem de feedback exibirá a mensagem incorreta de que a postagem foi publicada. Isso ocorre porque o WordPress nos redireciona para uma URL em que a variável message está incorreta.

Para alterá-lo, podemos usar o filtro redirect_post_location :

add_filter('redirect_post_location','my_redirect_location',10,2);
function my_redirect_location($location,$post_id){
    //If post was published...
    if (isset($_POST['publish'])){
        //obtain current post status
        $status = get_post_status( $post_id );

        //The post was 'published', but if it is still a draft, display draft message (10).
        if($status=='draft')
            $location = add_query_arg('message', 10, $location);
    }

    return $location;
}

Para resumir o filtro de redirecionamento acima: Se uma postagem estiver definida para ser publicada, mas ainda for um rascunho, alteramos a mensagem de acordo (que é message=10 ). Novamente, isso não foi testado, mas deve funcionar. O Codex do add_query_arg sugere que quando uma variável já está definida, a função a substitui (mas como eu digo , Eu não testei isso).

    
por Stephen Harris 13.02.2012 / 17:04
3

Eu acho que a melhor maneira de fazer isso não é para evitar que a mudança de status aconteça, mas sim para REVERTER se isso acontecer. Por exemplo: Você conecta save_post , com uma prioridade muito alta (para que o gancho seja acionado muito tarde, ou seja, depois de fazer sua meta-inserção), verifique o post_status da postagem que acabou de ser salva e atualize-o para pendente (ou rascunho ou qualquer outra coisa) se não atender aos seus critérios.

Uma estratégia alternativa seria ligar wp_insert_post_data para definir o post_status diretamente. A desvantagem deste método, no que me diz respeito, é que você ainda não inseriu a postmeta no banco de dados, então você terá que processá-la, etc para fazer suas verificações, e então processá-la novamente para inserir no banco de dados ... o que pode se tornar muito sobrecarga, seja no desempenho ou no código.

    
por mor7ifer 12.02.2012 / 00:53
0

O melhor método pode ser JAVASCRIPT:

<script type="text/javascript">
var field_id =  "My_field_div__ID";    // <----------------- CHANGE THIS

var SubmitButton = document.getElementById("save-post") || false;
var PublishButton = document.getElementById("publish")  || false; 
if (SubmitButton)   {SubmitButton.addEventListener("click", SubmCLICKED, false);}
if (PublishButton)  {PublishButton.addEventListener("click", SubmCLICKED, false);}
function SubmCLICKED(e){   
  var passed= false;
  if(!document.getElementById(field_id)) { alert("I cant find that field ID !!"); }
  else {
      var Enabled_Disabled= document.getElementById(field_id).value;
      if (Enabled_Disabled == "" ) { alert("Field is Empty");   }  else{passed=true;}
  }
  if (!passed) { e.preventDefault();  return false;  }
}
</script>
    
por T.Todua 26.06.2015 / 12:56
-1

Desculpe eu não posso lhe dar uma resposta direta, mas eu me lembro de fazer algo semelhante, muito recentemente, eu apenas não consigo lembrar exatamente como. Eu acho que talvez tenha feito isso em torno de maneira - algo como eu tinha que ser um valor padrão e se a pessoa não tivesse mudado, eu peguei isso em uma declaração se assim - > if(category==default category) {echo "You didn't pick a category!"; return them to the post creation page; } desculpe, isso não é uma resposta direta, mas espero que ajude um pouco.

    
por MIINIIM 12.02.2012 / 02:20