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 …' ) ?></label> <select name="remove_role" id="remove_role"> <option value=''><?php _e( 'Remove role …' ) ?></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’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’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’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’ 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;