This is not an exact answer, but I think there are enough elements to build the desired output.
I sort the menu manipulating the global
$menu, not really best practice, but for now it works.
The result of this example is moving Links Manager, Comments and Media Library to the end of the first block. The effect is: first all Post Types then these items.
We need a recursive array search function to locate the key of each item,
, etc. This way we can be sure of being targeting the right item and don't need to inspect the menu to confirm each one's key.
The first thing is to grab the position of the second separator (
separator2), as we are going to move everything just before it. Then we search for each item that we want to move, make a copy of it and unset from the
$menu. Finally, we subtract the number of "movable" items from the position of the separator (say 100) and add our items in the positions 97, 98, 99 (in this example we're moving 3 items).
add_action( 'admin_menu', 'sort_cpts_wpse_99459', 9999 );
$mod_menu = array();
$separator = b5f_recursive_array_search( 'separator2', $menu );
$links = b5f_recursive_array_search( 'link-manager.php', $menu );
$upload = b5f_recursive_array_search( 'upload.php', $menu );
$comments = b5f_recursive_array_search( 'edit-comments.php', $menu );
if( $links )
$mod_menu['links'] = $menu[ $links ];
unset( $menu[ $links ] );
if( $upload )
$mod_menu['upload'] = $menu[ $upload ];
unset( $menu[ $upload ] );
if( $comments )
$mod_menu['comments'] = $menu[ $comments ];
unset( $menu[ $comments ] );
$position_menu = (int)$separator - count( $mod_menu );
foreach( $mod_menu as $m )
$menu[ $position_menu ] = $m;
function recursive_array_search( $needle, $haystack )
foreach( $haystack as $key => $value )
$current_key = $key;
$needle === $value
is_array( $value )
&& b5f_recursive_array_search( $needle, $value ) !== false