Sort admin menu items

On a related note to “Changing the Order of Admin Menu Sections?“, I’m looking for a way to alphabetically sort the entries in each sub-section of WordPress’s admin area.

Currently, whenever a new plugin is added, its entry will appear in a seemingly random location under Settings / Tools / Plugins, and it’s often hard to locate the new menu item. (I have a lot of plugins already, so my menus are pretty full.)

Read More

Being that I add and remove plugins fairly regularly, I would rather not need to continually go into the settings page for a menu-ordering plugin and adjust the order.

Sorry for the long question; I just want to make it clear what I’m looking for.

Example

Instead of:

    Settings
    - General
    - Writing
    - Reading
    - Discussion
    - (rest of core items)
    - Plugin 4
    - WP something
    - A plugin to help with stuff
    - Google-related plugin
    - RSS plugin
    - FeedBurner plugin
    - etc.

Can I have the menu sorted alphabetically (or by another logical method), e.g.:

    Settings
    - A plugin to help with stuff
    - Discussion
    - FeedBurner plugin
    - General
    - Google-related plugin
    - Plugin 4
    - Reading
    - (rest of core items)
    - RSS plugin
    - WP something
    - Writing
    - etc.

Even better would be a sort method that keeps the core entries where they are by default and only sorts items added by plugins:

    Settings
    - General
    - Writing
    - Reading
    - Discussion
    - (rest of core items)
    - A plugin to help with stuff
    - FeedBurner plugin
    - Google-related plugin
    - Plugin 4
    - RSS plugin
    - WP something
    - etc.

Related posts

Leave a Reply

3 comments

  1. It can be done sorting the global $submenu.

    The sorting that’s applied resets the key number of the sub-array $submenu['options-general.php'], which is:

    array
      'options-general.php' => 
        array
          10 => 
            array
              0 => string 'General'
              1 => string 'manage_options'
              2 => string 'options-general.php'
          15 => 
            array
              0 => string 'Writing'
              1 => string 'manage_options'
              2 => string 'options-writing.php'
          // etc
    

    and becomes:

    array
      'options-general.php' => 
        array
          0 => 
            array
              0 => string 'Discussion'
              1 => string 'manage_options'
              2 => string 'options-discussion.php'
          1 => 
            array
              0 => string 'General'
              1 => string 'manage_options'
              2 => string 'options-general.php'
          // etc
    

    Here, we are considering the length of the default items equal to six. Prior to WordPress 3.5, there were 7 items, Privacy is now gone and embedded with the Reading tab.

    Testing locally, this doesn’t produce any unexpected result and works ok. Maybe if a plugin depended on $submenu['options-general.php'][15] to position itself or something else, bugs could happen.

    Sort is being applied separately for the default items and for the rest of them. Just disable the usort of the first block and you have your desired output.

    add_action( 'admin_menu', 'sort_settings_menu_wpse_2331', 999 );
    
    function sort_settings_menu_wpse_2331() 
    {
        global $submenu;
    
        // Sort default items
        $default = array_slice( $submenu['options-general.php'], 0, 6, true );
        usort( $default, 'sort_arra_asc_so_1597736' );
    
        // Sort rest of items
        $length = count( $submenu['options-general.php'] );
        $extra = array_slice( $submenu['options-general.php'], 6, $length, true );
        usort( $extra, 'sort_arra_asc_so_1597736' );
    
        // Apply
        $submenu['options-general.php'] = array_merge( $default, $extra );
    }
    
    //http://stackoverflow.com/a/1597788/1287812
    function sort_arra_asc_so_1597736( $item1, $item2 )
    {
        if ($item1[0] == $item2[0]) return 0;
        return ( $item1[0] > $item2[0] ) ? 1 : -1;
    }
    

    reordered menu items

  2. I was looking for the same thing and figured I would copy code provide by Mike below which he claims is still very much in alpha since its only been him that has been using it.

    I have tested it and it seems to work well for me. I’m sure there are use-cases they do not yet handle.

    Maybe Mike will comment in here and provide additional help on how you can use his class to achieve your goals.

    You can download the file to drop in your theme’s directory here: https://gist.github.com/792b7aa5b695d1092520

    What follows below shows how you might call the functions in your theme’s functions.php file:

    <?php
    require_once('wp-admin-menu-classes.php');
    add_action('admin_menu','my_admin_menu');
    function my_admin_menu() {
      swap_admin_menu_sections('Pages','Posts');              // Swap location of Posts Section with Pages Section
      rename_admin_menu_section('Media','Photos & Video');    // Rename Media Section to "Photos & Video"
      delete_admin_menu_section('Links');                     // Get rid of Links Section
      $movie_tags_item_array = get_admin_menu_item_array('Movies','Movie Tags');  // Save off the Movie Tags Menu
      update_admin_menu_section('Movies',array(               // Rename two Movie Menu Items and Delete the Movie Tags Item
        array('rename-item','item'=>'Movies','new_title'=>'List Movies'),
        array('rename-item','item'=>'Add New','new_title'=>'Add Movie'),
        array('delete-item','item'=>'Movie Tags'),
      ));
      copy_admin_menu_item('Movies',array('Actors','Add New')); // Copy the 'Add New' over from Actors
      renamed_admin_menu_item('Movies','Add New','Add Actor');  // Rename copied Actor 'Add New' to 'Add Actor
      add_admin_menu_item('Movies',array(                       // (Another way to get a 'Add Actor' Link to a section.)
        'title' => 'Alt Add Actor ',
        'slug' => 'post-new.php?post_type=actor',
      ), array(// Add Back the Movie Tags at the end.
        'where'=>'end'
      ));
      add_admin_menu_item('Movies',$movie_tags_item_array,array(// Add Back the Movie Tags at the end.
        'where'=>'end'
      ));
      delete_admin_menu_section('Actors');                      // Finally just get rid of the actors section
    }
    

    What’s more, these functions are even under consideration (as a base) for inclusion in WordPress 3.1 so if we’re lucky these might even become standard!

  3. Thought this could be useful as another option. Here is a simple usort on the global $submenu. I use admin_init hook, because admin_menu may still have other plugins adding submenu items (in my case it did, so that was too early to do sorting). Also note the anonymous (lambda) function in the usort requires PHP 5.3+.

    <?php 
    add_action('admin_init','my_admin_init');
    function my_admin_init() {
        global $submenu;
        $submenu_key = 'my_key'; // See note below on $submenu_key
        if ( array_key_exists( $submenu_key, $submenu ) ) {
            // alphabetize submenu items
            usort( $submenu[ $submenu_key ], function ( $a, $b ) {
                // format of a submenu item is [ 'My Item', 'read', 'manage-my-items', 'My Item' ]
                return strcasecmp( $a[0], $b[0] );
            } );
        }
    }
    

    Note that $submenu_key is $parent_slug in add_submenu_page, which is the same as $menu_slug in add_menu_page.