16

Having read elsewhere on Stack of two WP plugins forcing identical menu positions (with the likelihood of one then not appearing), I'm wondering how I can control the position of menu items added by plugins.

I already use a function which seems to handle such submenu items in 'settings', and another function to reorder default (posts, pages, themes, plugins, settings, etcetera) 'top level' items - but which doesn't change the positioning of such items added by plugins.

function custom_menu_order() {
return array(
//Add items here in desired order.

);
}

add_filter( 'custom_menu_order', '__return_true' );
add_filter( 'menu_order', 'custom_menu_order' );

As an example, of the two top-level menu items added by WooCommerce, one appears above the item added by ContactForm7 and the other below, and it'd be nice to reorder them accordingly - and also, to be able to better reorder items which don't force a menu position and instead appear at the bottom.

I find it usually works fine for re-ordering default and 'edit.php?post_type=...' items, but those with 'admin.php?page=...' don't re-order.

When my re-order function is disabled, the two WooCommerce items ('edit.php?post_type=product', and 'edit.php?post_type=shop_order') group together as intended, but when the function is reactivated they're split by ContactForm7 ('admin.php?page=wpcf7').

And, one ('edit.php?post_type=shop_order') of the WooCommerce CPTs won't reorder - although the other ('edit.php?post_type=product') does.

4 Answers 4

16

To change top level admin menu items order you'll need two hooks, two filters, and one function. Put the following code in your current theme's functions.php:

function wpse_custom_menu_order( $menu_ord ) {
    if ( !$menu_ord ) return true;

    return array(
        'index.php', // Dashboard
        'separator1', // First separator
        'edit.php', // Posts
        'upload.php', // Media
        'link-manager.php', // Links
        'edit-comments.php', // Comments
        'edit.php?post_type=page', // Pages
        'separator2', // Second separator
        'themes.php', // Appearance
        'plugins.php', // Plugins
        'users.php', // Users
        'tools.php', // Tools
        'options-general.php', // Settings
        'separator-last', // Last separator
    );
}
add_filter( 'custom_menu_order', 'wpse_custom_menu_order', 10, 1 );
add_filter( 'menu_order', 'wpse_custom_menu_order', 10, 1 );

The returned array of top level admin menu items, above, represents menu items inserted by core, in their default order. To include menu items added by plugins, we have to add them to this array. Let's say we have two plugins added and activated ( for example: Wordfence and NextCellent Gallery ). We have to find names of these menu items, first. When we click on Wordfence's top level menu item, the resulting URL will end with ?page=Wordfence. The part after ?page= is our name ( Wordfence ). For NextCellent Gallery, the name will be nextcellent-gallery-nextgen-legacy. Now, let's add these items to our array:

return array(
    'index.php', // Dashboard
    'separator1', // First separator
    'edit.php', // Posts
    'upload.php', // Media
    'link-manager.php', // Links
    'edit-comments.php', // Comments
    'edit.php?post_type=page', // Pages
    'separator2', // Second separator
    'themes.php', // Appearance
    'plugins.php', // Plugins
    'users.php', // Users
    'tools.php', // Tools
    'separator3', // Third separator
    'options-general.php', // Settings
    'separator-last', // Last separator
    'Wordfence', // Wordfence
    'nextcellent-gallery-nextgen-legacy', // NextCellent Gallery
);

We can, now, move items of this array, up and down, to get the final order.

Note: you can use Admin Menu Editor plugin, for easy drag and drop actions, as well.

3
  • 1
    @ Frank P. Walentynowicz ...thanks for the comprehensive answer. I updated my original post to clarify, and your suggestion of using 'the part after ?page=' is very helpful and solves my issue - except for one of the two WooCommerce items.
    – glvr
    Commented Aug 9, 2017 at 9:44
  • Quick addition to my above comment: I'd previously used Admin Menu Editor, but prefer a hardcoded function.
    – glvr
    Commented Aug 9, 2017 at 9:51
  • What is the significane of the 10,1? As in add_filter( 'menu_order', 'wpse_custom_menu_order', 10, 1 ); I cannot find any info on that. Probably something simple. But, I cannot get WooCommerce to budge and wondering if that has something to do with it. Commented Dec 3, 2020 at 18:16
22

The existing answers are fine, but if you would add a new custom post type, you would have to re-edit those functions again and again.

To fix this, I developed this small function. Just define your $new_positions inside the my_new_menu_order function:

/**
 * Activates the 'menu_order' filter and then hooks into 'menu_order'
 */
add_filter('custom_menu_order', function() { return true; });
add_filter('menu_order', 'my_new_admin_menu_order');
/**
 * Filters WordPress' default menu order
 */
function my_new_admin_menu_order( $menu_order ) {
  // define your new desired menu positions here
  // for example, move 'upload.php' to position #9 and built-in pages to position #1
  $new_positions = array(
    'upload.php' => 9,
    'edit.php?post_type=page' => 1
  );
  // helper function to move an element inside an array
  function move_element(&$array, $a, $b) {
    $out = array_splice($array, $a, 1);
    array_splice($array, $b, 0, $out);
  }
  // traverse through the new positions and move 
  // the items if found in the original menu_positions
  foreach( $new_positions as $value => $new_index ) {
    if( $current_index = array_search( $value, $menu_order ) ) {
      move_element($menu_order, $current_index, $new_index);
    }
  }
  return $menu_order;
};
3
  • 2
    This is awesome - so when new items are to be added as a menu item such as a custom post type (as you suggested) or a new plugin or even a new built-in option in the future it will get added just fine as normal?
    – Brett
    Commented Jan 2, 2019 at 21:19
  • @Brett It looks like it would work like that.
    – Davey
    Commented Jul 17, 2019 at 14:32
  • Still works a treat - thanks Commented Dec 21, 2021 at 15:29
17

when you're creating a post type with register_post_type() you can set the menu position:

menu_position (integer) (optional) The position in the menu order the post type should appear. show_in_menu must be true.

    Default: null - defaults to below Comments 

    5  - below Posts
    10 - below Media
    15 - below Links
    20 - below Pages
    25 - below Comments
    59 - below first separator
    60 - below Appearance
    65 - below Plugins
    70 - below Users
    75 - below Tools
    80 - below Settings
    99 - below second separator

If items have the same menu position they are sorted alphabetically.

in your own plugin you can set the level. if you're trying to change the menu position of a plugin you haven't created, many of them may have it pluggable, or you can to edit their calls.

4
  • @ rudtek... thanks. In my own CPTs I've avoided setting menu positions, preferring instead to use a menu re-order and thus 'have it all in the same place' and easier to subsequently change. Having no own plugins for which I'm trying to set menu position, it's for third-party plugins - for which I don't know enough about 'pluggable' or to edit their calls (which would presumably be overwritten on update).
    – glvr
    Commented Aug 9, 2017 at 9:48
  • Awesome, works fine
    – Jodyshop
    Commented Oct 25, 2021 at 19:58
  • 1
    You can also use decimal numbers: 25.3
    – Azamat
    Commented Aug 22, 2022 at 6:39
  • Wordpress core file source to back this answer up (and possibly update it if it changes with time): /wp-admin/menu.php Commented Jul 28, 2023 at 15:57
1

Thanks to rassoh for a nice option.

Here is a revised version that contains a list of pages that should probably always stay at the top...

/**
 * These 2 filters and 1 function move the built in WordPress admin pages to
 * the top so they don't get pushed down the menu every time a new plugin is installed.
 * Activates the 'menu_order' filter and then hooks into 'menu_order'
 */
add_filter('custom_menu_order', function() { return true; });
add_filter('menu_order', 'my_new_admin_menu_order');
/**
 * Filters WordPress' default menu order
 */
function my_new_admin_menu_order( $menu_order ) {
  // define your new desired menu positions here
  // for example, move 'upload.php' to position #9 and built-in pages to position #1
  $new_positions = array(
    'index.php' => 1,  // Dashboard
    'edit.php' => 2,  // Posts
    'upload.php' => 3,  // Media
    'edit.php?post_type=page' => 4,  // Pages
    'edit-comments.php' => 5  // Comments
  );
  // helper function to move an element inside an array
  function move_element(&$array, $a, $b) {
    $out = array_splice($array, $a, 1);
    array_splice($array, $b, 0, $out);
  }
  // traverse through the new positions and move 
  // the items if found in the original menu_positions
  foreach( $new_positions as $value => $new_index ) {
    if( $current_index = array_search( $value, $menu_order ) ) {
      move_element($menu_order, $current_index, $new_index);
    }
  }
  return $menu_order;
};

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.