Разрешение пользователю редактировать только определенные страницы

16

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

NAF
источник
2
Я удалил ваш запрос на рекомендацию плагина, так как это сделало вопрос не по теме. Да, это должно быть возможно с плагином, но когда я вижу попытки сделать вещи, которые требуют такого рода взлома базовой функциональности, я не могу не думать, что вы используете неправильный подход. Можете ли вы объяснить проект более подробно?
s_ha_dum

Ответы:

14

Первое, что нужно сделать для реализации такой задачи, - это определить, какую страницу пользователь может редактировать.

Есть разные способы сделать это. Это может быть пользовательская мета, какое-то значение конфигурации ... Ради этого ответа я буду предполагать, что эта функция существует:

function wpse_user_can_edit( $user_id, $page_id ) {

   $page = get_post( $page_id );

   // let's find the topmost page in the hierarchy
   while( $page && (int) $page->parent ) {
     $page = get_post( $page->parent );
   }

   if ( ! $page ) {
     return false;
   }

   // now $page is the top page in the hierarchy
   // how to know if an user can edit it, it's up to you...

}

Теперь, когда у нас есть способ определить, может ли пользователь редактировать страницу, нам просто нужно сказать 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 the capability being filtered isn't of our interest, just return current value
    if ( ! in_array( $cap, $to_filter, true ) ) {
        return $caps;
    }

    // First item in $args array should be page ID
    if ( ! $args || empty( $args[0] ) || ! wpse_user_can_edit( $user_id, $args[0] ) ) {
        // User is not allowed, let's tell that to WP
        return [ 'do_not_allow' ];
    }
    // Otherwise just return current value
    return $caps;

}, 10, 4 );

На данный момент нам нужен только способ подключения пользователя к одной или нескольким страницам.

В зависимости от варианта использования могут быть разные решения.

Гибким решением может быть добавление выпадающего списка «корневых» страниц (см. wp_dropdown_pages ) На экран редактирования пользовательского администратора и сохранение выбранных страниц в качестве пользовательских мета.

Мы могли бы использовать, 'edit_user_profile'чтобы добавить раскрывающееся поле страниц и'edit_user_profile_update' сохранить выбранное значение в качестве пользовательской мета.

Я уверен, что на этом сайте достаточно указаний, как это сделать в деталях.

Когда страницы сохранены как пользовательские мета, wpse_user_can_edit()вышеуказанную функцию можно завершить, проверив, является ли идентификатор страницы частью пользовательского мета-значения.

Удаляя возможность редактировать страницу, WordPress сделает все остальное: удалит любую ссылку для редактирования из бэкэнда и внешнего интерфейса, запретит прямой доступ ... и так далее.

Gmazzap
источник
3
Это намного лучше, чем мой ответ. Зачем ограничивать редактирование ссылок, когда вы можете просто изменить возможности пользователя и позволить WordPress обрабатывать все остальное?
рикотека
Вы должны использовать «a» перед словом «user», а не «an», потому что длинный «u» звучит как «yu», начинающийся с согласного.
Филипп
7

Для реализации этой функции требуется небольшой объем кода, даже если вы используете класс PHP, чтобы избежать глобальных переменных. Я также не хотел скрывать запрещенные страницы для пользователя в Личном кабинете. Что делать, если они добавили контент, который уже был на сайте?

$user_edit_limit = new NS_User_Edit_Limit(
    15,       // User ID we want to limit
    [2, 17]   // Array of parent page IDs user is allowed to edit
                 (also accepts sub-page IDs)
);

class NS_User_Edit_Limit {

    /**
     * Store the ID of the user we want to control, and the
     * posts we will let the user edit.
     */
    private $user_id = 0;
    private $allowed = array();

    public function __construct( $user_id, $allowed ) {

        // Save the ID of the user we want to limit.
        $this->user_id = $user_id;

        // Expand the list of allowed pages to include sub pages
        $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;
            }
        }

        // For the prohibited user...
        // Remove the edit link from the front-end as needed
        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 );
        // Remove the edit link from wp-admin as needed
        add_action( 'page_row_actions', array( $this, 'remove_page_list_edit_link' ), 10, 2 );
    }

    /**
     * Helper functions that check if the current user is the one
     * we want to limit, and check if a specific post is in our
     * list of posts that we allow the user to edit.
     */
    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 );
    }

    /**
     * Removes the edit link from the front-end as needed.
     */
    public function remove_edit_link( $link, $post_id, $test ) {
        /**
         * If...
         * - The limited user is logged in
         * - The page the edit link is being created for is not in the allowed list
         * ...return an empty $link. This also causes edit_post_link() to show nothing.
         *
         * Otherwise, return link as normal.
         */
        if ( $this->is_user_limited() && !$this->is_page_allowed( $post_id ) ) {
            return '';
        }
        return $link;
    }

    /**
     * Removes the edit link from WP Admin Bar
     */
    public function remove_wp_admin_edit_link( $wp_admin_bar ) {
        /**
         *  If:
         *  - We're on a single page
         *  - The limited user is logged in
         *  - The page is not in the allowed list
         *  ...Remove the edit link from the WP Admin Bar
         */
        if ( 
            is_page() &&
            $this->is_user_limited() &&
            !$this->is_page_allowed( get_post()->ID )
        ) {
            $wp_admin_bar->remove_node( 'edit' );
        }
    }

    /**
     * Removes the edit link from WP Admin's edit.php
     */
    public function remove_page_list_edit_link( $actions, $post ) {
        /**
         * If:
         * -The limited user is logged in
         * -The page is not in the allowed list
         * ...Remove the "Edit", "Quick Edit", and "Trash" quick links.
         */
        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;
    }
}

Код, приведенный выше, предотвращает работу или появление следующего:

  1. get_edit_post_link
  2. Edit Page ссылка на панели администратора WP, которая появляется для страниц
  3. Edit, Quick EditИ Trashбыстрые ссылки , которые появляются под страницу в/wp-admin/edit.php?post_type=page

Это работало на моей локальной установке WordPress 4.7. Предполагая, что страницы на сайте не будут часто меняться, возможно, было бы лучше жестко закодировать идентификаторы страницы и ее подстраниц и удалить WP_Queryизнутри __constructметод. Это значительно сэкономит на вызовах базы данных.

ricotheque
источник
+1 за более полный ответ, чем у @ Бена, но правильный способ справиться со ссылками - манипулировать возможностями,
Марк Каплун,
Да, когда я увидел ответ gmazzap, я подумал: «Почему я не подумал об этом?»
рикотека
5

Если вы хотите держаться подальше от плагинов, вы можете изменить приведенный ниже код в файле functions.php или в пользовательском плагине.

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

Часть 1 указывает отдельного пользователя и ограничивает его определенным постом.

Часть 2 позволяет создавать карту пользователей и идентификаторы сообщений, а также позволяет создавать несколько сообщений.

Приведенный ниже код предназначен только для страницы, но если вы хотите изменить это на сообщение или пользовательский тип сообщения, вам нужно изменить строку $screen->id == 'page'на что-то другое.

Вы можете найти ссылку на идентификатор экрана вокруг wp-admin здесь

function my_pre_get_posts( $query ){

    $screen = get_current_screen();
    $current_user = wp_get_current_user();

    /**
     * Specify a single user and restrict to a single page
     */
    $restricted_user_id = 10; //User ID of the restricted user
    $allowed_post_id = 1234; //Post ID of the allowed post

    $current_post_id = isset( $_GET['post'] ) ? (int)$_GET['post'] : false ;

    //Only affecting a specific user
    if( $current_user->ID !== $restricted_user_id ){
        return;
    }

    //Only Affecting EDIT page.
    if( ! $current_post_id ){
        return;
    }

    if( $screen->id == 'page' && $current_post_id !== $allowed_post_id ){
        wp_redirect( admin_url( ) );
        exit;
    }

    /**
     * Specify a map of user_id => $allowed_posts
     */
    $restrictions_map = [
        10 => [ 123 ], //Allow user ID to edit Page ID 123
        11 => [ 152, 186 ] //Allow user ID to edit Page ID 123 and 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' );
Бен Кейси
источник
1
+1, поскольку он может работать для выполнения основных функций, но при этом остаются ссылки для редактирования выводимых страниц даже для пользователей, которые не могут их редактировать, что создает плохой пользовательский интерфейс
Марк Каплун,
-4

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

user2319361
источник
Кажется, это сплошной плагин, но я не могу найти способ, как ограничить пользователя для редактирования определенных страниц.
naf
Сделайте так, чтобы пользователи хотели ограничить таким образом пользователей уровня автора. Добавьте возможность «edit_pages» к уровню пользователя автора (с помощью редактора ролей пользователей). Установите автора страницы тем пользователем, которому вы хотите предоставить право на ее редактирование. Пользователь уровня автора, имеющий возможность edit_pages, может видеть список страниц на панели инструментов, но не имеет возможности редактировать, за исключением страниц, автором которых он является.
user2319361
4
Спасибо, это работает в определенной степени. В какой-то момент мне может потребоваться ограничить доступ к нескольким пользователям для изменения конкретной страницы, поэтому должен быть способ назначить нескольким авторам страницу.
Наф
Чтобы ограничить пользователей определенными страницами, вам необходимо приобрести Pro versoin. Я ищу то же самое и выяснил это. wordpress.stackexchange.com/questions/191658/…
Рикардо Андрес
1
в то время как этот конкретный плагин сейчас очень важен, вероятно, проще написать код для его выполнения, чем использовать все параметры, предлагаемые плагином. (если он даже позволяет вам делать то, что просит ОП)
Марк Каплун