0 && user_can( $user_id, 'list_users' ); /** * Filter whether the current user can have the Users window * registered. This is the boot-time check; runtime "should the * dock click use the native window?" is the JS-side * `nativeUsersEnabled` flag. * * @since 0.18.0 * * @param bool $can Default: `list_users` capability. * @param int $user_id User being checked. */ return (bool) apply_filters( 'desktop_mode_users_window_user_can_register', $can, $user_id ); } /** * Combined cap-and-opt-in check. Used by callers that want the * combined answer (e.g. analytics, an arrange-menu entry). * * @since 0.18.0 * * @param int|null $user_id Optional. * @return bool */ function desktop_mode_users_window_user_can_use( $user_id = null ) { $user_id = null === $user_id ? get_current_user_id() : (int) $user_id; $cap_ok = desktop_mode_users_window_user_can_register( $user_id ); $opt_in = false; if ( $cap_ok && function_exists( 'desktop_mode_get_os_settings' ) ) { $settings = desktop_mode_get_os_settings( $user_id ); $opt_in = ! empty( $settings['nativeUsersEnabled'] ); } $can = $cap_ok && $opt_in; /** * Filter whether the current user has opted into the native Users * experience. * * @since 0.18.0 * * @param bool $can Default gate result. * @param int $user_id User being checked. */ return (bool) apply_filters( 'desktop_mode_users_window_user_can_use', $can, $user_id ); } /** * Resolve the role slugs the current viewer is allowed to assign to * the given target user. * * Honors core's `editable_roles` filter and applies the standard * role-hierarchy rules: a user can only assign roles whose * capabilities are a subset of their own. We compute this server-side * and surface the result on the row so the UI can hide options the * viewer can't apply, but the REST mutation route re-derives the * exact same list and rejects anything outside it. * * @since 0.18.0 * * @param int $viewer_id Requesting user. * @param int $target_id Target user (optional — used by filters). * @return string[] Role slugs the viewer can assign to the target. */ function desktop_mode_users_window_assignable_roles( $viewer_id, $target_id = 0 ) { $viewer_id = (int) $viewer_id; if ( $viewer_id <= 0 || ! user_can( $viewer_id, 'promote_users' ) ) { return array(); } // Switch to the viewer's perspective so `current_user_can` and // `get_editable_roles` evaluate against their caps, not whoever // happens to be acting at REST-init time. $prev_user = get_current_user_id(); $switched = false; if ( $prev_user !== $viewer_id ) { wp_set_current_user( $viewer_id ); $switched = true; } // `get_editable_roles()` lives in wp-admin/includes/user.php // which is NOT auto-loaded by the time `init` fires (the hook // our window registers on). Without this require_once the // function doesn't exist, the array comes back empty, and the // admin sees only the site's default_role ("subscriber") in // the role dropdown — exactly the symptom that surfaced once // real-world testing started. if ( ! function_exists( 'get_editable_roles' ) ) { require_once ABSPATH . 'wp-admin/includes/user.php'; } $editable = function_exists( 'get_editable_roles' ) ? (array) get_editable_roles() : array(); if ( $switched ) { wp_set_current_user( $prev_user ); } $slugs = array_keys( $editable ); /** * Filter the role slugs assignable by `$viewer_id` to `$target_id`. * * Use this to LOCK DOWN role assignment further (e.g. "site * managers can't promote anyone to administrator even if core * would let them"). Returning an empty array fully disables role * mutation for the viewer. * * Returning a SUPERSET of the default has no effect on the REST * endpoint — that re-derives `get_editable_roles()` itself, this * filter only widens / narrows what the UI surfaces. * * @since 0.18.0 * * @param string[] $slugs Default role slug list. * @param int $viewer_id * @param int $target_id */ return (array) apply_filters( 'desktop_mode_users_window_assignable_roles', $slugs, $viewer_id, $target_id ); }