Empêcher la publication de la publication si les champs personnalisés ne sont pas remplis

17

J'ai un type de publication personnalisé Eventqui contient des champs personnalisés de date / heure de début et de fin (sous forme de métaboxes dans l'écran d'édition de publication).

Je voudrais m'assurer qu'un événement ne peut pas être publié (ou programmé) sans que les dates soient remplies, car cela entraînera des problèmes avec les modèles affichant les données de l'événement (en plus du fait que c'est une condition nécessaire!). Cependant, je souhaiterais pouvoir disposer de projets d'événements qui ne contiennent pas de date valide pendant leur préparation.

Je pensais accrocher save_postpour faire la vérification, mais comment puis-je empêcher le changement de statut de se produire?

EDIT1: Ceci est le crochet que j'utilise maintenant pour enregistrer le 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: et c'est ce que j'essaie d'utiliser pour vérifier les données de publication après l'enregistrement dans la base de données.

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;
    }
}
}

Le principal problème est un problème qui a été décrit dans une autre question : l'utilisation wp_update_post()dans un save_postcrochet déclenche une boucle infinie.

EDIT3: J'ai trouvé un moyen de le faire, en accrochant wp_insert_post_dataau lieu de save_post. Le seul problème est que maintenant le post_statusest inversé, mais maintenant un message trompeur disant "Publication publiée" apparaît (en ajoutant &message=6à l'URL redirigée), mais le statut est défini sur Brouillon.

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;
}
englebip
la source

Réponses:

16

Comme l'a souligné m0r7if3r, il n'y a aucun moyen d' empêcher la publication d'un message à l'aide du save_posthook, car au moment où ce hook est déclenché, le message est déjà enregistré. Cependant, ce qui suit vous permettra de revenir à l'état sans utiliser wp_insert_post_dataet sans provoquer une boucle infinie.

Ce qui suit n'est pas testé, mais devrait fonctionner.

<?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');
    }
}
?>

Je n'ai pas vérifié, mais en regardant le code, le message de rétroaction affichera le message incorrect que la publication a été publiée. C'est parce que WordPress nous redirige vers une URL où la messagevariable est maintenant incorrecte.

Pour le changer, nous pouvons utiliser le redirect_post_locationfiltre:

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;
}

Pour résumer le filtre de redirection ci-dessus: Si un article est configuré pour être publié, mais qu'il s'agit toujours d'un brouillon, nous modifions le message en conséquence (ce qui est le cas message=10). Encore une fois, ce n'est pas testé, mais devrait fonctionner. Le Codex du add_query_argsuggère que lorsqu'une variable est déjà définie, la fonction la remplace (mais comme je l'ai dit, je n'ai pas testé cela).

Stephen Harris
la source
Autre que les disparus; sur votre ligne add_query_arg, cette astuce de filtre redirect_post_location est exactement ce dont j'avais besoin. Merci!
MadtownLems
@MadtownLems fixed :)
Stephen Harris
9

OK, c'est finalement ainsi que j'ai fini par le faire: un appel Ajax à une fonction PHP qui vérifie, en quelque sorte inspiré par cette réponse et en utilisant un astuce intelligent d'une question que j'ai posée sur StackOverflow . Surtout, je m'assure que ce n'est que lorsque nous voulons publier que la vérification est effectuée, afin qu'un brouillon puisse toujours être enregistré sans la vérification. Cela a fini par être la solution la plus simple pour empêcher la publication du message. Cela pourrait aider quelqu'un d'autre, alors je l'ai écrit ici.

Tout d'abord, ajoutez le Javascript nécessaire:

//AJAX to validate event before publishing
//adapted from /wordpress/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
}
}

Ensuite, la fonction qui gère la vérification:

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 /wordpress//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();
}

Cette fonction retourne truesi tout va bien et soumet le formulaire pour publier le message par le canal normal. Sinon, la fonction renvoie un message d'erreur qui s'affiche sous alert()la forme d' un et le formulaire n'est pas soumis.

englebip
la source
J'ai suivi la même approche et obtenu que le message soit enregistré en tant que "Brouillon" au lieu de "Publier" lorsque la fonction de validation renvoie true. Vous ne savez pas comment résoudre ce problème!
Mahmudur
1
J'ai appliqué cette solution un peu différemment: tout d'abord j'ai utilisé le code ci-dessous dans le javascript en cas de succès: delayed_autosave(); //get data from textarea/tinymce field jQuery('#publish').data("valid", true).trigger('click'); //publish postMerci beaucoup.
Mahmudur
3

Je pense que la meilleure façon de procéder est de ne pas empêcher le changement de statut de se produire autant que de le RÉVERVER s'il le fait. Par exemple: vous accrochez save_post, avec une priorité très élevée (pour que le crochet se déclenche très tard, c'est-à-dire après avoir fait votre méta-insert), puis vérifiez post_statusle message qui vient d'être enregistré, et mettez-le à jour en attente (ou brouillon ou peu importe) s'il ne répond pas à vos critères.

Une autre stratégie consisterait à accrocher wp_insert_post_datapour définir directement le post_status. L'inconvénient de cette méthode, en ce qui me concerne, est que vous n'aurez pas encore inséré le postmeta dans la base de données, vous devrez donc le traiter, etc. en place pour faire vos vérifications, puis le traiter à nouveau pour insérer il dans la base de données ... qui pourrait devenir beaucoup de frais généraux, soit en performances ou en code.

mor7ifer
la source
Je suis actuellement accroché save_postà la priorité 1 pour enregistrer les champs méta des métaboxes; ce que vous proposez alors, c'est d'avoir un deuxième crochet save_postavec la priorité, disons, 99? Cela garantirait-il l'intégrité? Que se passe-t-il si, pour une raison quelconque, le premier crochet se déclenche, les métadonnées sont insérées et la publication publiée, mais pas le deuxième crochet, vous vous retrouvez donc avec des champs invalides?
englebip
Je ne peux pas imaginer une situation où le premier crochet se déclencherait mais pas le second ... quel genre de scénario pensez-vous qui pourrait provoquer cela? Si cela vous inquiète, vous pouvez insérer la méta post, vérifier la méta post, puis mettre à jour la fonction post_statustout en un à partir d'un seul appel vers un hook si vous préférez.
mor7ifer
J'ai publié mon code en tant que modification de ma question; J'ai essayé d'utiliser un deuxième crochet pour save_postdéclencher une boucle infinie.
englebip
Votre problème est que vous devriez vérifier la publication créée. C'est if( get_post_status( $post_id ) == 'publish' )ce que vous voulez utiliser, car vous redéfinirez les données dans $wpdb->posts, pas les données dans $_POST[].
mor7ifer
0

La meilleure méthode peut être 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>
T.Todua
la source
-1

Désolé, je ne peux pas vous donner une réponse directe, mais je me souviens d'avoir fait quelque chose de similaire très récemment, je ne peux pas me rappeler exactement comment. Je pense que je l'ai peut-être fait sur le chemin - quelque chose comme si je l'avais comme valeur par défaut et si la personne ne l'avait pas changé, je l'ai ramassé dans une instruction if donc -> if(category==default category) {echo "You didn't pick a category!"; return them to the post creation page; }désolé, ce n'est pas une réponse directe mais j'espère ça aide un peu.

MIINIIM
la source