問題描述
我想新增幾個編輯 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 輔助翻譯的英文資料結果。如果您對結果不滿意,可以加入我們改善翻譯效果:薇曉朵技術論壇。