3

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.

5
  • Does it fix the problem for editing pages? If so, you need to repeat the code with post instead of page. If not, what's the plugin in question? – TheDeadMedic Jan 15 '15 at 16:39
  • I've tried editing both posts and pages, changing the 2nd parameter so all combinations are tested. Nothing will budge Excerpt. Initially I thought maybe plugin meta boxes were taking precedence, but when editing posts, I can see that even a core metabox (revisions) is appearing above excerpt. I could manually drag excerpt to its correct location, but I want to catch all method that will work for every user, every time. – iltdev Jan 15 '15 at 16:50
  • Try the action add_meta_boxes instead of admin_menu – TheDeadMedic Jan 15 '15 at 16:58
  • 1
    I would also advise clearing meta-box-order_post and meta-box-order_page in wp_usermeta for sanity - these will override any order defined in your code. – TheDeadMedic Jan 15 '15 at 17:01
  • Thanks, @TheDeadMedic. Clearing both meta-box-order_post and meta-box-order_page did the trick and restored Excerpt to the top. I'd now like to prevent users from moving the Excerpt, if possible. – iltdev Jan 16 '15 at 9:51
3

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.

9
  • You've left out the array( $this, 'filter' ), arg to add_filter() in activate() - great stuff though! – bonger Jan 15 '15 at 18:00
  • Thanks @bonger, hopefully I've inserted the whole code now ;-) – birgire Jan 15 '15 at 18:11
  • Thank you, @birgire for such a detailed answer. I've activated your plugin and added your sample code (just Sticky metabox #2) then tested on a post. It does indeed move the Excerpt above core WP meta boxes e.g. 'Revisions', so that's great :) However, a theme meta box and a plugin meta box (Attachments by Jonathan Christopher) still appear above the Excerpt. Any thoughts? – iltdev Jan 16 '15 at 9:28
  • Hi, I just tested it with the Attachments plugin (this usage example) and wpse_sticky_metabox( array( 'cpt' => 'post', 'context' => 'normal', 'metabox' => 'postexcerpt' ) ); and it works as expected on my WP install. I always get the excerpt meta-box on top. – birgire Jan 16 '15 at 9:50
  • With @TheDeadMedic's guidance, I've cleared out meta-box-order_post and meta-box-order_page which has restored Excerpt to the top of the admin page. A nice addition to your plugin would be to make it impossible for the sticky post to be moved by the user :) – iltdev Jan 16 '15 at 9:53
1

As pointed out, you can only change the default ordering, if a user has modified it there's not a huge amount you can do without writing a reset script.

Either way, here's how you change the default order properly, (disclaimer, I wrote the article) https://ozthegreat.io/wordpress/wordpress-how-to-move-the-excerpt-meta-box-above-the-editor/

/**
 * Removes the regular excerpt box. We're not getting rid
 * of it, we're just moving it above the wysiwyg editor
 *
 * @return null
 */
function oz_remove_normal_excerpt() {
    remove_meta_box( 'postexcerpt' , 'post' , 'normal' );
}
add_action( 'admin_menu' , 'oz_remove_normal_excerpt' );

/**
 * Add the excerpt meta box back in with a custom screen location
 *
 * @param  string $post_type
 * @return null
 */
function oz_add_excerpt_meta_box( $post_type ) {
    if ( in_array( $post_type, array( 'post', 'page' ) ) ) {
        add_meta_box(
            'oz_postexcerpt',
            __( 'Excerpt', 'thetab-theme' ),
            'post_excerpt_meta_box',
            $post_type,
            'after_title',
            'high'
        );
    }
}
add_action( 'add_meta_boxes', 'oz_add_excerpt_meta_box' );

/**
 * You can't actually add meta boxes after the title by default in WP so
 * we're being cheeky. We've registered our own meta box position
 * `after_title` onto which we've regiestered our new meta boxes and
 * are now calling them in the `edit_form_after_title` hook which is run
 * after the post tile box is displayed.
 *
 * @return null
 */
function oz_run_after_title_meta_boxes() {
    global $post, $wp_meta_boxes;
    # Output the `below_title` meta boxes:
    do_meta_boxes( get_current_screen(), 'after_title', $post );
}
add_action( 'edit_form_after_title', 'oz_run_after_title_meta_boxes' );
2
  • Please post the content from your article in your answer. If your site goes offline, this answer will mean nothing to others :-) – Pieter Goosen Jul 28 '15 at 16:49
  • Of course, will do now! – OzTheGreat Jul 28 '15 at 17:04

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.