问题描述
我想添加几个编辑 can edit_others_posts
,但我不希望他们能够发布其他帖子,只保存发布,点击提交审查按钮。
我该怎么做?
编辑:详细解释这一点。目前,我们无法允许用户通过仅保存帖子来编辑其他帖子。如果为用户启用了 edit_others_post
,则可以发布该帖子。
工作流程我的目标是:
-
编辑者只能编辑其他待处理的帖子 (solved here) 。
-
编辑可以保存挂起的帖子,但不发布。所以提交审查按钮将可用于他们 (这是一个职位待处理模式下的”update post” 按钮)
最佳解决方案
如果我理解得很好,用户在您的站点中有特殊角色应该:
-
能够编辑自己的所有状态的帖子,但’publish’ 并不能发布,只需发送修改
-
只能在待机时编辑其他帖子,但无法发布,只需发送修改
-
永远不能删除其他职位,无论状态如何
如果是这样,在我看来似乎更像’author’ 那么’editor’ 。
只有与作者的区别才是
-
您的角色的用户无法编辑已发布的帖子,即使他们是作者
-
您角色的用户可以编辑其他待处理的帖子,但不会发布
所以我可以给出的第一个建议是创建自定义角色,使用角色’author’ 作为点,删除 3 个不需要的上限并添加自定义的一个,一个简单的类:
class CustomEditorRole {
private static $role = 'authorplus';
private $role_label;
function __construct() {
// here we need a real, loaded, text domain
$this->role_label = __( 'Author +', 'yout-txt-dmn' );
}
function addRole() {
global $wp_roles;
if ( ! $wp_roles instanceof WP_Roles ) {
$wp_roles = new WP_Roles;
}
$author = get_role( 'author' );
$caps = $author->capabilities; // start with author capabilities
$caps['publish_posts'] = FALSE;
$caps['edit_published_posts'] = FALSE;
$caps['delete_published_posts'] = FALSE;
$caps['edit_others_pending_posts'] = TRUE; // custom cap
// create new role with custom caps
add_role( self::$role, $this->role_label, $caps );
}
function removeRole() {
global $wp_roles;
if ( ! $wp_roles instanceof WP_Roles ) {
$wp_roles = new WP_Roles;
}
remove_role(self::$role);
}
}
让我们添加插件激活/停用操作:
register_activation_hook( __FILE__, array( new CustomEditorRole, 'addRole' ) );
register_deactivation_hook( __FILE__, array( new CustomEditorRole, 'removeRole' ) );
这里我假设以前的代码是在主要的插件文件。
我们上面设置的功能对于每个帖子都是有效的,无论发布作者还是帖子状态。现在,我们必须允许用户使用自定义角色来编辑其他的帖子。
我们遇到的第一个问题是,在帖子列表屏幕 (edit.php
) 上,如果功能 edit_others_posts
没有为用户启用 (而且对于我们的自定义角色而言),则其他用户的帖子不会显示在列表中,因为已删除从查询出发,当发生查询时,我们无法访问帖子数据,因此我们必须至少分配能力,mo 事件状态,至少直到查询运行。
第二个问题是,在保存之前,给予用户自定义角色的 edit_others_posts
上限,我们不仅要检查当前的状态是”pending”,而且用户还没有尝试改变它。可以在 $_POST
数据中查看信息。这意味着我们需要 2 个”routines”,一个在管理屏幕 (edit.php
和 post.php
) 上运行,在后期保存期间运行。
给我们的自定义角色用户的方式只有待处理的帖子 edit_others_post
功能是添加一个过滤器到'user_has_cap'
。
在过滤器回调中,我们可以实现此工作流程:
-
检查过滤功能是否为我们要管理的 2(
'edit-post'
或'edit-others-posts'
),检查我们是否在管理员中,检查用户是否具有我们的自定义功能,而不是编辑器或管理员,如果所有条件都为真我们可以继续,否则我们不得不做任何事情,即返回原始能力 -
检查我们是否保存,并运行 2 个不同的例程:
-
保存时常常
-
不保存时的例行程序
-
保存时常常:
-
检查当前操作是否是编辑帖子
-
从 $ _POST 数据获取帖子信息,检查帖子是否具有正确的帖子类型并处于待处理状态
-
检查挂起状态只能由管理员或”real” 编辑器更改
-
如果所有以前的检查通过,分配给用户
'edit-others-posts'
功能 ('edit-post'
将自动映射)
不保存的例程
-
检查我们是否在两个感兴趣的屏幕之一,如果不是,什么都不做
-
不同的行为取决于过滤能力:
-
当过滤能力是
'edit-others-posts'
时,我们没有发布数据,所以只需要分配它,但只有在主查询之前还没有发生,只有在edit.php
屏幕上 -
当过滤能力是
'edit-post'
获取帖子数据,并且如果帖子待处理分配给用户'edit-others-posts'
上限 ('edit-post'
将被自动映射)
-
有最后要做的事情。使用所描述的工作流自定义角色,用户将无法预览其他待处理的帖子,即使他们能够编辑它们。
我们可以再次过滤功能,但是有一个更简单的方法:在主查询 (使用 WP_Query
触发的十几个钩子中的一个) 时,我们可以在 $wp_post_statuses['pending']
对象中设置 public
属性为 true,当前用户具有自定义角色:唯一的效果是待处理的帖子是可预览的,一旦我们不改变任何能力,我们就能保持安全。
好的,只是在代码中翻译单词:
class CustomEditorCaps {
function manageCaps( $allcaps, $caps, $args, $user ) {
if ( ! $this->shouldManage( $args[0], $user ) ) {
return $allcaps;
}
// Are we saving?
$action = filter_input( INPUT_POST, 'action', FILTER_SANITIZE_STRING );
$method = strtoupper(filter_var($_SERVER['REQUEST_METHOD'], FILTER_SANITIZE_STRING ));
if ( $method !== 'POST' ) { // not saving
global $pagenow;
// we are interested only on post list and post edit screens
if (
is_admin()
&& in_array( $pagenow, array( 'post.php', 'post-new.php', 'edit.php' ), TRUE
) ) {
$screen_id = $pagenow === 'edit.php' ? 'edit-post' : 'post';
$allcaps = $this->maybeAllow( $args, $allcaps, $user, $screen_id );
}
} elseif ( $action === 'editpost' ) { // saving and right action
$allcaps = $this->maybeAllowOnSave( $args, $allcaps, $user );
}
return $allcaps; // always return: it's a filter
}
function lockPendingStatus( $data, $postarr ) {
if (
isset( $postarr['ID'] )
&& ! empty($postarr['ID'])
&& $data['post_type'] === 'post' // 'post' post type
&& $data['post_status'] !== 'pending' // a non pending status
&& ! current_user_can( 'delete_others_posts' ) // current user is not an admin
) {
$orig = get_post_status( $postarr['ID'] );
if ( $orig === 'pending' ) { // hey post was pending!
$data['post_status'] = 'pending'; // let's restore pending status
}
}
return $data; // always return: it's a filter
}
function allowPreview( $posts, $query ) {
if ( is_admin()
|| ! $query->is_main_query()
|| empty( $posts )
|| ! $query->is_single
|| $posts[0]->post_type !== 'post'
) {
return $posts; // return first argument: it's a filter
}
$status = get_post_status( $posts[0] );
$post_status_obj = get_post_status_object( $status );
if (
! $post_status_obj->public
&& $status === 'pending'
&& current_user_can('edit_others_pending_posts')
) {
// post is pending and our user has our special role
// allow preview
global $wp_post_statuses;
$wp_post_statuses[$status]->public = TRUE;
}
return $posts; // return first argument: it's a filter
}
private function maybeAllow( $args, $allcaps, $user, $screen ) {
if ( $args[0] === 'edit_others_posts' ) {
// if filtering 'edit_others_posts' we have no access to single post data
// allow cap only on post list screen and before querying posts
$allcaps['edit_others_posts'] = ! did_action('pre_get_posts')
&& $screen === 'edit-post';
return $allcaps;
}
$post = get_post( $args[2] );
if ( $post->post_status === 'pending' ) {
$allcaps['edit_others_posts'] = TRUE;
}
return $allcaps; // always return: it's a filter
}
private function maybeAllowOnSave( $args, $allcaps, $user ) {
$data = $this->getPostedData();
if ( $data['post_type'] !== 'post' || (int) $data['post_ID'] <= 0 ) {
return $allcaps;
}
$post = get_post( $data['post_ID'] );
if (
$post->post_status === 'pending'
&& $data['original_post_status'] === 'pending'
&& ( empty( $data['post_status'] ) || $data['post_status'] === 'pending' )
) {
// if post is pending and will stay pending allow editing
$allcaps['edit_others_posts'] = true;
}
return $allcaps;
}
private function shouldManage( $cap, $user ) {
return is_admin() // not affect frontend
&& in_array( $cap, array( 'edit_others_posts', 'edit_post' ), TRUE )
&& ! $user->has_cap( 'delete_others_posts' ) // real editor or more
&& $user->has_cap( 'edit_others_pending_posts' ) // our role
&& ! defined( 'DOING_AJAX' ); // does not affect ajax
}
private function getPostedData() {
return filter_input_array( INPUT_POST, array(
'post_type' => FILTER_SANITIZE_STRING,
'post_ID' => FILTER_SANITIZE_NUMBER_INT,
'original_post_status' => FILTER_SANITIZE_STRING,
'post_status' => FILTER_SANITIZE_STRING,
) );
}
}
并添加 2 个相关的钩子:一个用于过滤'user_has_cap'
,一个用于确保挂起状态只能由管理员或实际编辑者更改,最后一次过滤'posts_results'
以允许预览:
$cap_manager = new CustomEditorCaps;
add_filter( 'user_has_cap', array( $cap_manager, 'manageCaps' ), PHP_INT_MAX, 4 );
add_filter( 'posts_results', array( $cap_manager, 'allowPreview' ), 10, 2 );
add_filter( 'wp_insert_post_data', array( $cap_manager, 'lockPendingStatus' ), 10, 2 );
一旦将所有这些代码插入到插件中并激活它,您只需要为自定义角色插件创建的用户分配。
所有代码可用作插件,在 Gist here 中。
参考文献
注:本文内容整合自 Google/Baidu/Bing 辅助翻译的英文资料结果。如果您对结果不满意,可以加入我们改善翻译效果:薇晓朵技术论坛。