Access on specific pages in wordpress for a specific user

My Question is regarding User access control in wordpress

I have two user – admin (admin access) and user1(editor). My requirement is admin can do any modification in admin site. But user1 can only edit and update some pages and no access other access except assigned pages.

I have no idea how to implement this functionality so your help is required?

My working environment :

  1. wordpress v 3.2.1
  2. Theme is twenty ten

3 Answers
3

Very interesting question. It’s a bit outside the scope of the typical rolls/capabilities functionality as it’s more fine grained (unless I’m wrong — very possible).

The first step would be to put some sort of way in that you can assign which posts a user can edit.

It makes the most sense to stick that the user’s profile page. So you can hook into edit_user_profile, get all the pages on the site, and stick them in a multi select box. The code comments should explain the step by step a bit. edit_user_profile only shows up when editing other users’ profiles.

<?php
add_action( 'edit_user_profile', 'wpse30211_user_profile' );
function wpse30211_user_profile( $user )
{
    // only show this on editor pages
    if( ! in_array( 'editor', $user->roles ) ) return;

    // get the pages.
    $pages = get_posts(
        array(
            'post_type'     => 'page',
            'numberposts'   => -1,
            'post_status'   => 'any',
        )
    );

    // Bail if we don't have pages.
    if( ! $pages ) return;

    // Which pages can our user edit?
    $allowed = get_user_meta( $user->ID, 'wpse30211_pages', true );
    if( ! is_array( $allowed ) || empty( $allowed ) ) $allowed = array();

    // nonce-i-fy things
    wp_nonce_field( 'wpse30211_nonce', 'wpse30211_nonce' );

    // section heading...
    echo '<h3>' . __( 'Grant this User permission to edit...' ) . '</h3>';
    echo '<select multiple="multiple" name="wpse30211[]">';
    echo '<option value="0">None</option>';
    foreach( $pages as $p )
    {
        // for use in checked() later...
        $selected = in_array( $p->ID, $allowed ) ? 'on' : 'off';
        echo '<option ' . selected( 'on', $selected, false ) . ' value="' . esc_attr( $p->ID ) . '">' . esc_html( $p->post_title ) . '</option>';
    }
    echo '</select>';
}

Next you need to save the extra data you added to peoples profile. To do that, you hook into edit_user_profile_update. Verify the nonce set in the above function, then check if the wpse30211 field is set and save the stuff with update_user_meta.

<?php
add_action( 'edit_user_profile_update', 'wpse30211_user_save' );
function wpse30211_user_save( $user_id )
{
    // verify our nonce
    if( ! isset( $_POST['wpse30211_nonce'] ) || ! wp_verify_nonce( $_POST['wpse30211_nonce'], 'wpse30211_nonce' ) )
        return;

    // make sure our fields are set
    if( ! isset( $_POST['wpse30211'] ) ) 
        return;

    $save = array();
    foreach( $_POST['wpse30211'] as $p )
    {
        $save[] = $p;
    }
    update_user_meta( $user_id, 'wpse30211_pages', $save );
}

Now the fun part: disallowing access to certain pages. To do that, you can hook into load-post.php, which fires when wp-admin/post.php, the post editing screen, is loaded. Grab the the post id being editted, check to make sure it’s a page. Then, grab the current user with wp_get_current_user and get the pages they are allowed to edit with get_user_meta. If the current post ID isn’t in the array of pages they’re allowed to edit, call wp_die and kill the page.

<?php
add_action( 'load-post.php', 'wpse30211_kill_edit' );
function wpse30211_kill_edit()
{
    $post_id = isset( $_REQUEST['post'] ) ? absint( $_REQUEST['post'] ) : 0;
    if( ! $post_id ) return;

    // bail if this isn't a page
    if( 'page' !== get_post_type( $post_id ) ) return;

    $user = wp_get_current_user();
    $allowed = get_user_meta( $user->ID, 'wpse30211_pages', true );
    if( ! is_array( $allowed ) || empty( $allowed ) ) $allowed = array();

    // if the user can't edit this page, stop the loading...
    if( ! in_array( $post_id, $allowed ) )
    {
        wp_die( 
            __( 'User cannot edit this page' ),
            __( "You can't edit this post" ),
            array( 'response' => 403 )
        );
    }
}

Finally, you can hook into pre_update_post and stop that from updating of the user can’t edit the page.

<?php
add_action( 'pre_post_update', 'wpse30211_stop_update' );
function wpse30211_stop_update( $post_id )
{
    // not a page? bail.
    if( 'page' !== get_post_type( $post_id ) ) return;

    $user = wp_get_current_user();
    $allowed = get_user_meta( $user->ID, 'wpse30211_pages', true );
    if( ! is_array( $allowed ) || empty( $allowed ) ) $allowed = array();

    if( ! in_array( $post_id, $allowed ) ) 
    {
        wp_die( 
            __( 'User cannot edit this page' ),
            __( "You can't edit this post" ),
            array( 'response' => 403 )
        );
    }
}

The above works, but I suspect there may be a way to do this a bit easier with WP’s built in role & capabilities. Here’s the whole thing as a plugin.

Leave a Comment