Leave a Reply

4 comments

  1. You can do this by hand, but WP natively does it like this for settings errors:

    1. add_settings_error() to create message.
    2. Then set_transient('settings_errors', get_settings_errors(), 30);
    3. settings_errors() in admin_notices hook to display (will need to hook for non-settings screens).
  2. you can use admin_notices hook

    first define the notice function:

    function my_admin_notice(){
        //print the message
        echo '<div id="message">
           <p>metabox as errors on save message here!!!</p>
        </div>';
        //make sure to remove notice after its displayed so its only displayed when needed.
        remove_action('admin_notices', 'my_admin_notice');
    }
    

    The you you metabox save function based on if needed add:

    ...
    ...
    if($errors){
        add_action('admin_notices', 'my_admin_notice');
    }
    ...
    ...
    

    Update

    Like I promised here is an example of a of how i add an error message form my metabox

    <?php
    /*
    Plugin Name: one-trick-pony-notice
    Plugin URI: http://en.bainternet.info
    Description: Just to proof a point using admin notice form metabox
    Version: 1.0
    Author: Bainternet
    Author URI: http://en.bainternet.info
    */
    
    /*  admin notice */
    function my_admin_notice(){
        //print the message
        global $post;
        $notice = get_option('otp_notice');
        if (empty($notice)) return '';
        foreach($notice as $pid => $m){
            if ($post->ID == $pid ){
                echo '<div id="message" class="error"><p>'.$m.'</p></div>';
                //make sure to remove notice after its displayed so its only displayed when needed.
                unset($notice[$pid]);
                update_option('otp_notice',$notice);
                break;
            }
        }
    }
    
    //hooks
    
    add_action('add_meta_boxes', 'OT_mt_add');
    add_action('save_post', 'OT_mt_save');
    add_action('admin_notices', 'my_admin_notice',0);
    
    //add metabox
    function OT_mt_add() {
        add_meta_box('OT_mt_sectionid', __( 'One Trick Meta Box notice', 'textdomain' ),'OT_mt_display','post');
    }
    
    //display metabox
    function OT_mt_display() {
    
      // Use nonce for verification
      wp_nonce_field( plugin_basename(__FILE__), 'myplugin_noncename' );
    
      // The actual fields for data entry
      echo '<label for="myplugin_new_field">';
           _e("leave blank to get a notice on publish or update", 'textdomain' );
      echo '</label> ';
      echo '<input type="text" id="ot_field" name="ot_field" value="" size="25" />';
    
    }
    
    
    //save metabox here is were i check the fields and if empty i display a message
    function OT_mt_save( $post_id ) {
    
      // verify this came from the our screen and with proper authorization,
      // because save_post can be triggered at other times
        if (!isset($_POST['myplugin_noncename'])) return $post_id;
      if ( !wp_verify_nonce( $_POST['myplugin_noncename'], plugin_basename(__FILE__) ) )
          return $post_id;
    
      // verify if this is an auto save routine. 
      // If it is our form has not been submitted, so we dont want to do anything
      if ( defined('DOING_AUTOSAVE') && DOING_AUTOSAVE ) 
          return $post_id;
    
    
      if(!isset($_POST['ot_field']) || empty($_POST['ot_field'])){
        //field left empty so we add a notice
        $notice = get_option('otp_notice');
        $notice[$post_id] = "You have left the field empty";
        update_option('otp_notice',$notice);
      }
    
    }
    

    Now when looking for this code i found my old way of doing it using post_updated_messages filter hook in about the same way so i’ll add that too:

    <?php
    /*
    Plugin Name: one-trick-pony-notice2
    Plugin URI: http://en.bainternet.info
    Description: just like the one above but this time using post_updated_messages hook
    Version: 1.0
    Author: Bainternet
    Author URI: http://en.bainternet.info
    */
    
    //hooks
    add_filter('post_updated_messages','my_messages',0);
    add_action('add_meta_boxes', 'OT_mt_add');
    add_action('save_post', 'OT_mt_save');
    
    
    //add metabox
    function OT_mt_add() {
        add_meta_box('OT_mt_sectionid', __( 'One Trick Meta Box notice', 'textdomain' ),'OT_mt_display','post');
    }
    
    //display metabox
    function OT_mt_display() {
    
      // Use nonce for verification
      wp_nonce_field( plugin_basename(__FILE__), 'myplugin_noncename' );
    
      // The actual fields for data entry
      echo '<label for="myplugin_new_field">';
           _e("leave blank to get a notice on publish or update", 'textdomain' );
      echo '</label> ';
      echo '<input type="text" id="ot_field" name="ot_field" value="" size="25" />';
    
    }
    
    
    //save metabox here is were i check the fields and if empty i display a message
    function OT_mt_save( $post_id ) {
    
      // verify this came from the our screen and with proper authorization,
      // because save_post can be triggered at other times
        if (!isset($_POST['myplugin_noncename'])) return $post_id;
      if ( !wp_verify_nonce( $_POST['myplugin_noncename'], plugin_basename(__FILE__) ) )
          return $post_id;
    
      // verify if this is an auto save routine. 
      // If it is our form has not been submitted, so we dont want to do anything
      if ( defined('DOING_AUTOSAVE') && DOING_AUTOSAVE ) 
          return $post_id;
    
    
      if(!isset($_POST['ot_field']) || empty($_POST['ot_field'])){
        //field left empty so we add a notice
        $notice = get_option('otp_notice');
        $notice[$post_id] = "You have left the field empty";
        update_option('otp_notice',$notice);
      }
    
    }
    
    //messages filter
    function my_messages($m){
        global $post;
        $notice = get_option('otp_notice');
        if (empty($notice)) return $m;
        foreach($notice as $pid => $mm){
            if ($post->ID == $pid ){
                foreach ($m['post'] as $i => $message){
                    $m['post'][$i] = $message.'<p>'.$mm.'</p>';
    
                }
                unset($notice[$pid]);
                update_option('otp_notice',$notice);
                break;
            }
        }
        return $m;
    }
    
  3. This answer [mirror] from Otto in WP Tavern, actually solves the transient problem by doing what WordPress itself does to overcome the redirect problem. Totally worked for me.

    The problem is that transients are there for everybody. If you have more than one user doing things at the same time, the error message can go to the wrong person. It’s a race condition.

    WordPress actually does this by passing a message parameter in the
    URL. The message number indicates which message to display.

    You can do the same by hooking the redirect_post_location filter and
    then using add_query_arg to add your own parameter to the request.
    Like so:

    add_filter('redirect_post_location','my_message');
    function my_message($loc) {
     return add_query_arg( 'my_message', 123, $loc );
    }
    

    This adds my_message=123 to the query. Then, after the redirect, you can detect the my_message setting in the $_GET and display the proper message accordingly.

  4. I know this question is old but I find the answers here to not solve the issue.

    Extending off the answer from Ana Ban, using Otto’s method, I found this to be the best method to handle errors. This doesn’t require storing the errors in the db.

    I included a stripped down version of a Metabox object I use. This allows for me to easily add new error messages and ensuring the correct user sees the error message (using the db, this is not a guarantee).

    <?php
    /**
     * Class MetaboxExample
     */
    class MetaboxExample {
    
        /**
         * Defines the whitelist for allowed screens (post_types)
         */
        private $_allowedScreens = array( 'SCREENS_TO_ALLOW_METABOX' );
    
        /**
         * Get parameter for the error box error code
         */
        const GET_METABOX_ERROR_PARAM = 'meta-error';
    
        /**
         * Defines admin hooks
         */
        public function __construct() {
            add_action('add_meta_boxes', array($this, 'addMetabox'), 50);
            add_action('save_post', array($this, 'saveMetabox'), 50);
            add_action('edit_form_top', array($this, 'adminNotices')); // NOTE: admin_notices doesn't position this right on custom post type pages, haven't testes this on POST or PAGE but I don't see this an issue
        }
    
        /**
         * Adds the metabox to specified post types
         */
        public function addMetabox() {
            foreach ( $this->_allowedScreens as $screen ) {
                add_meta_box(
                    'PLUGIN_METABOX',
                    __( 'TITLE', 'text_domain' ),
                    array($this, 'metaBox'),
                    $screen,
                    'side',
                    'high'
                );
            }
        }
    
        /**
         * Output metabox content
         * @param $post
         */
        public function metaBox($post) {
            // Add an nonce field so we can check for it later.
            wp_nonce_field( 'metaboxnonce', 'metaboxnonce' );
            // Load meta data for this metabox
            $someValue = get_post_meta( $post->ID, 'META_KEY_IDENTIFIER', true );
            ?>
            <p>
                <label for="some-value" style="width: 120px; display: inline-block;">
                    <?php _e( 'Some Field:', 'text_domain' ); ?>
                </label>
                &nbsp;
                <input type="text" id="some-value" name="some_value" value="<?php esc_attr_e( $someValue ); ?>" size="25" />
            </p>
        <?php
        }
    
        /**
         * Save method for the metabox
         * @param $post_id
         */
        public function saveMetabox($post_id) {
            global $wpdb;
    
            // Check if our nonce is set.
            if ( ! isset( $_POST['metaboxnonce'] ) ) {
                return $post_id;
            }
            // Verify that the nonce is valid.
            if ( ! wp_verify_nonce( $_POST['metaboxnonce'], 'metaboxnonce' ) ) {
                return $post_id;
            }
            // If this is an autosave, our form has not been submitted, so we don't want to do anything.
            if ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE ) {
                return $post_id;
            }
            // Check the user's permissions.
            if ( isset( $_POST['post_type'] ) && 'page' == $_POST['post_type'] ) {
                if ( ! current_user_can( 'edit_page', $post_id ) ) {
                    return $post_id;
                }
            } else {
                if ( ! current_user_can( 'edit_post', $post_id ) ) {
                    return $post_id;
                }
            }
            // Make sure that it is set.
            if ( !isset( $_POST['some_value'] ) ) {
                return $post_id;
            }
            // Sanitize user input.
            $someValue = sanitize_text_field( $_POST['some_value'] );
            // Check to make sure there is a value
            if (empty($someValue)) {
                // Add our error code
                add_filter('redirect_post_location', function($loc) {
                    return add_query_arg( self::GET_METABOX_ERROR_PARAM, 1, $loc );
                });
                return $post_id; // make sure to return so we don't allow further processing
            }
            // Update the meta field in the database.
            update_post_meta( $post_id, 'META_KEY_IDENTIFIER', $someValue );
        }
    
        /**
         * Metabox admin notices
         */
        public function adminNotices() {
            if (isset($_GET[self::GET_METABOX_ERROR_PARAM])) {
                $screen = get_current_screen();
                // Make sure we are in the proper post type
                if (in_array($screen->post_type, $this->_allowedScreens)) {
                    $errorCode = (int) $_GET[self::GET_METABOX_ERROR_PARAM];
                    switch($errorCode) {
                        case 1:
                            $this->_showAdminNotice( __('Some error happened', 'text_domain') );
                            break;
                        // More error codes go here for outputting errors
                    }
                }
            }
        }
    
        /**
         * Shows the admin notice for the metabox
         * @param $message
         * @param string $type
         */
        private function _showAdminNotice($message, $type='error') {
            ?>
            <div class="<?php esc_attr_e($type); ?> below-h2">
                <p><?php echo $message; ?></p>
            </div>
        <?php
        }
    
    }