如果我理解得很好,在您的站点中扮演特殊角色的用户应该:
能够在所有状态下编辑自己的帖子,但不能发布,只能发送修订,只能在挂起时编辑其他帖子,但不能发布,只能发送修订,不能删除其他帖子,无论状态如何,在我看来,这是一个更类似于“作者”而不是“编辑”的角色。
与作者的唯一区别在于
您角色的用户不能编辑已发布的帖子,即使他们是作者,您角色的用户也可以编辑其他挂起的帖子,但不能发布这些帖子,因此我可以给出的第一个建议是创建一个自定义角色,使用角色“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\' ) );
这里我假设前面的代码在主插件文件中。
我们在上面设置的功能对每个帖子都有效,无论帖子作者或帖子状态如何,现在我们必须允许具有自定义角色的用户在挂起时编辑其他帖子。
我们遇到的第一个问题是在post列表屏幕上(edit.php
), 如果能力edit_others_posts
在未为用户启用的情况下(对于我们的自定义角色,它不是),则其他用户的帖子不会显示在列表中,因为从查询中剥离出来,并且当查询发生时,我们无法访问帖子数据,因此我们只需分配功能,不管帖子状态如何,至少直到查询运行为止。
第二个问题是,在保存时,在将edit_others_posts
cap,我们不仅要检查当前状态是否为“待定”,还要检查用户是否没有尝试更改它。这可以通过查看$_POST
数据这意味着我们需要2个“例程”,一个在管理屏幕上运行(edit.php
和post.php
) 后期保存期间运行的第二个。
为自定义角色用户提供edit_others_post
仅针对挂起的帖子的功能是将筛选器添加到\'user_has_cap\'
.
在筛选器回调中,我们可以实现以下工作流:
检查筛选功能是否是我们要管理的2项功能之一(\'edit-post\'
或\'edit-others-posts\'
, 检查我们是否在admin中,检查用户是否具有我们的自定义功能,并且它不是编辑器或管理员。如果所有这些条件都成立,我们可以继续,否则我们什么都不做,即返回原始功能,检查是否保存,并运行两个不同的例程:保存时运行例程,不保存时运行例程:
检查当前操作是否为编辑帖子从$\\u帖子数据中获取帖子信息,检查帖子是否具有正确的帖子类型并且处于挂起状态,检查挂起状态是否只能由管理员或“真实”编辑器更改如果之前的所有检查都通过,请将\'edit-others-posts\'
能力(\'edit-post\'
将自动映射)不保存时的例程:
检查我们是否处于感兴趣的两个屏幕之一,如果不在,则根据过滤能力的不同,不采取任何行动:\'edit-others-posts\'
我们没有post数据,所以只需在主查询尚未发生之前分配它,并且只在edit.php
当过滤能力为\'edit-post\'
获取post数据,如果post处于挂起状态,则将分配给用户\'edit-others-posts\'
盖(\'edit-post\'
将自动映射)有最后一件事要做。使用所述工作流自定义角色的用户将无法预览其他挂起的帖子,即使他们能够编辑这些帖子。
我们可以再次过滤功能,但有一种更简单的方法:在主查询期间(使用WP_Query
) 我们可以$wp_post_statuses[\'pending\']
object 并设置其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 );
一旦您在插件中包含所有这些代码并将其激活,您只需将插件创建的自定义角色分配给用户。
所有代码都可以作为插件使用here.