# desktop-mode/1.1.1/includes/users-window/permissions.php

OpenStation: Desktop Windows, Dock &amp; Virtual Desktops for WP Admin, version 1.1.1. 160 lines.

- Page: https://pluginprobe.com/plugins/desktop-mode/1.1.1/code/includes/users-window/permissions.php
- Raw: https://pluginprobe.com/plugins/desktop-mode/1.1.1/raw/includes/users-window/permissions.php
- Modified: 2026-08-07T20:39:04+00:00

Line numbers below start at 1. Link to a line or a range by appending a fragment to the
page URL, for example `https://pluginprobe.com/plugins/desktop-mode/1.1.1/code/includes/users-window/permissions.php#L10-L20`.

```php
<?php
/**
 * OpenStation — Native Users Window: capability gates.
 *
 * Multi-tier gating, parallel to WordPress core's `users.php` flow:
 *
 *   - `list_users`     → REGISTER the window. Cap-only check; the
 *                        opt-in toggle is JS-side.
 *   - `edit_users`     → mutation quick-actions (Send password reset,
 *                        Resend welcome).
 *   - `promote_users`  → bulk role-change action; per-target gated
 *                        through {@see openstation_users_window_assignable_roles()}.
 *   - `create_users`   → "Add new user" toolbar button.
 *   - `delete_users`   → bulk-delete (single-site).
 *   - `remove_users`   → bulk-remove (multisite — removes from current site,
 *                        leaves the network user record alone).
 *
 * UI-side gating is purely UX polish — the REST routes in `rest.php`
 * re-validate every cap and every per-target permission before
 * mutating anything.
 *
 * @package OpenStation
 */

defined( 'ABSPATH' ) || exit;

/**
 * Whether the user is eligible to have the Users window registered.
 *
 * @param int|null $user_id Optional. Defaults to `get_current_user_id()`.
 * @return bool
 */
function openstation_users_window_user_can_register( $user_id = null ) {
	$user_id = null === $user_id ? get_current_user_id() : (int) $user_id;
	$can     = $user_id > 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.
	 *
	 * @param bool $can     Default: `list_users` capability.
	 * @param int  $user_id User being checked.
	 */
	return (bool) apply_filters(
		'openstation_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).
 *
 * @param int|null $user_id Optional.
 * @return bool
 */
function openstation_users_window_user_can_use( $user_id = null ) {
	$user_id = null === $user_id ? get_current_user_id() : (int) $user_id;

	$cap_ok = openstation_users_window_user_can_register( $user_id );

	$opt_in = false;
	if ( $cap_ok && function_exists( 'openstation_get_os_settings' ) ) {
		$settings = openstation_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.
	 *
	 * @param bool $can     Default gate result.
	 * @param int  $user_id User being checked.
	 */
	return (bool) apply_filters( 'openstation_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. Note core's default returns
 * EVERY registered role (administrator included) to any user with
 * `promote_users` — there is no built-in capability-subset hierarchy
 * in core. Sites wanting stricter rules must filter `editable_roles`
 * or `openstation_users_window_assignable_roles` below. We compute
 * the list server-side and surface it on the row so the UI can hide
 * options the viewer can't apply; the REST mutation routes call this
 * same filtered helper and reject anything outside it.
 *
 * @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 openstation_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 widens the REST endpoints too — both the
	 * bulk-role route and the create-user route validate the requested
	 * role against this same filtered list (see `rest.php`), so only
	 * add roles you genuinely intend to make assignable.
	 *
	 * @param string[] $slugs    Default role slug list.
	 * @param int      $viewer_id
	 * @param int      $target_id
	 */
	return (array) apply_filters(
		'openstation_users_window_assignable_roles',
		$slugs,
		$viewer_id,
		$target_id
	);
}

```
