web-dev-qa-db-fra.com

Permettre à l'utilisateur de n'éditer que certaines pages

Je voudrais permettre à certains utilisateurs de ne modifier qu'une seule page et ses sous-pages. Comment cela serait-il possible? J'ai essayé l'ancien Role Scoper, mais il semble avoir beaucoup de problèmes et de bugs.

14
naf

La première chose à faire pour implémenter une telle tâche est de pouvoir savoir quelle page un utilisateur peut éditer.

Il y a différentes façons de le faire. Ce pourrait être une méta utilisateur, une valeur de configuration ... Pour répondre à cette question, je supposerai qu'une fonction seule existe:

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...

}

Maintenant que nous avons un moyen de déterminer si un utilisateur peut modifier une page, nous devons simplement dire à WordPress d'utiliser cette fonction pour vérifier la capacité de l'utilisateur à modifier une page.

Cela peut être fait via 'map_meta_cap' filter.

Quelque chose comme:

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' ];
    }

    // Every user is allowed to exist.
    // Return this array, the check for capability will be true
    return [ 'exist' ];

}, 10, 4 );

À ce stade, nous avons seulement besoin d’un moyen de connecter un utilisateur à une ou plusieurs pages.

Il peut y avoir différentes solutions en fonction du cas d'utilisation.

Une solution flexible pourrait consister à ajouter une liste déroulante de pages "racine" (voir wp_dropdown_pages ) à l'écran de modification d'utilisateur et à enregistrer les pages sélectionnées en tant que méta de l'utilisateur.

Nous pourrions utiliser 'edit_user_profile' pour ajouter le champ de liste déroulante des pages et 'edit_user_profile_update' pour stocker la valeur sélectionnée en tant que méta utilisateur.

Je suis sûr que sur ce site Web, il y a suffisamment d'indications sur la manière de le détailler.

Lorsque la ou les pages sont stockées en tant que méta utilisateur, vous pouvez terminer la fonction wpse_user_can_edit() décrite ci-dessus en vérifiant si l'identifiant de page fait partie de la valeur méta de l'utilisateur.

En supprimant la possibilité de modifier la page, WordPress fera le reste: supprimera tout lien de modification du backend et du frontend, empêchera l'accès direct ... et ainsi de suite.

12
gmazzap

Une petite quantité de code est nécessaire pour implémenter cette fonctionnalité, même si vous utilisez une classe PHP pour éviter les variables globales. Je ne voulais pas non plus cacher les pages interdites à l'utilisateur dans le tableau de bord. Et s'ils ajoutaient du contenu déjà présent sur le site?

$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;
    }
}

Le code ci-dessus empêche les éléments suivants de fonctionner ou de s'afficher selon les besoins:

  1. get_edit_post_link
  2. Le lien Edit Page dans la barre d’administration WP qui apparaît pour les pages
  3. Liens rapides Edit, Quick Edit et Trash qui apparaissent sous les pages dans /wp-admin/edit.php?post_type=page

Cela a fonctionné sur mon installation WordPress 4.7 locale. En supposant que les pages du site ne changent pas souvent, il serait peut-être préférable de coder en dur les ID de la page et de ses sous-pages et de supprimer le WP_Query dans la méthode __construct. Cela permettra d'économiser beaucoup sur les appels de base de données.

7
ricotheque

Si vous voulez éviter les plugins, vous pouvez utiliser une variante du code ci-dessous dans un fichier functions.php ou un plugin personnalisé.

Il existe deux parties distinctes dans ce code. Vous n’auriez besoin d’en utiliser qu’une, mais celle-ci dépend de la complexité des exigences.

La première partie spécifie un utilisateur unique et le limite à une publication spécifique.

La partie 2 vous permet de créer une carte des utilisateurs et des identifiants de post et autorise plusieurs posts

Le code ci-dessous concerne uniquement une page, mais si vous souhaitez le modifier en un message ou un type de message personnalisé, vous devez remplacer la chaîne dans $screen->id == 'page' par un autre.

Vous pouvez trouver une référence à l'ID d'écran autour de wp-admin ici

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' );
5
Ben Casey