Take the 2-minute tour ×
WordPress Development Stack Exchange is a question and answer site for WordPress developers and administrators. It's 100% free, no registration required.

I have a custom post type (CPT) called event. I have a meta box for the type with several fields. I would like to validate some fields before publishing an event. For example, if an event's date is not specified I would like to display an informative error message, save the event for future editing, but prevent that event from being published. Is 'pending' status for an CPT post without all necessary info the right way to treat it?

What's the best practice to do CPT fields validation and prevent a post from being published, but save it for future editing.

Many thanks, Dasha

share|improve this question
    
Gentle nudge to remind you this question still needs an accepted answer.. ;) If neither answer addressed your question please consider updating your question with additional comments detailing what has not been addressed(or where you may need help, if applicable). –  t31os Jul 28 '11 at 9:14

3 Answers 3

up vote 7 down vote accepted

You can stop the post from saving all together with minor JQuery hacks and validate the fields before saving on the client side or server side with ajax:

first we add our JavaScript to capture the submit/publish event and use it to submit our own ajax function before the actual submit:

 add_action('wp_print_scripts','my_publish_admin_hook');

function my_publish_admin_hook(){
if (is_admin()){
        ?>
        <script language="javascript" type="text/javascript">
            jQuery(document).ready(function() {
                jQuery('#post').submit(function() {

                    var form_data = jQuery('#post').serializeArray();
                    form_data = jQuery.param(form_data);
                    var data = {
                        action: 'my_pre_submit_validation',
                        security: '<?php echo wp_create_nonce( 'pre_publish_validation' ); ?>',
                        form_data: form_data
                    };
                    jQuery.post(ajaxurl, data, function(response) {
                        if (response.indexOf('True') > -1 || response.indexOf('true') > -1 || response = true ||  response) {
                            jQuery('#ajax-loading').hide();
                            jQuery('#publish').removeClass('button-primary-disabled');
                            return true;
                        }else{
                            alert("please correct the following errors: " + response);
                            jQuery('#ajax-loading').hide();
                            jQuery('#publish').removeClass('button-primary-disabled');
                            return false;
                        }
                    });
                    return false;
                });
            });
        </script>
        <?php
    }
}

then we create the function to do the actual validation:

add_action('wp_ajax_my_pre_submit_validation', 'pre_submit_validation');
function pre_submit_validation(){
    //simple Security check
    check_ajax_referer( 'pre_publish_validation', 'security' );

    //do your validation here
    //all of the form fields are in $_POST['form_data'] array
    //and return true to submit: echo 'true'; die();
    //or your error message: echo 'bal bla bla'; die();
}

you can always change it up a bit to do the validation only for your post type by adding a conditional check to my_publish_admin_hook function for your post type and to validate on the client side but i prefer on the server side.

share|improve this answer
    
Is there no server-side way to do this? –  Jeff Jul 4 at 21:58
    
This is a server side way to do it. –  Bainternet Jul 5 at 4:59
    
Maybe I am misunderstanding something. It looks like you're just using PHP to render JavaScript which does the validation. That isn't server side validation. I'm not really understanding how the pre_submit_validation fits in. –  Jeff Jul 7 at 16:42

Just wanted to add that to read the post variables, using Bainternet's solution, you'll have to parse the string in $_POST['form_data'] using PHP parse_str function (just to save you some research time).

$vars = parse_str( $_POST['form_data'] );

Then you can access each variable just using $varname. For example, if you have a meta field called "my_meta" you would access it like this:

$vars = parse_str ( $_POST['form_data'] ) 
if ( $my_meta == "something" ) { // do something }
share|improve this answer

There are two steps to the method: first, a function to save your custom metabox field data (hooked to save_post), and second, a function to read that new post_meta (which you just saved), validate it, and modify the result of saving as necessary (also hooked to save_post, but after the first). The validator function, if validation fails, actually changes the post_status right back to "pending", effectively preventing the post from being published.

Since the save_post function gets called a lot, each function has checks to only execute when the user means to publish, and only for your custom post type (mycustomtype).

I also typically add some custom notice messages to help the user know why their post didn't publish, but those got a bit complicated to include here...

I haven't tested this exact code, but it's a simplified version of what I've done in large-scale custom post type setups.

add_action('save_post', 'save_my_fields', 10, 2);
add_action('save_post', 'completion_validator', 20, 2);

function save_my_fields($pid, $post) {
    // don't do on autosave or when new posts are first created
    if ( ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE ) || $post->post_status == 'auto-draft' ) return $pid;
    // abort if not my custom type
    if ( $post->post_type != 'mycustomtype' ) return $pid;

    // save post_meta with contents of custom field
    update_post_meta($pid, 'mymetafield', $_POST['mymetafield']);
}


function completion_validator($pid, $post) {
    // don't do on autosave or when new posts are first created
    if ( ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE ) || $post->post_status == 'auto-draft' ) return $pid;
    // abort if not my custom type
    if ( $post->post_type != 'mycustomtype' ) return $pid;

    // init completion marker (add more as needed)
    $meta_missing = false;

    // retrieve meta to be validated
    $mymeta = get_post_meta( $pid, 'mymetafield', true );
    // just checking it's not empty - you could do other tests...
    if ( empty( $mymeta ) ) {
        $meta_missing = true;
    }

    // on attempting to publish - check for completion and intervene if necessary
    if ( ( isset( $_POST['publish'] ) || isset( $_POST['save'] ) ) && $_POST['post_status'] == 'publish' ) {
        //  don't allow publishing while any of these are incomplete
        if ( $meta_missing ) {
            global $wpdb;
            $wpdb->update( $wpdb->posts, array( 'post_status' => 'pending' ), array( 'ID' => $pid ) );
            // filter the query URL to change the published message
            add_filter( 'redirect_post_location', create_function( '$location','return add_query_arg("message", "4", $location);' ) );
        }
    }
}

For multiple metabox fields, just add more completion markers and retrieve more post_meta and do more tests..

share|improve this answer

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.