19

I have three extra roles in my site.

  1. Doctor
  2. Receptionist
  3. Guest

those roles are added by following code:

* adding Doctor role */
$doctor_role = add_role('Doctor', __('Doctor'), array('read'=>'true'));

/* adding Receptionist role */
$receptionist_role = add_role('Receptionist', __('Receptionist'), array('read'=>'true'));

/* adding Guest role */
$guest_role = add_role('Guest', __('Guest'), array('read'=>'true'));

By default Administrator role create all other roles. But I want to limit this assigning role by user level. What I mean is:

  1. Administrator - should be able to create all role users - possible by default.
  2. Doctor - should be able to create Receptionist and Guest role users ONLY
  3. Receptionist - should be able to create Guest role users ONLY
  4. Guest - Not allowed to create any users.

How can do this? Better if I can achieve this without using any plugins.

2 Answers 2

32

Firstly, you need to add the following capabilities to the Doctor and Receptionist role:

  • list_users
  • edit_users
  • create_users
  • delete_users

Now we can get to work with controlling which users they can create/edite/delete. Let's start with a "helper" function that will return which roles a user is allowed to edit:

/**
 * Helper function get getting roles that the user is allowed to create/edit/delete.
 *
 * @param   WP_User $user
 * @return  array
 */
function wpse_188863_get_allowed_roles( $user ) {
    $allowed = array();

    if ( in_array( 'administrator', $user->roles ) ) { // Admin can edit all roles
        $allowed = array_keys( $GLOBALS['wp_roles']->roles );
    } elseif ( in_array( 'Doctor', $user->roles ) ) {
        $allowed[] = 'Receptionist';
        $allowed[] = 'Guest';
    } elseif ( in_array( 'Receptionist', $user->roles ) ) {
        $allowed[] = 'Guest';
    }

    return $allowed;
}

And to set which roles they can assign a user:

/**
 * Remove roles that are not allowed for the current user role.
 */
function wpse_188863_editable_roles( $roles ) {
    if ( $user = wp_get_current_user() ) {
        $allowed = wpse_188863_get_allowed_roles( $user );

        foreach ( $roles as $role => $caps ) {
            if ( ! in_array( $role, $allowed ) )
                unset( $roles[ $role ] );
        }
    }

    return $roles;
}

add_filter( 'editable_roles', 'wpse_188863_editable_roles' );

And finally, limit which users they can edit/delete based on their role:

/**
 * Prevent users deleting/editing users with a role outside their allowance.
 */
function wpse_188863_map_meta_cap( $caps, $cap, $user_ID, $args ) {
    if ( ( $cap === 'edit_user' || $cap === 'delete_user' ) && $args ) {
        $the_user = get_userdata( $user_ID ); // The user performing the task
        $user     = get_userdata( $args[0] ); // The user being edited/deleted

        if ( $the_user && $user && $the_user->ID != $user->ID /* User can always edit self */ ) {
            $allowed = wpse_188863_get_allowed_roles( $the_user );

            if ( array_diff( $user->roles, $allowed ) ) {
                // Target user has roles outside of our limits
                $caps[] = 'not_allowed';
            }
        }
    }

    return $caps;
}

add_filter( 'map_meta_cap', 'wpse_188863_map_meta_cap', 10, 4 );
5
  • Perfect thanks a lot.. After I added capabilities and your code it works as we expected. But Doctors , Receptionist and Guest are unable to edit their own profile too. I want them to edit their own profile. How can I do that? May 21, 2015 at 5:24
  • hi TheDeadMedic.. Are u there? I've upvoted ur answer. could u plz look into my the above comment?? May 22, 2015 at 3:45
  • Check my revision. May 22, 2015 at 10:48
  • @TheDeadMedic Your code helped me a lot. I only have one trouble. I use this on a Multisite. And while this code solved my problem, it created another one. When logged in as the Super Administrator I cannot select any roles at all from the Super Admin's Network Pages. How do I solve this?
    – jockebq
    Mar 26, 2018 at 13:25
  • 1
    This is a brilliant answer to the question. I found Wordpress' own documentation wholly lacking in this area, even full of grammatical errors. This is a really efficient solution to the problem.
    – Benji
    May 22, 2018 at 20:45
0

The above answer worked great. However, the roles need to be lowercase for

function wpse_188863_get_allowed_roles( $user ) { }

For instance:

if ( in_array( 'administrator', $user->roles ) ) { // Admin can edit all roles
    $allowed = array_keys( $GLOBALS['wp_roles']->roles );
} elseif ( in_array( 'doctor', $user->roles ) ) {
    $allowed[] = 'receptionist';
    $allowed[] = 'guest';
} elseif ( in_array( 'receptionist', $user->roles ) ) {
    $allowed[] = 'guest';
}
1
  • The case of the roles should likely be lowercase in most instances. In the above example, both the slug and name are given using title case, so this is technically correct, although not recommended.
    – Benji
    May 22, 2018 at 20:28

Not the answer you're looking for? Browse other questions tagged or ask your own question.