Wordpress - Add validation and error handling when saving custom fields?
Store errors in your class or as a global, possibly in a transient or meta, and display them in admin notices on POST requests. WP does not feature any flash message handler.
I suggest to use sessions since this will not create strange effects when two users editing at the same time. So this is what I do:
Sessions are not started by wordpress. So you need to start a session in your plugin, functions.php or even wp-config.php:
if (!session_id())
session_start();
When saving the post, append errors and notices to the session:
function my_save_post($post_id, $post) {
if($something_went_wrong) {
//Append error notice if something went wrong
$_SESSION['my_admin_notices'] .= '<div class="error"><p>This or that went wrong</p></div>';
return false; //might stop processing here
}
if($somthing_to_notice) { //i.e. successful saving
//Append notice if something went wrong
$_SESSION['my_admin_notices'] .= '<div class="updated"><p>Post updated</p></div>';
}
return true;
}
add_action('save_post','my_save_post');
Print notices and errors and then clean the messages in the session:
function my_admin_notices(){
if(!empty($_SESSION['my_admin_notices'])) print $_SESSION['my_admin_notices'];
unset ($_SESSION['my_admin_notices']);
}
add_action( 'admin_notices', 'my_admin_notices' );
Based on pospi's suggestion to use transients, I came up with the following. The only problem is there is no hook to put the message below the h2
where other messages go, so I had to do a jQuery hack to get it there.
First, save the error message duing your save_post
(or similar) handler. I give it a short lifetime of 60 seconds, so it is there just long enough for the redirect to happen.
if($has_error)
{
set_transient( "acme_plugin_error_msg_$post_id", $error_msg, 60 );
}
Then, just retrieve that error message on the next page load and display it. I also delete it so it wont get displayed twice.
add_action('admin_notices', 'acme_plugin_show_messages');
function acme_plugin_show_messages()
{
global $post;
if ( false !== ( $msg = get_transient( "acme_plugin_error_msg_{$post->ID}" ) ) && $msg) {
delete_transient( "acme_plugin_error_msg_{$post->ID}" );
echo "<div id=\"acme-plugin-message\" class=\"error below-h2\"><p>$msg</p></div>";
}
}
Since admin_notices
fires before the primary page content is generated, the notice is not where the other post edit messages go, so I had to use this jQuery to move it there:
jQuery('h2').after(jQuery('#acme-plugin-message'));
Since the post ID is part of the transient name, this should work in most multi-user environments except when multiple users are concurrently editing the same post.