I am looking to add a new, custom menu item to the WordPress admin sidebar that displays pages that use a certain template. For example: a group of pages uses the template called "Retailer Sendout" (page-retailer-sendout.php) would be displayed below the default "Pages" top-level menu item.
I have tried the following URL combinations, but none of them have worked so far.
- /wp-admin/edit.php?post_type=page&page_template=page-retailer-sendout
- /wp-admin/edit.php?post_type=pagetemplate=page-retailer-sendout
- /wp-admin/edit.php?post_type=page&page_template=retailer_sendout
I have also tested some admin menu customization plugins, but they do not offer the ability to link to pages with a specific template name.
I am comfortable with adding a custom function to the theme's functions.php file, but I do not know where to start.
Any help is greatly appreciated.
I am looking to add a new, custom menu item to the WordPress admin sidebar that displays pages that use a certain template. For example: a group of pages uses the template called "Retailer Sendout" (page-retailer-sendout.php) would be displayed below the default "Pages" top-level menu item.
I have tried the following URL combinations, but none of them have worked so far.
- http://domain.test/wp-admin/edit.php?post_type=page&page_template=page-retailer-sendout
- http://domain.test/wp-admin/edit.php?post_type=pagetemplate=page-retailer-sendout
- http://domain.test/wp-admin/edit.php?post_type=page&page_template=retailer_sendout
I have also tested some admin menu customization plugins, but they do not offer the ability to link to pages with a specific template name.
I am comfortable with adding a custom function to the theme's functions.php file, but I do not know where to start.
Any help is greatly appreciated.
Share Improve this question asked Mar 26, 2020 at 21:50 Mike HermaryMike Hermary 2193 silver badges11 bronze badges1 Answer
Reset to default 4If this is what you want:
... you can do it like so:
Step #1: Add the custom menu item (Retailer Sendout
).
function add_retailer_sendout_admin_menu() {
$slug = 'edit.php?post_type=page&template=page-retailer-sendout.php';
add_menu_page( 'Retailer Sendout', 'Retailer Sendout', 'edit_pages', $slug,
'', 'dashicons-admin-page', 19 );
}
add_action( 'admin_menu', 'add_retailer_sendout_admin_menu' );
Notes:
I'm using
add_menu_page()
to add the menu (which is a top-level menu) with the permissionedit_pages
and the icondashicons-admin-page
— just check the reference for more details about the syntax and parameters, but the position (the 7th parameter) is set to19
which would put the menu above thePages
menu.If you change the name of the query string
template
, you also need to change it in both steps #2 and #3 below. But if you change just the value, you only need to also change it in step #2 below.
Step #2: Highlight the menu item (this adds current
to the item/li
class
) after you clicked on the link.
Because the 'Pages' and 'Pages » All Pages' menu slug is edit.php?post_type=page
, we have to make sure WordPress doesn't override the $parent_file
(i.e. the above $slug
value) as set in highlight_retailer_sendout_admin_menu()
below, so the fix_admin_parent_file_override()
, although is a hack/trick, is needed — you could just add the CSS class current
to the Retailer Sendout
menu, but you'd end up with two/three highlighted menus..
And even though I'm not changing the CSS class
, you can use the function to add your custom class(es) and/or remove/edit existing ones. Also, just so you know, the $menu
is an array of the top-level menus only, so I use the $submenu
to access the sub-menus. I also use $pagenow
because at this point, the current screen (see get_current_screen()
) hasn't been setup yet.
function fix_admin_parent_file_override( $menu ) {
global $pagenow, $submenu;
// If we're NOT on the edit.php page, do nothing.
if ( ! is_admin() || 'edit.php' !== $pagenow ) {
return $menu;
}
// Same as in the above add_retailer_sendout_admin_menu().
$slug = 'edit.php?post_type=page&template=page-retailer-sendout.php';
// We have to make sure the $parent_file is not overriden (by WordPress) to
// the 'Pages' menu.
if ( ! empty( $_GET['template'] ) &&
'page-retailer-sendout.php' === $_GET['template'] ) {
// Change the 'Pages' URL.
$menu[20][2] .= '&template=';
$submenu[ $menu[20][2] ] = $submenu['edit.php?post_type=page'];
// Change the 'All Pages' URL.
$submenu[ $menu[20][2] ][5][2] = $menu[20][2];
unset( $submenu['edit.php?post_type=page'] );
}
return $menu;
}
add_filter( 'add_menu_classes', 'fix_admin_parent_file_override' );
And next, we set the $parent_file
— note that in the above, we don't set the $parent_file
, we only make sure WordPress doesn't override the value afterwards. So for this part, we should use the parent_file
hook:
function highlight_retailer_sendout_admin_menu( $parent_file ) {
// Make sure the PARENT slug is the one for the 'Pages' / 'All Pages' menu.
if ( 'edit.php?post_type=page' === $parent_file &&
( ! empty( $_GET['template'] ) ) &&
// ..and make sure the template being queried is page-retailer-sendout.php.
'page-retailer-sendout.php' === $_GET['template'] )
{
// Return the $slug value as in the above add_retailer_sendout_admin_menu()
return 'edit.php?post_type=page&template=page-retailer-sendout.php';
}
return $parent_file;
}
add_filter( 'parent_file', 'highlight_retailer_sendout_admin_menu' );
Step #3: Filter the pages by the template.
The template name is stored in a private metadata named _wp_page_template
, so we filter the posts/pages query by adding a meta query for that metadata.
And we're using the pre_get_posts
hook which also runs on the public side of the site, so we perform checks like is_admin()
and is_main_query()
to prevent messing with other WP_Query
calls (or posts requests).
function filter_admin_pages_by_template( $query ) {
// Make sure we run the filter only on the admin side of the site (wp-admin)
// and that the query is the main query.
if ( is_admin() && $query->is_main_query() &&
( ! empty( $_GET['template'] ) ) &&
// ..and also, check if we're on the edit.php?post_type=page screen.
'edit-page' === get_current_screen()->id )
{
$meta_query = (array) $query->get( 'meta_query' );
$meta_query[] = array(
'key' => '_wp_page_template',
'value' => $_GET['template'],
);
$query->set( 'meta_query', $meta_query );
}
}
add_action( 'pre_get_posts', 'filter_admin_pages_by_template' );
And actually, you can use the code to filter any templates.. not just the page-retailer-sendout.php
as in the question. :)