I have a error handling mechanism setup in one of my plugins to add notices and errors to the admin area, just like the core does. It works fine in most cases, but there are some situations (like saving a custom post type) where it doesn’t. I’m guessing that a redirect is happening behind the scenes, and the messages are being printed before the redirect happens, so that they appear to never show up.
So, I’m guessing this is what’s happening
- User edits a custom post type and hits Publish
- My post_updated callback is called, which validates and saves the custom fields
- The callback adds an error message
- WordPress does a redirect to a some page to do some processing
- My admin_notices callback is called, which prints and clears the messages
- WordPress redirects back to the post
- My admin_notices callback is called again, but there are no messages to print because they were printed in step #5
Under normal circumstances steps 4 and 5 don’t happen, so everything works fine, but I think when WordPress saves posts it introduces the extra redirect. Is something I can do to make sure this always works? I was thinking I might be able to check something inside printMessages() and return immediately if it’s at step 4, but I’m not sure what.
These two questions may shed some light on the problem, but don’t fully give a solution: Add validation and error handling when saving custom fields?, How to display admin error notice if settings saved succesfully?.
Here’s the code:
/**
* Constructor
* @author Ian Dunn <redacted@mpangodev.com>
*/
public function __construct()
{
// Initialize variables
$defaultOptions = array( 'updates' => array(), 'errors' => array() );
$this->options = array_merge( get_option( self::PREFIX . 'options', array() ), $defaultOptions );
$this->updatedOptions = false;
$this->userMessageCount = array( 'updates' => 0, 'errors' => 0 );
// more
add_action( 'admin_notices', array($this, 'printMessages') );
add_action( 'post_updated', array($this, 'saveCustomFields') );
// does other stuff
}
/**
* Saves values of the the custom post type's extra fields
* @author Ian Dunn <redacted@mpangodev.com>
*/
public function saveCustomFields()
{
// does stuff
if( true ) // if there was an error
$this->enqueueMessage( 'foo', 'error' );
}
/**
* Displays updates and errors
* @author Ian Dunn <redacted@mpangodev.com>
*/
public function printMessages()
{
foreach( array('updates', 'errors') as $type )
{
if( $this->options[$type] && ( self::DEBUG_MODE || $this->userMessageCount[$type] ) )
{
echo '<div id="message" class="'. ( $type == 'updates' ? 'updated' : 'error' ) .'">';
foreach($this->options[$type] as $message)
if( $message['mode'] == 'user' || self::DEBUG_MODE )
echo '<p>'. $message['message'] .'</p>';
echo '</div>';
$this->options[$type] = array();
$this->updatedOptions = true;
$this->userMessageCount[$type] = 0;
}
}
}
/**
* Queues up a message to be displayed to the user
* @author Ian Dunn <redacted@mpangodev.com>
* @param string $message The text to show the user
* @param string $type 'update' for a success or notification message, or 'error' for an error message
* @param string $mode 'user' if it's intended for the user, or 'debug' if it's intended for the developer
*/
protected function enqueueMessage($message, $type = 'update', $mode = 'user')
{
array_push($this->options[$type .'s'], array(
'message' => $message,
'type' => $type,
'mode' => $mode
) );
if($mode == 'user')
$this->userMessageCount[$type . 's']++;
$this->updatedOptions = true;
}
/**
* Destructor
* Writes options to the database
* @author Ian Dunn <redacted@mpangodev.com>
*/
public function __destruct()
{
if($this->updatedOptions)
update_option(self::PREFIX . 'options', $this->options);
}
Update: The updated code with the accepted answer has been committed to core.php in the plugin’s trunk in case anyone wants to see a full working copy. The next stable release that will have it is 1.2.
Update 2: I’ve abstracted this functionality into a self-contained library that you can include in your plugin. Core is discussing including similar functionality in #11515.
There are few things that I have pointed out in the code below too:
get_option
usingarray_merge
__destruct
just does not work. (I don’t have any clue yet, may be experts will shed some light on it.I have marked all the sections where I have made the changes with HKFIX, with a bit of description:
I currently don’t have a clue what’s going on with your plugin, so I point you at two things:
wp_parse_args()
is a nice way to merge defaults with other arguments.And this Plugin is a little closer to how core handles errors (straight out of my head – may contain errors itself):
EDIT: Test Plugin
Give it a try. 🙂