web-dev-qa-db-ja.com

ユーザーに "edit_others_posts"による保存のみを許可し、公開は許可しない

canedit_others_postsというエディタをいくつか追加したいのですが、publishothersの投稿を許可したくない、saveだけにします。 レビュー用に送信 ボタンをクリックして投稿します。

これどうやってするの?

編集:これを詳細に説明する。現在のところ、ユーザーにonlyによる投稿の編集による他の投稿の編集を許可することはできません。ユーザに対してedit_others_postが有効になっている場合は、publish投稿できます。

私が目指しているワークフロー

  1. 編集者は保留中の他の投稿のみ編集できます( ここで解決 )。
  2. 編集者は保留中の投稿を保存することができますしかしそれを公開しないでください。そのため、 レビューのために送信 ボタンが利用可能になります(投稿が保留モードの場合は「投稿の更新」ボタンです)。
7

私がよく理解しているならば、あなたのサイトで特別な役割を持っているユーザーは以下をするべきです:

  • すべてのステータスで自分の投稿を編集することはできますが「公開」し、公開することはできません。改訂のために送信するだけです
  • 保留中の場合にのみ他の投稿を編集できますが、それらを公開することはできません。改訂のために送信するだけです
  • ステータスに関係なく、他の投稿を削除することはできません

もしそうなら、それは私には '作者'より '編集者'に似た役割のようです。

作者との違いはそれだけです

  • 自分のロールのユーザーは、投稿者が投稿者であっても、公開された投稿を編集できません
  • 自分のロールのユーザーは保留中の他の投稿を編集できますが公開はできません

最初に提案できるのは、ロール '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がそのユーザーに対して有効になっていない(そして私たちのカスタムロールでは無効になっている)場合、他のユーザーによる投稿はリストに表示されないことです。クエリから除外され、クエリが発生したときには投稿データにアクセスできないため、少なくともクエリが実行されるまで、投稿ステータスに関係なく機能を割り当てる必要があります。

2つ目の問題は、保存時に、カスタムロールを持つユーザーにedit_others_postsキャップを付与する前に、現在のステータスが「保留」であることだけでなく、ユーザーがそれを変更しようとしていないことも確認する必要があることです。それは$_POSTデータ内の情報を見ることで行うことができます。 2つの "ルーチン"が必要です。1つは管理画面(edit.phppost.php)で実行され、もう1つはポストセーブ中に実行されます。

保留中の投稿に対してのみedit_others_post機能をカスタムロールユーザーに付与する方法は、'user_has_cap'にフィルタを追加することです。

フィルタコールバックの内側に、このワークフローを実装できます。

  1. フィルタリングする機能が管理したい2つの機能のいずれかであるかどうかを確認します('edit-post'または'edit-others-posts'、管理者であるかどうか、ユーザーがカスタム機能を持っているかどうかを確認します)。それ以外の場合は何もしない、つまり元の機能を返す
  2. 保存しているかどうかを確認し、2つの異なるルーチンを実行します。
    • 保存時のルーチン
    • 保存しないときのルーチン

保存時のルーチン:

  1. 現在のアクションが編集投稿であることを確認してください
  2. $ _POSTデータから投稿情報を取得し、投稿が正しい投稿タイプで保留中かどうかを確認します
  3. 保留中のステータスが管理者または "本物の"エディタによってのみ変更可能であることを確認する
  4. 前のチェックがすべて合格した場合は、'edit-others-posts'機能をユーザーに割り当てます('edit-post'は自動的にマッピングされます)。

保存しないときのルーチン:

  1. 興味のある2つの画面のうちの1つにいることを確認し、そうでない場合は何もしません。
  2. フィルタリング能力に応じて異なる動作:
    • フィルタリング機能が'edit-others-posts'の場合、投稿データはありませんので、主クエリが実行される前で、edit.php画面上でのみ割り当てます。
    • フィルタリング機能が'edit-post'の場合は投稿データを取得し、投稿が保留中の場合は'edit-others-posts'キャップを割り当てます('edit-post'は自動的にマッピングされます)

最後にやることがあります。説明されているワークフローのカスタムロールを使用すると、編集可能であっても、保留中の他の投稿をプレビューすることはできません。

ケーパビリティを再度フィルタリングすることもできますが、もっと簡単な方法があります。メインクエリの間に(WP_Queryによって起動される多数のフックのうちの1つを使用)、 $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つの関連するフックを追加します。1つは'user_has_cap'をフィルタリングするためのもの、もう1つは管理者または実際の編集者のみ保留中のステータスを変更できるようにするためのもの、最後のフィルタリングはプレビューを許可する'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 );

このコードをすべてプラグインに入れてアクティブにしたら、プラグインが作成したカスタムロールをユーザーに割り当てるだけで済みます。


要旨 ここ の中で、プラグインとして利用可能なすべてのコード。

8
gmazzap