Prevent post from being published if custom fields not filled

As m0r7if3r pointed out, there is no way of preventing a post from being published using the save_post hook, since the by the time that hook is fired, the post is already saved. The following, however, will allow you to revert the status without using wp_insert_post_data and without causing an infinite loop.

The following is not tested, but should work.

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

I’ve not checked, but looking at the code, the feedback message will display the incorrect message that the post was published. This is because WordPress redirects us to an url where the message variable is now incorrect.

To change it, we can use the redirect_post_location filter:

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

To summarise the above redirect filter: If a post is set to be published, but is still a draft then we alter the message accordingly (which is message=10). Again, this is untested, but should work. The Codex of the add_query_arg suggests that when a variable is already it set, the function replaces it (but as I say, I haven’t tested this).

Leave a Comment