6

I'm trying to do some server side validation of post fields (custom and/or non-custom fields). Validation works just fine, but I can't seem to stop the post from saving if validation fails.

I tried hooks like save_post, publish_post, wp_insert_post_data, etc. All of these hooks are being called but I can't stop the post from saving unless I use wp_die(). To be clear, I do not want to change the post status; I just want to return to the post page (unsaved) and show a message when the validation of a field fails.

I think this is pretty basic stuff, but whatever I do the post is being saved.

UPDATE:

OK, so the pre_post_update hook is an option. However, on a validation failure I need to use wp_redirect to get back to the edit page without saving. I'm storing the invalidation msgs and showing them on the page, but this way all the $_POST data is gone... which of course is not acceptable. Is there really no other way to do this?

Here's my code so far:

add_action('pre_post_update', 'validate_meta', 99, 2);
function validate_meta($post_id){

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

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

    if ( !current_user_can( 'edit_posts', $post_id ) ) return $post_id;

    $errors = array();

    if(trim($_POST['post_title']) == ''){
        $errors[] = new WP_Error('cpt_validation_titke',  'Bitte einen Titel eintragen', 'error');
    }

    if (!empty($errors)) {
        add_user_meta(get_current_user_id(), 'admin_notices', $errors, true);
        $url = admin_url( 'post.php?post=' . $post_id ) . '&action=edit';
        //exit;
        wp_redirect( $url );
        exit;     
    }
    return $_POST;
}

// Display any errors
add_action( 'admin_notices', 'admin_notice_handler' );
function admin_notice_handler() {
    $user_id = get_current_user_id();
    $admin_notices = get_user_meta($user_id, 'admin_notices', true);

    if(!empty($admin_notices)){
        $html = '';

        if(is_wp_error($admin_notices[0])){

            delete_user_meta($user_id, 'admin_notices');

            foreach($admin_notices AS $notice){

                $msgs = $notice->get_error_messages();

                if(!empty($msgs)){
                    $msg_type = $notice->get_error_data();
                    if(!empty($notice_type)){
                        $html .= '<div class="'.$msg_type.'">';
                    } else {                    
                        $html .= '<div class="error">';
                        $html .= '<p><strong>Validation errors</strong></p>';
                    }

                    foreach($msgs as $msg){
                        $html .= '<p>- '.$msg.'</p>';
                    }                    
                    $html .= '</div>';                   
                }
            }
        }

        echo $html;
    }
}
  • Please note that a redirect is not an options since this would all previous post data – john23klipp Nov 26 '14 at 13:28
  • No one any idea? – john23klipp Nov 28 '14 at 16:01
  • Ok, so the pre_post_update hook is an option. However , on a validation failure I need to use wp_redirect to get back to the edit page without saving. I'm storing the invalidation msgs and showing them on the page. BUT, this way all the $_POST data is gone... Which of course is not really acceptable. Is there really no other way for this? – john23klipp Dec 1 '14 at 11:14
  • If any of the answers was helpful to you, then consider accepting one. See »What should I do when someone answers my question?« and/or »Why is voting important?«, more information about the WordPress Development model is available at the help center. – Nicolai Mar 10 '15 at 9:50
1

You can capture the form submission by creating a small plugin / or by editing your theme functions file, and you can do this using a ajax hook. In the plugin you would load this on the edit post page:

jQuery(document).ready(function(){

jQuery('#post').submit(function(){
        var request = jQuery(this).serializeArray();
        request.push({name: 'action', value: 'check_some_stuff'});

        jQuery.post(ajaxurl, request, function(response){
            if(response.error){
                response = jQuery.parseJSON(response);
            jQuery('#local-storage-notice').after('<div class="error">'+response.error+'</div>');
        });
                return false;
        } else {
            return true;
        }
    });

});

When you return false, it will stop the form from submission.

The ajax will call a function check_some_stuff. The request gets send as $_POST. You can then validate server side all you want:

add_action( 'wp_ajax_check_some_stuff', 'check_some_stuff' );

public function check_some_stuff()
{
    //validate some stuff here
    $valid = false;
    if($valid){
        $results = array('msg'=>'You win');
        print json_encode($results);
    } else {
        $results = array('error'=>'<pre>'+print_r($_POST, true)+'</pre>');
        print json_encode($results);
    }
    die();
}

I can go in more detail on how to add the javascript in if you need.

| improve this answer | |
  • Hi paxamus, Thanx for your contribution. However, this is still, in the basis just a client side validation option :) I mean, yes the actual validation happens server side, but making the validation call is just JS. As a general update. I talked to some of the Core WP developers and to be honest they didn't have a full proof solution either. Makes the topic all the more interesting of course! – john23klipp Jul 16 '15 at 9:22
  • +1 for the Ajax-way. @john23klipp that's not a client-side option. You send your form submission via ajax call to SERVER SIDE, check all the data on the server with php and return the results to your frontend as ajax response. In your php function triggered by ajax call (check_some_stuff in the example above), validate all data, and if any check failed - return the error and display it with js. If all checks passed - insert/update a post. This is how it works, I'm surprised that 'some Core devs' couldn't help you with this. – Ihor Vorotnov Jul 18 '15 at 13:49
  • Well, like I said the actual validation in this solution takes place server side. However, the call for validation is a client side method. The validation is as strong as the weakest link! – john23klipp Jul 21 '15 at 9:45
0

The easiest - in my mind - way to achieve this, is to make use of PHP: Sessions, so the $_SESSION global or some thing like »WP Session Manager«. When using sessions you can just add your post data to the session variable before redirecting and have the data available after the redirect.

| improve this answer | |
  • Hi, thanx for the reply. I would have to populate standard fields like the title and content as well though. This feels very devious. Am I the only one to think that the lack of (at least basic) validation support in WordPress is a bit odd? – john23klipp Dec 1 '14 at 16:35
  • I don't know, it isn't so much a WordPress restriction, it depends on PHP. Aside from that, if you want to do this server side - otherwise you can simply do it with jQuery/javascript on the client side -, then you have to transport the information somehow between requests - thats just it. @john23klipp – Nicolai Dec 1 '14 at 16:43
  • I think this is WP related. First things first; client side validation should never be the only form of vaildation for obvious reasons. Normal server side (php in this case) validation of course should do the job. However, as I wrote, I'm having problems canceling the whole saving process in WP. Validating and invalidating fields works fine, but I can't find a good way to cancel the db insert/ update and return to the edit screen. – john23klipp Dec 2 '14 at 8:35
  • Unless I totally misread what you wrote, you have solved the cancellation of the saving process and figured out how to redirect, right? So the only thing left is, not loosing the information/data. @john23klipp – Nicolai Dec 2 '14 at 16:11
  • Well, yes. I have found a solution but it's far from perfect. Because of the wp_redirect I'm loosing all posted data. Of course this can be saved in a session or such, but a much more intuitive and logical solution would be just just return to the page and thus not loosing any data. There must be a way to do this! However, like mentioned returning is not the problem, not saving the data is. – john23klipp Dec 3 '14 at 8:55
0

Autosave is in your way.

How to properly turn off REVISIONS and AUTOSAVE for whole site and optionally for a custom post type only

(I would have simply left a comment but I lack the cred.)

| improve this answer | |
  • Hi chrisdillon, thanx for your reply. However, as you can see in my code example I am checking for autosaves (end if yes, skipping validation). The link you provided describes how to completely turn off autosave and revisions. This would be pretty harsh! I can't imagine that a simple thing as validation is not possible in combination with WP's neat autosave and revision functions. – john23klipp Dec 4 '14 at 8:22
  • You cannot "stop the post from saving if validation fails" and allow autosave at the same time. The pre_post_update hook won't stop saving. Try adding validation to (auto)save using the wp_insert_post_data filter (just before pre_post_update in post.php). – chrisdillon Dec 4 '14 at 13:01
0

It has been a while, but I thought that using wp_die() and then hitting the back button would re-display the page with the form data as it was.

If that is the case, you could provide a message using wp_die to say what the problem is, possibly how to fix it, and to select the back button in their browser, fix the issue, and then try again.

A better way might be to:

Use the pre_post_update hook, do your validation, and...

  1. Create a list of each field that did not validate, possibly with an explanation.
  2. Change anything that fails validation to be what it was before the user changed it
  3. Write a custom user meta entry containing your array of errors
  4. Let the post data be written (you cleaned the bad data back out)
  5. On form display, grab the user meta that was created and use it to over-ride the displayed data and provide notes about what is wrong with each field.

Part 5 might be a bit of a pain, but should be doable using a filter or javascript.

Your saved user meta might look like:

$meta_value = array(
   'field_name' => array(
      'bad_value' => $users_bad_value,
      'problem' => 'Your data had this problem with it'
   )
);
update_user_meta( $user_id, "_edit_post_problems_{$post_id}", $meta_value );

Just don't forget to delete the meta data after reading it during the page load.

| improve this answer | |
  • Hi Jan, Thanx for your solution. I'm quite sure the POST data is lost when returning from a wp_die(). Your solution is of course ok, but it just seems like a HUGE amount of work for something so elemental as validation! I mean, saving stuff to a database just to invalidate a submission... In my opinion this should be really something the Core theme should have a look at. – john23klipp Jan 20 '15 at 9:57
  • The only other thing I can think of would be to set up an ajax call to do validation when the user creates or tries to save a post. That would probably be the cleanest / easiest method. If I have time at some point (and remember) I'll put up an example. – Privateer Jan 20 '15 at 12:20
0

This sounds like something you could solve with the help of AJAX. Have a look at the "JavaScript, AJAX & jQuery" section of the WordPress plugin handbook:

https://developer.wordpress.org/plugins/javascript/jquery/

Don't let the fact that it is a plugin handbook irritate you. It might be useful to create a small plugin for this task, or else you could use the instructions in your functions.php

That's how I would proceed after reading the handbook instructions:

  • I would use the change or blur events of the fields you wish to validate with jquery to trigger an ajax request.
  • Next you can use PHP to validate the given data.
  • And as last step you return an error or success message that you can display in your WordPress backend.

Yes, it requires some small jQuery knowledge but there's no saving and no page refresh. The handbook serves some really good explanations, hope it helps!

| improve this answer | |
  • Hi, thanx. But doesn't this completely defeat the purpose of server side validation?! No validation will take place if someone has their js disabled (regardless of the reason). There should be a solid (simple) way to check if certain data is set before this is stored in the db. – john23klipp Feb 17 '15 at 15:49
  • Guess you're right with this one. Nevertheless I can't think of a way to show an error message without using ajax and javascript so the page doesn't need to be reloaded... However, maybe this will help you somehow: code.tutsplus.com/tutorials/… – Fyn Feb 19 '15 at 7:03

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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