Create custom user role (client) that can create another custom user role (employee) of that client

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.

Leave a Comment