Groups of capabilities: users with multiple roles?

I’m pretty sure I understand the roles and capabilities setup in WordPress: granular capabilities, grouped together in roles that can be assigned to users. Code should check the granular capabilities, not the roles (because the capabilities for particular roles can change). Roles are not necessarily hierarchical (though the default roles are).

Is there any way of assigning multiple roles to users? Alternatively, having a number of groups of capabilities, and associating one or more group to a user? The way my site works, there are a number of obvious responsibilities: updating web pages, moderating forums, updating the events calendar, and so on. Each responsibility has a group of capabilities that are needed in order to perform the tasks associated with it. I’d like to enable a user to perform one or more responsibilities. So user A could update web pages and the events calendar but not moderate the forums (not nearly tactful enough), but user B could moderate forums, update the events calendar, but isn’t allowed near the web pages.

Short of defining a role for each possible combination of responsibilities, is there any way of doing this?

3 s
3

The lack of mutiple roles has irritated me for a long time since the underlying WP_User class supports multiple roles. I have even considered looking for an alternative software solution. @lpryor – after reading your post, I was re-motivated to implement it myself.

It took a surprisingly short number of lines to do although I have had to hack the users.php file since I was too lazy to create a separate plugin to do it for me. Clearly this is the wrong way to do it so if I am motivated enough in future, I may try to do it properly.

If you don’t care about being able to upgrade to the latest version of WordPress (which you should) – you can implement multiple roles with the code snippets below. Please bear in mind that I’m not a wordpress expert. I just opened the relevant files and made the changes without trying to understand the full implications of what I was doing. The code seems reasonable to me but I wouldn’t trust it with my life.

(I am using 3.2 so your line numbers may vary)
In class-wp-users-list-table.php
just before line 150 add some like the following:

<div class="alignleft actions">
    <label class="screen-reader-text" for="remove_role"><?php _e( 'Remove role &hellip;' ) ?></label>
    <select name="remove_role" id="remove_role">
        <option value=""><?php _e( 'Remove role &hellip;' ) ?></option>
        <?php wp_dropdown_roles(); ?>
    </select>
    <?php submit_button( __( 'Remove' ), 'secondary', 'changeit', false ); ?>
</div>

then change the current_account function to look something like this

function current_action() {
    if ( isset($_REQUEST['changeit']) ) {
        if ( !empty($_REQUEST['new_role']) )
            return 'promote';
        elseif ( !empty($_REQUEST['remove_role']) )
            return 'remove_role';
    }

    return parent::current_action();

}

Now in users.php
Comment out lines 71-76

/*
if ( $id == $current_user->ID && !$wp_roles->role_objects[$_REQUEST['new_role']]->has_cap('promote_users') ) {
    $update="err_admin_role";
    continue;
}
*/

Replace the set_role in line 83 with add_role

$user->add_role($_REQUEST['new_role']);

At line 92 add the following (This is just a lightly edited copy & paste from the promote action – I haven’t checked to ensure that the promote_user capability is appropriate for removing roles)

case 'remove_role':
    check_admin_referer('bulk-users');

    if ( ! current_user_can( 'promote_users' ) )
            wp_die( __( 'You can&#8217;t edit that user.' ) );

    if ( empty($_REQUEST['users']) ) {
            wp_redirect($redirect);
            exit();
    }

    $editable_roles = get_editable_roles();
    if ( empty( $editable_roles[$_REQUEST['remove_role']] ) )
            wp_die(__('You can&#8217;t remove that role'));

    $userids = $_REQUEST['users'];
    $update="remove_role";
    foreach ( $userids as $id ) {
            $id = (int) $id;

            if ( ! current_user_can('promote_user', $id) )
                    wp_die(__('You can&#8217;t edit that user.'));
            // The new role of the current user must also have promote_users caps
            // Need to think this through
            /*
            if ( $id == $current_user->ID && !$wp_roles->role_objects[$_REQUEST['new_role']]->has_cap('promote_users') ) {
                    $update="err_admin_role";
                    continue;
            }
            */

            // If the user doesn't already belong to the blog, bail.
            if ( is_multisite() && !is_user_member_of_blog( $id ) )
                    wp_die(__('Cheatin&#8217; uh?'));

            $user = new WP_User($id);
            $user->remove_role($_REQUEST['remove_role']);
    }

    wp_redirect(add_query_arg('update', $update, $redirect));
    exit();

At line 370 add the following

case 'remove_role':
    $messages[] = '<div id="message" class="updated"><p>' . __('Removed role.') . '</p></div>';
    break;

Leave a Comment