允许用户“EDIT_OTHERS_POSTS”只保存,不能发布

时间:2014-09-11 作者:Christine Cooper

我想添加几个编辑器can edit_others_posts, 但我不希望他们能够publish 仅限其他帖子save 单击“提交以供审阅”按钮,即可发布帖子。

我该怎么做?

编辑:详细解释这一点。目前,我们不允许用户编辑其他帖子only 保存帖子。如果edit_others_post 为用户启用,则他们可以publish 邮报。

我的目标工作流程:

编辑只能编辑其他挂起的帖子(solved here). but 不发布它。因此,他们可以使用“提交审核”按钮(当帖子处于挂起模式时,这是“更新帖子”按钮)

1 个回复
SO网友:gmazzap

如果我理解得很好,在您的站点中扮演特殊角色的用户应该:

能够在所有状态下编辑自己的帖子,但不能发布,只能发送修订,只能在挂起时编辑其他帖子,但不能发布,只能发送修订,不能删除其他帖子,无论状态如何,在我看来,这是一个更类似于“作者”而不是“编辑”的角色。

与作者的唯一区别在于

您角色的用户不能编辑已发布的帖子,即使他们是作者,您角色的用户也可以编辑其他挂起的帖子,但不能发布这些帖子,因此我可以给出的第一个建议是创建一个自定义角色,使用角色“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.phppost.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.

结束