問題描述

我想添加幾個編輯 can edit_others_posts,但我不希望他們能夠發佈其他帖子,只保存發佈,點擊提交審查按鈕。

我該怎麼做?

編輯:詳細解釋這一點。目前,我們無法允許用户通過僅保存帖子來編輯其他帖子。如果為用户啓用了 edit_others_post,則可以發佈該帖子。

工作流程我的目標是:

  1. 編輯者只能編輯其他待處理的帖子 (solved here) 。

  2. 編輯可以保存掛起的帖子,但不發佈。所以提交審查按鈕將可用於他們 (這是一個職位待處理模式下的”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.phppost.php) 上運行,在後期保存期間運行。

給我們的自定義角色用户的方式只有待處理的帖子 edit_others_post 功能是添加一個過濾器到'user_has_cap'

在過濾器回調中,我們可以實現此工作流程:

  1. 檢查過濾功能是否為我們要管理的 2('edit-post''edit-others-posts'),檢查我們是否在管理員中,檢查用户是否具有我們的自定義功能,而不是編輯器或管理員,如果所有條件都為真我們可以繼續,否則我們不得不做任何事情,即返回原始能力

  2. 檢查我們是否保存,並運行 2 個不同的例程:

    • 保存時常常

    • 不保存時的例行程序

保存時常常:

  1. 檢查當前操作是否是編輯帖子

  2. 從 $ _POST 數據獲取帖子信息,檢查帖子是否具有正確的帖子類型並處於待處理狀態

  3. 檢查掛起狀態只能由管理員或”real” 編輯器更改

  4. 如果所有以前的檢查通過,分配給用户'edit-others-posts'功能 ('edit-post'將自動映射)

不保存的例程

  1. 檢查我們是否在兩個感興趣的屏幕之一,如果不是,什麼都不做

  2. 不同的行為取決於過濾能力:

    • 當過濾能力是'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 輔助翻譯的英文資料結果。如果您對結果不滿意,可以加入我們改善翻譯效果:薇曉朵技術論壇。