7

Bit of a long question, but I do believe this situation will occur for other people in other contexts.

Our company needs to create an Employee Assistance Program MU subdirectory network site where our company can create user accounts with a custom user role of client.

The client will be another company needing to use our company's EAP services for that company's own employees.

The client must be able to login to the MU network site admin, and add user accounts with an employee user role for their employees.

Those employee accounts will be able to log in to the front end and view restricted EAP content.

For the employee user role, I have use the code here to create in my theme's functions.php:

$result1 = add_role( 'employee', __('Employee'),  
    array(
        'edit_users' => false,
        'create_users' => false,
        'delete_users' => false,
        'read' => true,
        'edit_posts' => false,
        'edit_pages' => false,
        'edit_others_posts' => false,
        'create_posts' => false,
        'manage_categories' => false,
        'publish_posts' => false,
        'edit_themes' => false,
        'install_plugins' => false,
        'update_plugin' => false,
        'update_core' => false
    )
);

Can I get away with only specifying what is true, and not need to specify what is false?

For the client, I have:

$result2 = add_role( 'client', __('Client Company Admin'),  
    array(
        'edit_users' => true,
        'create_users' => true,
        'delete_users' => true,
        'read' => true,
        'edit_posts' => false,
        'edit_pages' => false,
        'edit_others_posts' => false,
        'create_posts' => false,
        'manage_categories' => false,
        'publish_posts' => false,
        'edit_themes' => false,
        'install_plugins' => false,
        'update_plugin' => false,
        'update_core' => false
    )
);

I need each client account to only be able to edit and delete employee accounts created by that individual client account, and no other company's employee accounts.

I gather I could add a hidden custom field to the employee user role which will be the client ID (added when the client creates an employee), and this custom field must not be editable by the employee role.

I have searched for how to add a hidden custom field to a custom user role, and haven't been able to find a solution.

So, my questions are:

  1. How do I add a hidden custom field called clientID to a custom user role (employee)?
  2. How do ensure that when a client creates an employee, the client's user ID or username (unsure which is best) is added to the new employee's clientID meta field?
  3. How do I ensure the client can only edit or delete an employee account (and not other standard user accounts)?
  4. Extending 3, how do I ensure the client can only edit and delete an employee account where the employee account has a clientID client's own user ID or username?

Thanks.

3
  • 2
    This is rather broad and looks more than a project than a question. Jun 7, 2016 at 13:10
  • You do need to add list_users to your client role so they can manage the user list.
    – Howdy_McGee
    Jun 7, 2016 at 18:41
  • This is probably a typo but in your employee role register, you have a } bracket instead of an ending paren ).
    – Howdy_McGee
    Jun 8, 2016 at 14:24

1 Answer 1

8
+50

Clients get added by admins, clients have a parent child relationship with employees which makes filtering easy. So all we need to do is remove anything that doesn't have to do with employees and filter for employees with a certain meta value.

First thing's first, whenever a new user is registered on the admin side of our CMS we will assign it a parent of the current user IF that user is a client ( assuming clients cannot assign employees to other clients ):

/**
 * Admin New Employee Function
 * @var int $user_id
 * @return void
 */
function client_register( $user_id ) {
    if( ! is_admin() ) {
        return;
    }

    // Grab the current user
    $current_user = wp_get_current_user();

    // IF the current user ID isn't 0 and our current user is a 'client' role
    if( $current_user->ID && in_array( 'client', $current_user->roles ) ) {

        // Update the new user with a 'parent' usermeta value of the current 'client'
        update_user_meta( $user_id, '_user_parent', $current_user->ID );
    }
}
add_action( 'user_register', 'client_register' );

Great, so now whenever a client creates a new user ( of any type ) it gets assigned a parent of whichever client created it. Now we need to filter our user table to only show users with our client parent usermeta:

/**
 * Pre Get Users filter
 * @var WP_Query Object $query
 * @return void
 */
function theme_pgu( $query ) {
    if( ! is_admin() ) {
        return;
    }

    // Grab our current user
    $current_user = wp_get_current_user();

    // IF our user ID is not 0 and our current user has a role of 'client'
    if( $current_user->ID && in_array( 'client', $current_user->roles ) ) {

        // Set the query to only return employee roles
        $query->set( 'role', 'employee' );

        // Which has a usermeta key '_user_parent' set
        $query->set( 'meta_key', '_user_parent' );

        // and has a usermeta value of the current client user
        $query->set( 'meta_value', $current_user->ID );
    }
}
add_action( 'pre_get_users', 'theme_pgu' );

Neat! Now we can address the issue of Clients being able to create roles of any type, so we move onto the cleanup process. The below will remove any selectable role when creating a new user or editing a current user to only employee:

/**
 * Selectable roles on the new user and user edit screen
 * @var Multi-dimensional Array $roles
 * @return Array $roles
 */
function client_sel_roles( $roles ) {
    // Grab our current user
    $current_user = wp_get_current_user();

    if( in_array( 'client', $current_user->roles ) ) {
        $roles = array( 'employee' => $roles['employee'] );
    }

    return $roles;
}
add_filter( 'editable_roles', 'client_sel_roles' );

On the All Users screen we can see the filter views still show other user roles so we need to fix that too:

/**
 * All Users screen filterable views
 * @var Array $views
 * @return Array $views
 */
function client_user_views( $views ) {
    // Grab our current user
    $current_user = wp_get_current_user();

    if( in_array( 'client', $current_user->roles ) ) {
        if( isset( $views['employee'] ) ) {
            $views = array( 'employee' => $views['employee'] );
        } else {
            $views = array();
        }
    }

    return $views;
}
add_filter( 'views_users', 'client_user_views' );

Finally, one oversight is that the user could potentially change the URL to view other users profiles which may not be their own employees, so we need to fix that by adding this little redirect:

/**
 * Stop clients from changing the URL to get to other profiles
 * @var WP_Screen Object $screen
 * @return void
 */
function edit_employees_only( $screen ) {

    // Check if we're on the correct screen
    if( 'user-edit' === $screen->base ) {

        // Ensure our desired user ID is set
        if( isset( $_GET['user_id'] ) && is_numeric( $_GET['user_id'] ) ) {
            $user_id        = absint( $_GET['user_id'] );
            $current_user   = wp_get_current_user();
            $parent         = get_user_meta( $user_id, '_user_parent', true );

            // Ensure that we're viewing a profile that is not our own
            if( $current_user->ID && in_array( 'client', $current_user->roles ) && $user_id !== $current_user->ID && $parent !== $current_user->ID ) {

                // We're viewing an incorrect profile - redirect to clients own profile
                wp_redirect( admin_url( "user-edit.php?user_id={$current_user->ID}" ) );
            }
        }
    }
}
add_action( 'current_screen', 'edit_employees_only' );

And that should do it. Client roles can only see and edit employees with the parent assigned as their ID.

11
  • That was 99% perfect. :-) I would never of been able to develop that myself. The only issues I had were: 1) Right after creating the first client Company Admin, I return to /wp-admin/users.php?id=2, with New User created, but: Notice: Undefined index: employee in C:\wamp\www\orseap\wp-content\themes\ors-eap-portal\functions.php on line 99. Line 99 is: return array( 'employee' => $views['employee'] );.
    – Steve
    Jun 8, 2016 at 5:01
  • 2) I created an employee account with a client account, but when logging in as the employee, the browser times out and the message Fatal error: Maximum execution time of 120 seconds exceeded in C:\wamp\www\orseap\wp-includes\post.php on line 449 appears.
    – Steve
    Jun 8, 2016 at 5:01
  • @Steve - I fixed the views filter, if there's no employees the index in the array doesn't exist so I've added a check to ensure it's set. As for the fatal error, I suggest commenting out function by function to narrow down where the issues lies, I'm not getting that issue using the roles in your question and my functions in the answer.
    – Howdy_McGee
    Jun 8, 2016 at 14:23
  • 1
    Are you using current_user_can() with user roles? You shouldn't.
    – cybmeta
    Jun 9, 2016 at 10:10
  • 1
    @Steve The comment section isn't really for extended discussion or additional questions. I would suggest creating a new WPSE question ( or search the site for page restrictions by role ) and wait for someone to post a solid insightful answer ( Comments have a character limit and are poor format to post code snippets ). As a clarification note, please add in whether the restriction would be on the front-end, back-end, or both. Thanks & Good Luck!
    – Howdy_McGee
    Sep 15, 2016 at 3:22

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

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