Редактор может создавать любых новых пользователей, кроме администратора
Я настроил WordPress сайт для клиента. У клиента есть роль Редактора, при этом я установил плагин Members и предоставил клиенту возможность добавлять новых пользователей в админ-панель WordPress. Это работает отлично.
Мой вопрос в том, что я хотел бы дать клиенту возможность создавать новых пользователей с ролями Подписчика, Автора, Редактора и Участника, но НЕ Администратора. Новые пользователи, которых создает клиент, не должны иметь роль Администратора. Возможно ли как-то скрыть этот вариант?
Спасибо Vayu
На самом деле это довольно просто. Вам нужно добавить фильтры в map_meta_caps
, чтобы запретить редакторам создавать/редактировать администраторов, а также удалить роль администратора из массива 'editable roles'. Этот класс, реализованный как плагин или в файле functions.php вашей темы, справится с задачей:
class JPB_User_Caps {
// Добавляем наши фильтры
function __construct(){
add_filter( 'editable_roles', array($this, 'editable_roles'));
add_filter( 'map_meta_cap', array($this, 'map_meta_cap'), 10, 4);
}
// Удаляем 'Administrator' из списка ролей, если текущий пользователь не является администратором
function editable_roles( $roles ){
if( isset( $roles['administrator'] ) && !current_user_can('administrator') ){
unset( $roles['administrator']);
}
return $roles;
}
// Если кто-то пытается редактировать или удалить администратора, и сам не является администратором - запрещаем
function map_meta_cap( $caps, $cap, $user_id, $args ){
switch( $cap ){
case 'edit_user':
case 'remove_user':
case 'promote_user':
if( isset($args[0]) && $args[0] == $user_id )
break;
elseif( !isset($args[0]) )
$caps[] = 'do_not_allow';
$other = new WP_User( absint($args[0]) );
if( $other->has_cap( 'administrator' ) ){
if(!current_user_can('administrator')){
$caps[] = 'do_not_allow';
}
}
break;
case 'delete_user':
case 'delete_users':
if( !isset($args[0]) )
break;
$other = new WP_User( absint($args[0]) );
if( $other->has_cap( 'administrator' ) ){
if(!current_user_can('administrator')){
$caps[] = 'do_not_allow';
}
}
break;
default:
break;
}
return $caps;
}
}
$jpb_user_caps = new JPB_User_Caps();
РЕДАКТИРОВАНИЕ
Итак, я разобрался, почему пропускалось удаление пользователей. Оказывается, delete_user
обрабатывается немного иначе, чем edit_user
. Я изменил метод map_meta_cap
, чтобы это учесть. Протестировано на версии 3.0.3 - теперь никто, кроме администраторов, не сможет удалять, редактировать или создавать администраторов.
РЕДАКТИРОВАНИЕ 2
Я обновил код в соответствии с ответом @bugnumber9 ниже. Пожалуйста, проголосуйте за этот ответ!

Может кто-то подтвердить, что этот код предотвращает удаление администраторов другими пользователями? Я не могу воспроизвести такое поведение. Код действительно предотвращает редактирование, но ссылка "удалить" при наведении все равно отображается, и WordPress позволяет пользователю выполнить удаление...

@somatic - ты был абсолютно прав. Спасибо, что обратил на это внимание. Проблема теперь исправлена.

Мне тоже нужно это реализовать, но я не уверен, куда вставлять этот код! В functions.php? Если нет, то как можно сделать, чтобы это работало из functions.php? С уважением, Dc

если кто-то ещё хочет ограничить дополнительные роли, просто добавьте дополнительную строку для каждой роли под этой строкой: unset( $roles['administrator']);
… так после этой строки вы можете добавить, например: unset( $roles['editor']);

Отлично работает в 3.4.1, спасибо! Убедитесь, что добавили возможности для create_users, delete_users, add_users, remove_users, edit_users, list_users и promote_users

Отличный код. Для тех, кто его использует, обязательно ознакомьтесь с небольшой доработкой от @bugnumber9 ниже. (Может быть, John P Block внесёт правки в свой код?)

Несмотря на то, что этой ветке уже около 7 лет, её легко найти через Google, и предложенное решение всё ещё работает. Я имею в виду код, предоставленный @John P Bloch.
Однако в PHP 7 этот код вызывает некритическую ошибку (PHP Deprecated):
PHP Deprecated: Methods with the same name as their class will not be constructors in a future version of PHP; JPB_User_Caps has a deprecated constructor in ...
Чтобы исправить это, просто замените следующий фрагмент:
// Добавляем наши фильтры
function JPB_User_Caps(){
add_filter( 'editable_roles', array(&$this, 'editable_roles'));
add_filter( 'map_meta_cap', array(&$this, 'map_meta_cap'),10,4);
}
на этот:
// Добавляем наши фильтры
function __construct() {
add_filter( 'editable_roles', array(&$this, 'editable_roles') );
add_filter( 'map_meta_cap', array(&$this, 'map_meta_cap'), 10, 4 );
}
Это исправит проблему.

Я искал решение, где Редактор мог бы редактировать только меню И создавать/редактировать пользователей без необходимости использования плагина. В итоге я сделал это для тех, кому это интересно.
// Настраивает роль 'Редактор' для возможности изменять меню, добавлять новых пользователей
// и многое другое.
class Custom_Admin {
// Добавляем наши фильтры
public function __construct(){
// Разрешаем редактору редактировать настройки темы (т.е. Меню)
add_action('init', array($this, 'init'));
add_filter('editable_roles', array($this, 'editable_roles'));
add_filter('map_meta_cap', array($this, 'map_meta_cap'), 10, 4);
}
public function init() {
if ($this->is_client_admin()) {
// Отключаем доступ к страницам тем/виджетов, если пользователь не администратор
add_action('admin_head', array($this, 'modify_menus'));
add_action('load-themes.php', array($this, 'wp_die'));
add_action('load-widgets.php', array($this, 'wp_die'));
add_action('load-customize.php', array($this, 'wp_die'));
add_filter('user_has_cap', array($this, 'user_has_cap'));
}
}
public function wp_die() {
_default_wp_die_handler(__('У вас недостаточно прав для доступа к этой странице.'));
}
public function modify_menus()
{
remove_submenu_page( 'themes.php', 'themes.php' ); // скрываем подменю выбора темы
remove_submenu_page( 'themes.php', 'widgets.php' ); // скрываем подменю виджетов
// Меню "Внешний вид"
global $menu;
global $submenu;
if (isset($menu[60][0])) {
$menu[60][0] = "Меню"; // Переименовываем "Внешний вид" в "Меню"
}
unset($submenu['themes.php'][6]); // Настроить
}
// Удаляем 'Администратор' из списка ролей, если текущий пользователь не админ
public function editable_roles( $roles ){
if( isset( $roles['administrator'] ) && !current_user_can('administrator') ){
unset( $roles['administrator']);
}
return $roles;
}
public function user_has_cap( $caps ){
$caps['list_users'] = true;
$caps['create_users'] = true;
$caps['edit_users'] = true;
$caps['promote_users'] = true;
$caps['delete_users'] = true;
$caps['remove_users'] = true;
$caps['edit_theme_options'] = true;
return $caps;
}
// Если кто-то пытается редактировать или удалять администратора, и этот пользователь не админ - запрещаем
public function map_meta_cap( $caps, $cap, $user_id, $args ){
// $args[0] == other_user_id
foreach($caps as $key => $capability)
{
switch ($cap)
{
case 'edit_user':
case 'remove_user':
case 'promote_user':
if(isset($args[0]) && $args[0] == $user_id) {
break;
}
else if(!isset($args[0])) {
$caps[] = 'do_not_allow';
}
// Не разрешаем не-админу редактировать админа
$other = new WP_User( absint($args[0]) );
if( $other->has_cap( 'administrator' ) ){
if(!current_user_can('administrator')){
$caps[] = 'do_not_allow';
}
}
break;
case 'delete_user':
case 'delete_users':
if( !isset($args[0])) {
break;
}
// Не разрешаем не-админу удалять админа
$other = new WP_User(absint($args[0]));
if( $other->has_cap( 'administrator' ) ){
if(!current_user_can('administrator')){
$caps[] = 'do_not_allow';
}
}
break;
break;
}
}
return $caps;
}
// Если текущий пользователь называется admin или administrative и является редактором
protected function is_client_admin() {
$current_user = wp_get_current_user();
$is_editor = isset($current_user->caps['editor']) ? $current_user->caps['editor'] : false;
return ($is_editor);
}
}
new Custom_Admin();

Решение от @John P Blochs по-прежнему работает отлично, но я хотел бы также поделиться своим небольшим фильтром для 'map_meta_cap'. Он немного короче и чище, по крайней мере, на мой взгляд ;)
function my_map_meta_cap( $caps, $cap, $user_id, $args ) {
$check_caps = [
'edit_user',
'remove_user',
'promote_user',
'delete_user',
'delete_users'
];
if( !in_array( $cap, $check_caps ) || current_user_can('administrator') ) {
return $caps;
}
$other = get_user_by( 'id', $args[0] ?? false ); // Проверка PHP 7 на наличие переменной в $args...
if( $other && $other->has_cap('administrator') ) {
$caps[] = 'do_not_allow';
}
return $caps;
}
add_filter('map_meta_cap', 'my_map_meta_cap', 10, 4 );
