When I write f(x) Photo Tag plugin, I think it’s best to put the menu under “Media” because it’s simpler, and also make sense.
Sometimes we want to add custom post type admin menu as sub-menu item on other post type or under settings page, because it make sense (not all post type need to be parent menu), and I like this approach because it make WordPress Admin cleaner.
And Here’s how I do that:
When registering your custom post type, simply change the show_in_menu
args as false
.
register_post_type( 'your-cpt', array( 'description' => '', 'public' => true, 'show_ui' => true, 'show_in_menu' => false, /* Do not show admin menu */ [...] ) );
Now, your CPT will have no link in admin menu.
Note:
before you remove the menu, copy the link of your edit post type menu item, we will add that link in second step.
Your menu link should be something like:
http://your-site/wp-admin/edit.php?post_type=your-cpt
The part we are using is edit.php?post_type=your-cpt
as menu slug in add_submenu_page()
function.
It’s very simple, we simply need to use add_submenu_page()
function via admin_menu
hook.
/* Register Admin Sub Menu */ add_action( 'admin_menu', 'my_cpt_admin_submenu' ); /** * Add admin menu * @link https://developer.wordpress.org/reference/functions/add_submenu_page/ */ function my_cpt_admin_submenu(){ add_submenu_page( 'upload.php', // parent slug 'Your CPT Title', // page title 'Sub Menu Title', // sub-menu title 'edit_posts', // capability 'edit.php?post_type=your-cpt' // your menu menu slug ); }
The args for page title, menu title, caps, etc above need to match your register CPT args.
Or you can make it pull data from your registered CPT object to make it easier:
/* Register Admin Sub Menu */ add_action( 'admin_menu', 'my_cpt_admin_submenu' ); /** * Add admin menu. */ function my_cpt_admin_submenu(){ /* Your CPT */ $cpt = 'your-cpt'; /* Get CPT Object */ $cpt_obj = get_post_type_object( $cpt ); add_submenu_page( 'upload.php', // parent slug $cpt_obj->labels->name, // page title $cpt_obj->labels->menu_name, // menu title $cpt_obj->cap->edit_posts, // capability 'edit.php?post_type=' . $cpt // menu slug ); }
Note:
The code above will add CPT link under “Media” menu. If you want to add it to other parent page, change the “parent slug” args in add_submenu_page() function.
e.g: If you want to add sub menu under the “Page” parent menu, you can change the “parent slug” args to edit.php?post_type=page
and not upload.php
.
Now you should see your CPT link under “Media” menu. But we are not done yet.
When you edit your CPT item, you will notice that the menu is not active. We are going to fix that in next step.
This problem occurs because WordPress did not recognize “Media” as parent menu for “Edit Screen” for your CPT.
So we need to add “Media” as parent for that page using parent_file
filter:
/* Parent Menu Fix */ add_filter( 'parent_file', 'my_cpt_parent_file' ); /** * Fix Parent Admin Menu Item */ function my_cpt_parent_file( $parent_file ){ /* Get current screen */ global $current_screen, $self; /** * Add upload.php as parent file/menu if * it's Post Type list Screen or Edit screen of our post type. */ if ( in_array( $current_screen->base, array( 'post', 'edit' ) ) && 'your-cpt' == $current_screen->post_type ) { $parent_file = 'upload.php'; } return $parent_file; }
UPDATE: 23 July 2016
For “add new” page if you want to highlight the sub menu, you need to also filter “submenu_file”, here’s an example code:
/* Parent Menu Fix */ add_filter( 'submenu_file', 'my_cpt_submenu_file' ); /** * Fix Sub Menu Item Highlights */ function my_cpt_submenu_file( $submenu_file ){ /* Get current screen */ global $current_screen, $self; if ( in_array( $current_screen->base, array( 'post', 'edit' ) ) && 'your-cpt' == $current_screen->post_type ) { $submenu_file = 'edit.php?post_type=your-cpt'; } return $submenu_file; }
Now all done and working perfectly 🙂
Note:
you can use this method for other purposes, not only for post type, etc.
UPDATE (20 June 2016):
Matt Pramschufer ask in the comment about sub-menu in sub-menu. I think I found a simple solution, just add dash when adding sub menu to “fake” the level. Here’s an example:
Okay this is great, but the real tricky part is adding a Child of a Sub Menu. So if you wanted to add an “Add New” to your Photo Tag menu item, how would you go about doing that? Also, I am still running into issues with embedding sub menus and the “Active” state of the WP Admin navigation bar not realizing I am in that “sub menu” area.
if you need submenu for your menu item I suggest to not set the parent as submenu. WP only support one level of admin sub menu.
the “Add New” is available in CPT screen, also in admin bar (if you set it).
about the active state of the menu, I don’t know about that. I didn’t experience that in my CPT (as above). can you share your code?
Hi Matt,
I think I found a solution for multi level menu. Check out the solution in the post above.
Hi David! This is an excellent tutorial, very specific and just what I’ve been looking for.
Thank you for sharing it!
There is no other solution on any other side. It is my pleasure to meet you with this solution.
Thank you very much for sharing it.
Thank you for this, very clear, I used it as a starting point for something much simpler and it guided me in the right direction!
Any idea what the solution might be in 2020 with WP5? I find these options no longer work, unless I’m just implementing it wrongly but in all tests I find I can easily set the “submenu item”, but I can never control the parent item selection. Even when I just filter “parent_file” and always return the same parent link (a custom page) it only works when not posting or editing with the CPT. I suspect there is some code in WP that runs when you’re adding/editing posts that completely overrides any parent_file filtering.
When on new post the current is your edit screen. unfortunatly it does not open the submenu but rather closes it on post-new.php.
heres my JS to open the menu:
var path = window.location.pathname + window.location.search;
jQuery(document).ready( function($) {
if (path.indexOf(“post-new.php?post_type=**YOURCPT**”) >= 0) {
$(“#adminmenu .current”).parents().parents().addClass(‘wp-has-current-submenu’).removeClass(‘wp-not-current-submenu’);
}
});
Your explanations solved my problem. Thank you very much.