具有自定义角色的 WordPress 用户无法在没有“create_posts”功能的情况下查看自定义帖子类型的列表页面

Jam*_*ies 6 wordpress wordpress-roles wordpress-capabilities

我正在运行一个 WordPress 5.2.3 站点并且在管理面板中遇到了一些问题。

我有一个自定义角色,我们称之为librarian,还有一个自定义帖子类型,我们称之为book

我想让它librarian可以编辑一个book但不能创建一个新的。

按照另一个问题(WordPress:在自定义帖子类型上禁用“添加新”)和WordPress 文档中的建议,我最终得到了以下代码:

// Custom post type.
register_post_type('book',
    array(
        'labels'                => array(
            'name' => __( 'book' ),
            'singular_name' => __( 'Book' )
        ),
        'capability_type'       => array('book', 'books'),
        'capabilities'          => array(
            'create_posts' => 'do_not_allow' // <-- The important bit.
        ),
        'map_meta_cap'          => true,
        'description'           => 'Book full of pages',
        'exclude_from_search'   => true,
        'publicly_queryable'    => false,
        'show_in_nav_menus'     => false,
        'show_ui'               => true,
        'show_in_menu'          => true,
        'show_in_rest'          => true,
        'menu_icon'             => 'dashicons-location',
        'menu_position'         => 5,
        'supports'              => array('title', 'revisions')
    ));
Run Code Online (Sandbox Code Playgroud)
// Custom role.
add_role('librarian', 'Librarian', array(
    'read'                  => true,
    'edit_books'            => true,
    'edit_published_books'  => true
));
Run Code Online (Sandbox Code Playgroud)

我原以为当我edit.php?post_type=book作为 a访问时,librariran我会看到books要编辑的列表,但我不会看到“添加新”按钮。然而,我实际得到的是一个403回应:

抱歉,您无权访问此页面。

我认为这可能是 WordPress 中的一个错误,因为以下情况:

  • 如果我edit.php?post_type=book作为访问administrator,那么我会根据需要看到没有添加新按钮的列表页面。
  • 如果我为librarian角色赋予edit_posts能力,那么我会根据需要看到没有添加新按钮的列表页面(但我不想赋予他们edit_posts能力!)。

这些让我认为一般设置的自定义帖子类型不是问题。

  • 如果我'create_posts' => 'do_not_allow'book类型注册中删除,则librarian 可以看到列表页面,但它包含“添加新”按钮。

这让我觉得一般设置的自定义角色没有问题。

有没有人遇到过这个问题?我是否错过了配置中的任何内容?或者是否有简单的补丁或解决方法?

任何帮助,将不胜感激!谢谢。

Jam*_*ies 5

看来这是WordPress中的一个错误。我找到了问题的根源和解决方法。

解决方法

如果您对原因不感兴趣,解决方法是在 中注释掉这段装饰性代码wp-admin/includes/menu.php

https://github.com/WordPress/WordPress/blob/master/wp-admin/includes/menu.php#L168

/*
 * If there is only one submenu and it is has same destination as the parent,
 * remove the submenu.
 */
if ( ! empty( $submenu[ $data[2] ] ) && 1 == count( $submenu[ $data[2] ] ) ) {
    $subs      = $submenu[ $data[2] ];
    $first_sub = reset( $subs );
    if ( $data[2] == $first_sub[2] ) {
        unset( $submenu[ $data[2] ] );
    }
}
Run Code Online (Sandbox Code Playgroud)

这意味着以前不显示子菜单的某些菜单项现在将显示(单个项与主菜单项相同),但这只是外观上的 UI 更改。

原因

对于那些想知道细节的人......

访问edit.php?post_type=book未通过此签入wp-admin/includes/menu.php

https://github.com/WordPress/WordPress/blob/master/wp-admin/includes/menu.php#L341

/*
 * If there is only one submenu and it is has same destination as the parent,
 * remove the submenu.
 */
if ( ! empty( $submenu[ $data[2] ] ) && 1 == count( $submenu[ $data[2] ] ) ) {
    $subs      = $submenu[ $data[2] ];
    $first_sub = reset( $subs );
    if ( $data[2] == $first_sub[2] ) {
        unset( $submenu[ $data[2] ] );
    }
}
Run Code Online (Sandbox Code Playgroud)

要将呼叫user_can_access_admin_page()通过对呼叫get_admin_page_parent()

如果子菜单已被删除,则get_admin_page_parent()返回一个空的父级,最终导致在角色的情况下user_can_access_admin_page()错误地返回(角色因不同的原因通过)。falselibrarianadministrator

如果子菜单保留在原位,则get_admin_page_parent()返回一个非空的父菜单,并且访问检查从那里正确进行。

所以根本问题是全局变量$submenu既被用来确定 UI,也被用来决定权限层次结构。除了上面的解决方法之外,我没有看到对这个问题的即时快速修复不会在整个 WordPress 代码的其他地方产生副作用。