Sometimes, due to meta boxes added by plugins, the excerpt gets moved down the order in the admin page.

Is there a way I can force it to always be directly below the post content?

Here is the way I’ve tried:

function remove_wordpress_meta_boxes() {
   remove_meta_box( 'postexcerpt', 'page', 'normal' );
   add_meta_box('postexcerpt', __('Excerpt'), 'post_excerpt_meta_box', 'page', 'normal', 'high');
}
add_action( 'admin_menu', 'remove_wordpress_meta_boxes' );

remove_meta_box() works as expected, but re-adding the excerpt meta box doesn’t seem to do anything to the ordering – I still have a plugin metabox appear above the excerpt.

2 Answers
2

The reason why your code doesn’t work is most likely that you’ve ordered your metaboxes before, and the that order has been saved into the meta-box-order_page meta value. This overrides the default setup.

Here’s an example of the meta-box-order_post meta value:

a:3:{
     s:4:"side";    
         s:61:"submitdiv,formatdiv,categorydiv,tagsdiv-post_tag,postimagediv";
     s:6:"normal";  
         s:96:"revisionsdiv,postexcerpt,trackbacksdiv,postcustom,
               commentstatusdiv,commentsdiv,slugdiv,authordiv";
     s:8:"advanced";
         s:0:"";
}

Here I’ve just reformatted the serialized array for better readability.

Sticky MetaBoxes:

To move a given metabox to the top position, for a given context and post-type, you can use this code snippet:

/**
 * Set some sticky metaboxes
 */

add_action( 'admin_init', function()
{
    if( function_exists( 'wpse_sticky_metabox' ) )
    {
         // Sticky metabox #1:
         wpse_sticky_metabox(
             array(
                 'cpt'      => 'page',
                 'context'  => 'normal',
                 'metabox'  => 'postexcerpt'
             )
         );

         // Sticky metabox #2:
         wpse_sticky_metabox(
             array(
                 'cpt'      => 'post',
                 'context'  => 'side',
                 'metabox'  => 'authordiv' 
             )
         );
    }
});

where you can adjust this to your needs.

Some info on the input parameters:

  • cpt is the custom post type of the edit screen ( i.e. post, page, …)
  • context is the section where you want to make the metabox sticky. ( i.e. normal, side, advanced, … )
  • metabox is the id of the sticky meta-box ( i.e. postexcerpt, authordiv, … )

Sticky MetaBoxes – the plugin:

This is supported by the following demo plugin:

/**
 * Plugin Name: Sticky Meta-Boxes
 * Description: Set a given meta-box to the top, for a given cpt and context.
 * Plugin URI:  http://wordpress.stackexchange.com/a/174980/26350
 * Author:      Birgir Erlendsson (birgire)
 * Version:     0.0.2
 */

function wpse_sticky_metabox( $args = array() )
{
    if( class_exists( 'MetaBoxSticker' ) )
    {
        $o = new MetaBoxSticker;
        $o->setup( $args )->activate();
    }
}

class MetaBoxSticker
{
    private $args;

    public function setup( $args = array() )
    {
        $default = array(
            'cpt'      => 'post',
            'context'  => 'normal',
            'metabox'  => 'postexcerpt'
        );
        $this->args = wp_parse_args( $args, $default );
        return $this;
    }

    public function activate()
    {
        add_filter(
            sprintf(
                'get_user_option_meta-box-order_%s',
                $this->args['cpt']
            ),
            array( $this, 'filter' ),
            PHP_INT_MAX
        );

        add_action( 
            'add_meta_boxes', 
            array( $this, 'relocate' ), 
            PHP_INT_MAX 
        );
    }

    public function relocate()
    {
        //-----------------------
        // Get the user input:
        //-----------------------
        $_cpt      = sanitize_key( $this->args['cpt']     );
        $_metabox  = sanitize_key( $this->args['metabox'] );
        $_context  = sanitize_key( $this->args['context'] );

        //-----------------------
        // Relocate 'high' metaboxes to 'default' in the current context
        //-----------------------                  
        global $wp_meta_boxes;
        if( isset( $wp_meta_boxes[$_cpt][$_context]['high'] ) )
        {                                                           
            foreach( $wp_meta_boxes[$_cpt][$_context]['high'] as $id => $item )
            {
                if( isset( $item['callback'] ) )
                {
                    remove_meta_box( 
                        $id, 
                        $_cpt, 
                        $_context 
                    );

                    add_meta_box( 
                        $id, 
                        $item['title'], 
                        $item['callback'], 
                        $_cpt, 
                        $_context, 
                        'default', 
                        $item['args'] 
                    );
                }
            }
        }
    }

    public function filter( $order )
    {
        //-----------------------
        // Get the user input:
        //-----------------------                               
        $_cpt      = sanitize_key( $this->args['cpt']     );
        $_metabox  = sanitize_key( $this->args['metabox'] );
        $_context  = sanitize_key( $this->args['context'] );

        //-----------------------
        // Handle the case if the current user hasn't made any meta-box ordering before:
        //-----------------------
        if( empty( $order ) )
        {
            global $wp_meta_boxes;
            if( ! isset( $wp_meta_boxes[$_cpt][$_context] ) )
               return $order;

            $order = array();
            foreach( $wp_meta_boxes[$_cpt] as $context_key => $context_item )
            {
                $tmp = array();
                foreach( $context_item as $priority_key => $priority_item )
                {
                    foreach( $priority_item as $metabox_key => $metabox_item )
                    {
                        $tmp[] = $metabox_key;
                    }
                }
                $order[$context_key] = join( ',', $tmp );
            }
        }

        //-----------------------
        // Let's make sure the context exists:
        //-----------------------
        if( ! isset( $order[$_context] ) )
            return $order;

        //-----------------------
        // Remove the given meta-box from the whole order array:
        //-----------------------
        foreach( $order as $context_key => $string )
        {
            $tmp = explode( ',', $string );
            $key = array_search( $_metabox, $tmp );
            if( ! empty( $key ) )
            {
                unset( $tmp[$key] );
                $order[$context_key] = join( ',', $tmp );
            }
        }

        //-----------------------
        // Make the given meta-box sticky for a given context
        //-----------------------
        $tmp = explode( ',', $order[$_context] );
        array_unshift( $tmp, $_metabox );
        $order[$_context] = join( ',', $tmp );

        return $order;
    }

} // end class

This plugin should also work, even if you haven’t made any ordering before.

It also respects the Screen Options, i.e. wether a meta-box is visible or not.

I hope you can extend this further to your needs.

Tags:

Leave a Reply

Your email address will not be published. Required fields are marked *