Разрешение пользователю редактировать только определенные страницы
Я хотел бы разрешить определенному пользователю редактировать только одну страницу и её подстраницы. Как это можно реализовать? Я пробовал старый плагин Role Scoper, но похоже, что в нем много проблем и ошибок.

Первое, что нужно сделать для реализации такой задачи - это определить, какие страницы пользователь может редактировать.
Есть несколько способов сделать это. Это может быть пользовательская метаданная, какое-то значение конфигурации... Для целей данного ответа я предположу, что существует функция вроде этой:
function wpse_user_can_edit( $user_id, $page_id ) {
$page = get_post( $page_id );
// найдем самую верхнюю страницу в иерархии
while( $page && (int) $page->parent ) {
$page = get_post( $page->parent );
}
if ( ! $page ) {
return false;
}
// теперь $page - это верхняя страница в иерархии
// как определить, может ли пользователь редактировать её - зависит от вашей реализации...
}
Теперь, когда у нас есть способ определить, может ли пользователь редактировать страницу, нам нужно сказать WordPress использовать эту функцию для проверки прав пользователя на редактирование страницы.
Это можно сделать с помощью фильтра 'map_meta_cap'
.
Например:
add_filter( 'map_meta_cap', function ( $caps, $cap, $user_id, $args ) {
$to_filter = [ 'edit_post', 'delete_post', 'edit_page', 'delete_page' ];
// Если проверяемая способность нас не интересует, просто возвращаем текущее значение
if ( ! in_array( $cap, $to_filter, true ) ) {
return $caps;
}
// Первый элемент в массиве $args должен быть ID страницы
if ( ! $args || empty( $args[0] ) || ! wpse_user_can_edit( $user_id, $args[0] ) ) {
// Пользователю не разрешено, сообщим об этом WordPress
return [ 'do_not_allow' ];
}
// В противном случае просто возвращаем текущее значение
return $caps;
}, 10, 4 );
На этом этапе нам осталось только найти способ связать пользователя с одной или несколькими страницами.
В зависимости от конкретного случая могут быть разные решения.
Гибким решением может быть добавление выпадающего списка "корневых" страниц (см. wp_dropdown_pages
) на экране редактирования пользователя в админке и сохранение выбранных страниц как пользовательских метаданных.
Мы можем использовать 'edit_user_profile'
для добавления поля выбора страниц и 'edit_user_profile_update'
для сохранения выбранного значения как метаданных пользователя.
Уверен, что на этом сайте достаточно руководств о том, как сделать это подробно.
Когда страницы сохранены как метаданные пользователя, функцию wpse_user_can_edit()
из предыдущего примера можно завершить, проверив, есть ли ID страницы среди значений метаданных пользователя.
Убрав возможность редактирования страницы, WordPress сделает все остальное: уберет ссылки на редактирование в админке и на фронтенде, предотвратит прямой доступ... и так далее.

Это намного лучше, чем мой ответ. Зачем ограничивать ссылки на редактирование, если можно просто изменить права пользователя и позволить WordPress разобраться с остальным?

Для реализации этой функции требуется небольшое количество кода, даже если вы используете PHP-класс, чтобы избежать глобальных переменных. Я также не хотел скрывать запрещенные страницы для пользователя в админке. Что, если они добавили контент, который уже есть на сайте?
$user_edit_limit = new NS_User_Edit_Limit(
15, // ID пользователя, которого мы хотим ограничить
[2, 17] // Массив ID родительских страниц, которые пользователь может редактировать
(также принимает ID подстраниц)
);
class NS_User_Edit_Limit {
/**
* Хранит ID пользователя, которого мы хотим ограничить, и
* записи, которые разрешено редактировать.
*/
private $user_id = 0;
private $allowed = array();
public function __construct( $user_id, $allowed ) {
// Сохраняем ID пользователя, которого ограничиваем
$this->user_id = $user_id;
// Расширяем список разрешенных страниц, включая подстраницы
$all_pages = new WP_Query( array(
'post_type' => 'page',
'posts_per_page' => -1,
) );
foreach ( $allowed as $page ) {
$this->allowed[] = $page;
$sub_pages = get_page_children( $page, $all_pages );
foreach ( $sub_pages as $sub_page ) {
$this->allowed[] = $sub_page->ID;
}
}
// Для запрещенного пользователя...
// Удаляем ссылку редактирования на фронтенде, если нужно
add_filter( 'get_edit_post_link', array( $this, 'remove_edit_link' ), 10, 3 );
add_action( 'admin_bar_menu', array( $this, 'remove_wp_admin_edit_link' ), 10, 1 );
// Удаляем ссылку редактирования в админке, если нужно
add_action( 'page_row_actions', array( $this, 'remove_page_list_edit_link' ), 10, 2 );
}
/**
* Вспомогательные функции для проверки, является ли текущий пользователь
* тем, кого мы ограничиваем, и проверяет, разрешено ли редактировать
* конкретную запись.
*/
private function is_user_limited() {
$current_user = wp_get_current_user();
return ( $current_user->ID == $this->user_id );
}
private function is_page_allowed( $post_id ) {
return in_array( $post_id, $this->allowed );
}
/**
* Удаляет ссылку редактирования на фронтенде, если нужно.
*/
public function remove_edit_link( $link, $post_id, $test ) {
/**
* Если:
* - Запрещенный пользователь вошел в систему
* - Страница, для которой создается ссылка редактирования, не в списке разрешенных
* ...возвращаем пустую $link. Это также приводит к тому, что edit_post_link() ничего не выводит.
*
* В противном случае возвращаем ссылку как обычно.
*/
if ( $this->is_user_limited() && !$this->is_page_allowed( $post_id ) ) {
return '';
}
return $link;
}
/**
* Удаляет ссылку редактирования из панели администратора WordPress
*/
public function remove_wp_admin_edit_link( $wp_admin_bar ) {
/**
* Если:
* - Мы на странице (не в архиве)
* - Запрещенный пользователь вошел в систему
* - Страница не в списке разрешенных
* ...удаляем ссылку редактирования из панели администратора
*/
if (
is_page() &&
$this->is_user_limited() &&
!$this->is_page_allowed( get_post()->ID )
) {
$wp_admin_bar->remove_node( 'edit' );
}
}
/**
* Удаляет ссылку редактирования из списка страниц в админке (edit.php)
*/
public function remove_page_list_edit_link( $actions, $post ) {
/**
* Если:
* - Запрещенный пользователь вошел в систему
* - Страница не в списке разрешенных
* ...удаляем быстрые ссылки "Редактировать", "Быстрое редактирование" и "Удалить".
*/
if (
$this->is_user_limited() &&
!$this->is_page_allowed( $post->ID )
) {
unset( $actions['edit'] );
unset( $actions['inline hide-if-no-js']);
unset( $actions['trash'] );
}
return $actions;
}
}
Приведенный выше код предотвращает работу или отображение следующих элементов при необходимости:
get_edit_post_link
- Ссылка
Редактировать страницу
в панели администратора WordPress для страниц - Быстрые ссылки
Редактировать
,Быстрое редактирование
иУдалить
под страницами в/wp-admin/edit.php?post_type=page
Этот код работал в моей локальной установке WordPress 4.7. Если страницы на сайте меняются редко, возможно, лучше жестко прописать ID страниц и их подстраниц, удалив WP_Query
внутри метода __construct
. Это уменьшит количество запросов к базе данных.

Если вы хотите обойтись без плагинов, вы можете использовать вариацию приведённого ниже кода в файле functions.php или в своём собственном плагине.
Код состоит из двух отдельных частей. Вам понадобится использовать только одну из них, выбор зависит от сложности ваших требований.
Часть 1 предназначена для указания одного пользователя и ограничения его доступа к определённой записи.
Часть 2 позволяет создать карту соответствий пользователей и ID записей, разрешая доступ к нескольким записям.
Приведённый код работает только со страницами, но если вы хотите применить его к записям или пользовательским типам записей, вам нужно изменить строку в $screen->id == 'page'
на соответствующую.
Список ID экранов в wp-admin можно найти здесь
function my_pre_get_posts( $query ){
$screen = get_current_screen();
$current_user = wp_get_current_user();
/**
* Указываем одного пользователя и ограничиваем доступ к одной записи
*/
$restricted_user_id = 10; // ID ограничиваемого пользователя
$allowed_post_id = 1234; // ID разрешённой записи
$current_post_id = isset( $_GET['post'] ) ? (int)$_GET['post'] : false ;
// Затрагиваем только определённого пользователя
if( $current_user->ID !== $restricted_user_id ){
return;
}
// Затрагиваем только страницу редактирования
if( ! $current_post_id ){
return;
}
if( $screen->id == 'page' && $current_post_id !== $allowed_post_id ){
wp_redirect( admin_url( ) );
exit;
}
/**
* Создаём карту соответствий user_id => $allowed_posts
*/
$restrictions_map = [
10 => [ 123 ], // Разрешаем пользователю с ID 10 редактировать страницу с ID 123
11 => [ 152, 186 ] // Разрешаем пользователю с ID 11 редактировать страницы с ID 152 и 186
];
if( array_key_exists( $current_user->ID, $restrictions_map ) ){
$allowed_posts = $restrictions_map[$current_user->ID];
if( $screen->id == 'page' && ! in_array( $current_user->ID, $allowed_posts ) ){
wp_redirect( admin_url( ) );
exit;
}
}
}
add_action( 'pre_get_posts', 'my_pre_get_posts' );

Я использовал User Role Editor
несколько раз, и он довольно хорош.
Возможно, он поможет и вам.
Вот ссылка Редактор ролей пользователей

Кажется, это надежный плагин, но я не могу найти способ ограничить пользователя в редактировании определенных страниц.

Сделайте пользователей, которых вы хотите ограничить, пользователями уровня "автор" Добавьте возможность "edit_pages" для уровня пользователя "автор" (используя User Role Editor) Установите автором страницы пользователя, которому вы хотите предоставить право редактирования. Пользователь уровня "автор" с возможностью edit_pages может видеть список страниц в админке, но не имеет возможности редактировать, кроме страниц, автором которых он является.

Спасибо, это работает в определенной степени. В какой-то момент мне может понадобиться ограничить нескольких пользователей для изменения конкретной страницы, поэтому потребуется способ установки нескольких авторов для страницы.

Для ограничения пользователей доступом к определенным страницам вам потребуется приобрести Pro версию. Я искал то же самое и выяснил это. http://wordpress.stackexchange.com/questions/191658/allowing-user-to-edit-only-certain-pages/191661#191661?s=f87626a8cf184588bca7c4e397c972a4
