Groups of capabilities: users with multiple roles?

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