How do I code access to the built-in UI of a CPT when it’s placed as submenu of another CPT that is protected by role?

From the codex

The function which is hooked in to handle the output of the page must check that the user has the required ‘capability’ as well.

What it fails to mention is that the check for subpages iterates upwards to its parent. If a user is not capable of accessing TEACHERS, it will not be able to access the sub-pages that appear under it in the admin menu. (I have no idea what benefits exist in this functionality, and I think it’s likely a bug.)

add_menu_page() is your answer. What it does not say in the codex is that it will implement the functionality that you desire if you pass its slug to the custom post type’s show_in_menu argument of register_post_type() (as opposed to the page’s hook suffix returned by add_menu_page()). In order to get this functionality, a submenu item must be added before the parent menu item, such as by way of registering a post type. (In this way, the parent menu item will take on the characteristics of the first sub-menu item added. Pay attention here in that TEACHERS is added before STUDENTS so it will appear above it in the menu order, and the SCHOOL item is added after the CPT submenu items are added.) So, here are the changes to make to your code so it will work the way you want it to:

public static function register_cpt() {
    register_post_type('teacher', array(
        'labels' => array(
            ...
        //  'all_items' => __('TEACHERS'),      // Remove
            'menu_name' => __('TEACHERS'),      // Change
        ),
        'show_in_menu'      => 'school',        // Add
        ...
    ));
    register_post_type('student', array(
        ...
        'show_in_menu'      => 'school',        // Change
    ));
}

public static function modify_menu() {          // Change
    $pg = add_menu_page(
        NULL,                                   // Page name will not be displayed.
        'SCHOOL',                               // Formerly register_post_type('teacher')->menu_name.
        'administrator',                        // Lowest level of permission required to be displayed.
        'school'                                // Slug (used in register_post_type).
    //  ,NULL                                   // Page callback omitted as will not be called.
    );
    // I think you were passing $pg to show_in_menu, but this will not work
    // The page slug (school) must be used instead.
}

UPDATE: I submitted a bug ticket about this issue to WordPress core. Also, take notice that whatever page the subpages are nested under must also have the permissions of their parents even though they are displayed as capable of being displayed. So, the lowest level permission required is what is given in the add_menu_page method. In example, the new SCHOOL admin menu page will not allow the UI of STUDENTS to be displayed if it’s capability is educator, exactly like what is happening with TEACHERS in your original example.

Leave a Comment