From e059fb31f617bbe1e0641b090a9fdaec45f26008 Mon Sep 17 00:00:00 2001 From: David Baumwald Date: Mon, 19 Sep 2022 20:49:10 +0000 Subject: [PATCH] Role/Capability: Add a new `update_role` function. Until now, changing a user's role involved deleting a user's role then re-adding. This change creates a new `update_role` function and associated method in `WP_Roles` to consolidate this process. This commit also introduces new unit tests around `update_role` and adds additional "unhappy path" tests for roles and capabilities in general. Props maksimkuzmin, peterwilsoncc, NomNom99, costdev, SergeyBiryukov. Fixes #54572. Built from https://develop.svn.wordpress.org/trunk@54213 git-svn-id: https://core.svn.wordpress.org/trunk@53772 1a063a9b-81f0-0310-95a4-ce76da25c4cd --- wp-includes/capabilities.php | 24 ++++++++++++ wp-includes/class-wp-roles.php | 70 ++++++++++++++++++++++++++++++++++ wp-includes/version.php | 2 +- 3 files changed, 95 insertions(+), 1 deletion(-) diff --git a/wp-includes/capabilities.php b/wp-includes/capabilities.php index d06887a2bb..1e55d55f53 100644 --- a/wp-includes/capabilities.php +++ b/wp-includes/capabilities.php @@ -1030,6 +1030,30 @@ function add_role( $role, $display_name, $capabilities = array() ) { return wp_roles()->add_role( $role, $display_name, $capabilities ); } +/** + * Updates an existing role. Creates a new role if it doesn't exist. + * + * Modifies the display name and/or capabilities for an existing role. + * If the role does not exist then a new role is created. + * + * The capabilities are defined in the following format: `array( 'read' => true )`. + * To explicitly deny the role a capability, set the value for that capability to false. + * + * @since 6.1.0 + * + * @param string $role Role name. + * @param string|null $display_name Optional. Role display name. If null, the display name + * is not modified. Default null. + * @param bool[]|null $capabilities Optional. List of capabilities keyed by the capability name, + * e.g. `array( 'edit_posts' => true, 'delete_posts' => false )`. + * If null, don't alter capabilities for the existing role and make + * empty capabilities for the new one. Default null. + * @return WP_Role|void WP_Role object, if the role is updated. + */ +function update_role( $role, $display_name = null, $capabilities = null ) { + return wp_roles()->update_role( $role, $display_name, $capabilities ); +} + /** * Removes a role, if it exists. * diff --git a/wp-includes/class-wp-roles.php b/wp-includes/class-wp-roles.php index f224250fa2..d5eb97ef38 100644 --- a/wp-includes/class-wp-roles.php +++ b/wp-includes/class-wp-roles.php @@ -172,6 +172,76 @@ public function add_role( $role, $display_name, $capabilities = array() ) { return $this->role_objects[ $role ]; } + /** + * Updates an existing role. Creates a new role if it doesn't exist. + * + * Modifies the display name and/or capabilities for an existing role. + * If the role does not exist then a new role is created. + * + * The capabilities are defined in the following format: `array( 'read' => true )`. + * To explicitly deny the role a capability, set the value for that capability to false. + * + * @since 6.1.0 + * + * @param string $role Role name. + * @param string|null $display_name Optional. Role display name. If null, the display name + * is not modified. Default null. + * @param bool[]|null $capabilities Optional. List of capabilities keyed by the capability name, + * e.g. `array( 'edit_posts' => true, 'delete_posts' => false )`. + * If null, don't alter capabilities for the existing role and make + * empty capabilities for the new one. Default null. + * @return WP_Role|void WP_Role object, if the role is updated. + */ + public function update_role( $role, $display_name = null, $capabilities = null ) { + if ( ! is_string( $role ) || '' === trim( $role ) ) { + return; + } + + if ( null !== $display_name && ( ! is_string( $display_name ) || '' === trim( $display_name ) ) ) { + return; + } + + if ( null !== $capabilities && ! is_array( $capabilities ) ) { + return; + } + + if ( null === $display_name && null === $capabilities ) { + if ( isset( $this->role_objects[ $role ] ) ) { + return $this->role_objects[ $role ]; + } + return; + } + + if ( null === $display_name ) { + if ( ! isset( $this->role_objects[ $role ] ) ) { + return; + } + + $display_name = $this->roles[ $role ]['name']; + } + + if ( null === $capabilities ) { + if ( isset( $this->role_objects[ $role ] ) ) { + $capabilities = $this->role_objects[ $role ]->capabilities; + } else { + $capabilities = array(); + } + } + + if ( isset( $this->roles[ $role ] ) ) { + if ( null === $capabilities ) { + $capabilities = $this->role_objects[ $role ]->capabilities; + } + + unset( $this->role_objects[ $role ] ); + unset( $this->role_names[ $role ] ); + unset( $this->roles[ $role ] ); + } + + // The roles database option will be updated in ::add_role(). + return $this->add_role( $role, $display_name, $capabilities ); + } + /** * Removes a role by name. * diff --git a/wp-includes/version.php b/wp-includes/version.php index 3993ff4c25..fab44a26d4 100644 --- a/wp-includes/version.php +++ b/wp-includes/version.php @@ -16,7 +16,7 @@ * * @global string $wp_version */ -$wp_version = '6.1-alpha-54212'; +$wp_version = '6.1-alpha-54213'; /** * Holds the WordPress DB revision, increments when changes are made to the WordPress DB schema.