最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

Add custom admin menu item for pages using a certain template

programmeradmin2浏览0评论

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 badges
Add a comment  | 

1 Answer 1

Reset to default 4

If 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:

  1. I'm using add_menu_page() to add the menu (which is a top-level menu) with the permission edit_pages and the icon dashicons-admin-page — just check the reference for more details about the syntax and parameters, but the position (the 7th parameter) is set to 19 which would put the menu above the Pages menu.

  2. 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. :)

发布评论

评论列表(0)

  1. 暂无评论