Nếu tôi hiểu rõ, người dùng có vai trò đặc biệt trong trang web của bạn nên:
- Có thể chỉnh sửa bài đăng của riêng mình trong tất cả các trạng thái nhưng 'xuất bản' và không thể xuất bản chúng, chỉ cần gửi để sửa đổi
- Chỉ có thể chỉnh sửa các bài đăng của người khác khi đang chờ xử lý, nhưng không thể xuất bản chúng, chỉ cần gửi để sửa đổi
- Không bao giờ có thể xóa các bài viết khác, bất kể trạng thái
Nếu vậy, dường như đối với tôi một vai trò giống với 'tác giả' hơn là 'biên tập viên'.
Chỉ khác với tác giả là
- người dùng vai trò của bạn không thể chỉnh sửa các bài đăng được xuất bản, ngay cả khi họ là tác giả
- người dùng vai trò của bạn có thể chỉnh sửa các bài đăng khác đang chờ xử lý nhưng không xuất bản chúng
Vì vậy, đề xuất đầu tiên tôi có thể đưa ra là tạo một vai trò tùy chỉnh, sử dụng vai trò 'tác giả' làm điểm thoát, loại bỏ 3 giới hạn không mong muốn và thêm một tùy chỉnh, một lớp đơn giản thực hiện điều đó:
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);
}
}
Hãy thêm hành động kích hoạt / hủy kích hoạt plugin:
register_activation_hook( __FILE__, array( new CustomEditorRole, 'addRole' ) );
register_deactivation_hook( __FILE__, array( new CustomEditorRole, 'removeRole' ) );
Ở đây tôi giả sử mã trước đó là trong tập tin plugin chính.
Các khả năng chúng tôi đặt ở trên là hợp lệ cho mọi bài đăng, bất kể tác giả bài đăng hoặc trạng thái bài đăng, Bây giờ chúng tôi phải cho phép người dùng với vai trò tùy chỉnh của mình để chỉnh sửa bài đăng của người khác khi chờ xử lý.
Vấn đề đầu tiên chúng tôi gặp phải là trên màn hình danh sách bài đăng ( edit.php
), nếu khả năng edit_others_posts
không được kích hoạt cho người dùng (và đối với vai trò tùy chỉnh của chúng tôi thì không), thì các bài đăng của người dùng khác sẽ không được hiển thị trong danh sách, vì bị loại khỏi truy vấn và khi truy vấn xảy ra, chúng tôi không có quyền truy cập vào dữ liệu bài đăng, vì vậy chúng tôi chỉ cần gán khả năng, trạng thái bài đăng quan trọng, ít nhất là cho đến khi truy vấn chạy.
Vấn đề thứ hai là, khi lưu, trước khi cấp cho người dùng với vai trò tùy chỉnh edit_others_posts
giới hạn, chúng tôi phải kiểm tra không chỉ trạng thái hiện tại là "đang chờ xử lý" mà còn người dùng không cố gắng thay đổi nó. Điều đó có thể được thực hiện nhìn vào thông tin trong $_POST
dữ liệu. Điều đó có nghĩa là chúng ta cần 2 "thói quen", một là chạy trên màn hình quản trị ( edit.php
và post.php
) thứ hai chạy trong khi lưu bài.
Cách cung cấp cho người dùng vai trò tùy chỉnh của chúng tôi edit_others_post
khả năng chỉ dành cho các bài đăng đang chờ xử lý là thêm bộ lọc vào 'user_has_cap'
.
Trong cuộc gọi lại bộ lọc, chúng ta có thể thực hiện quy trình công việc này:
- kiểm tra xem khả năng lọc có phải là một trong 2 chúng tôi muốn quản lý không (
'edit-post'
hoặc 'edit-others-posts'
, kiểm tra xem chúng tôi có trong quản trị viên hay không, kiểm tra xem người dùng có khả năng tùy chỉnh của chúng tôi không và đó không phải là trình chỉnh sửa hoặc quản trị viên. tiếp tục, nếu không chúng ta không phải làm gì, tức là trả lại các khả năng ban đầu
- kiểm tra xem chúng tôi có tiết kiệm hay không và chạy 2 thói quen khác nhau:
- Thói quen khi tiết kiệm
- Thường xuyên khi không tiết kiệm
Thói quen khi tiết kiệm:
- kiểm tra xem hành động hiện tại là chỉnh sửa bài
- lấy thông tin bài đăng từ dữ liệu $ _POST, kiểm tra xem bài đăng có đúng loại bài đăng không và đang chờ xử lý
- kiểm tra xem trạng thái chờ xử lý chỉ có thể được thay đổi bởi quản trị viên hoặc biên tập viên "thực"
- nếu tất cả các kiểm tra trước đó vượt qua, hãy gán cho người dùng
'edit-others-posts'
khả năng ( 'edit-post'
sẽ được ánh xạ tự động)
Thói quen khi không tiết kiệm:
- Kiểm tra xem chúng tôi có ở một trong 2 màn hình quan tâm không, nếu không, không làm gì cả
- hành vi khác nhau tùy thuộc vào khả năng lọc:
- Khi khả năng lọc là
'edit-others-posts'
chúng tôi không có dữ liệu bài đăng, vì vậy chỉ cần gán nó nhưng chỉ trước khi truy vấn chính chưa xảy ra và chỉ trên edit.php
màn hình
- khi khả năng lọc được
'edit-post'
lấy dữ liệu bài đăng và nếu bài đăng đang chờ xử lý, hãy gán cho người dùng 'edit-others-posts'
giới hạn ( 'edit-post'
sẽ được ánh xạ tự động)
Có điều cuối cùng phải làm. Sử dụng vai trò tùy chỉnh quy trình công việc được mô tả, người dùng sẽ không thể xem trước các bài đăng đang chờ xử lý khác, ngay cả khi họ có thể chỉnh sửa chúng.
Chúng ta có thể lọc lại khả năng, nhưng có một cách đơn giản hơn: trong khi truy vấn chính (sử dụng một trong số hàng chục hook được kích hoạt WP_Query
), chúng ta chỉ có thể lấy $wp_post_statuses['pending']
đối tượng và đặt thuộc tính của nó public
thành true khi người dùng hiện tại có vai trò tùy chỉnh của chúng ta: hiệu ứng duy nhất là rằng các bài đăng đang chờ xử lý có thể xem trước và một khi chúng tôi không thay đổi bất kỳ khả năng nào, chúng tôi có thể giữ an toàn.
Ok, chỉ cần dịch các từ trong mã:
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,
) );
}
}
Và thêm 2 móc liên quan: một để lọc 'user_has_cap'
, một để đảm bảo trạng thái chờ xử lý chỉ có thể được thay đổi bởi quản trị viên hoặc người chỉnh sửa thực và bộ lọc cuối cùng 'posts_results'
để cho phép xem trước:
$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 );
Khi bạn có tất cả mã này trong một plugin và bạn kích hoạt nó, bạn chỉ phải gán cho người dùng mà plugin vai trò tùy chỉnh tạo ra.
Tất cả mã có sẵn dưới dạng plugin, trong Gist ở đây .