From a415c29efee45520ae252d2aa28f1083a521cd7b Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Wed, 17 Apr 2024 09:56:49 +0200 Subject: Adding upstream version 6.4.3+dfsg1. Signed-off-by: Daniel Baumann --- wp-includes/class-wp-user.php | 907 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 907 insertions(+) create mode 100644 wp-includes/class-wp-user.php (limited to 'wp-includes/class-wp-user.php') diff --git a/wp-includes/class-wp-user.php b/wp-includes/class-wp-user.php new file mode 100644 index 0000000..aa75980 --- /dev/null +++ b/wp-includes/class-wp-user.php @@ -0,0 +1,907 @@ +prefix; + self::$back_compat_keys = array( + 'user_firstname' => 'first_name', + 'user_lastname' => 'last_name', + 'user_description' => 'description', + 'user_level' => $prefix . 'user_level', + $prefix . 'usersettings' => $prefix . 'user-settings', + $prefix . 'usersettingstime' => $prefix . 'user-settings-time', + ); + } + + if ( $id instanceof WP_User ) { + $this->init( $id->data, $site_id ); + return; + } elseif ( is_object( $id ) ) { + $this->init( $id, $site_id ); + return; + } + + if ( ! empty( $id ) && ! is_numeric( $id ) ) { + $name = $id; + $id = 0; + } + + if ( $id ) { + $data = self::get_data_by( 'id', $id ); + } else { + $data = self::get_data_by( 'login', $name ); + } + + if ( $data ) { + $this->init( $data, $site_id ); + } else { + $this->data = new stdClass(); + } + } + + /** + * Sets up object properties, including capabilities. + * + * @since 3.3.0 + * + * @param object $data User DB row object. + * @param int $site_id Optional. The site ID to initialize for. + */ + public function init( $data, $site_id = '' ) { + if ( ! isset( $data->ID ) ) { + $data->ID = 0; + } + $this->data = $data; + $this->ID = (int) $data->ID; + + $this->for_site( $site_id ); + } + + /** + * Returns only the main user fields. + * + * @since 3.3.0 + * @since 4.4.0 Added 'ID' as an alias of 'id' for the `$field` parameter. + * + * @global wpdb $wpdb WordPress database abstraction object. + * + * @param string $field The field to query against: 'id', 'ID', 'slug', 'email' or 'login'. + * @param string|int $value The field value. + * @return object|false Raw user object. + */ + public static function get_data_by( $field, $value ) { + global $wpdb; + + // 'ID' is an alias of 'id'. + if ( 'ID' === $field ) { + $field = 'id'; + } + + if ( 'id' === $field ) { + // Make sure the value is numeric to avoid casting objects, for example, to int 1. + if ( ! is_numeric( $value ) ) { + return false; + } + $value = (int) $value; + if ( $value < 1 ) { + return false; + } + } else { + $value = trim( $value ); + } + + if ( ! $value ) { + return false; + } + + switch ( $field ) { + case 'id': + $user_id = $value; + $db_field = 'ID'; + break; + case 'slug': + $user_id = wp_cache_get( $value, 'userslugs' ); + $db_field = 'user_nicename'; + break; + case 'email': + $user_id = wp_cache_get( $value, 'useremail' ); + $db_field = 'user_email'; + break; + case 'login': + $value = sanitize_user( $value ); + $user_id = wp_cache_get( $value, 'userlogins' ); + $db_field = 'user_login'; + break; + default: + return false; + } + + if ( false !== $user_id ) { + $user = wp_cache_get( $user_id, 'users' ); + if ( $user ) { + return $user; + } + } + + $user = $wpdb->get_row( + $wpdb->prepare( + "SELECT * FROM $wpdb->users WHERE $db_field = %s LIMIT 1", + $value + ) + ); + if ( ! $user ) { + return false; + } + + update_user_caches( $user ); + + return $user; + } + + /** + * Magic method for checking the existence of a certain custom field. + * + * @since 3.3.0 + * + * @param string $key User meta key to check if set. + * @return bool Whether the given user meta key is set. + */ + public function __isset( $key ) { + if ( 'id' === $key ) { + _deprecated_argument( + 'WP_User->id', + '2.1.0', + sprintf( + /* translators: %s: WP_User->ID */ + __( 'Use %s instead.' ), + 'WP_User->ID' + ) + ); + $key = 'ID'; + } + + if ( isset( $this->data->$key ) ) { + return true; + } + + if ( isset( self::$back_compat_keys[ $key ] ) ) { + $key = self::$back_compat_keys[ $key ]; + } + + return metadata_exists( 'user', $this->ID, $key ); + } + + /** + * Magic method for accessing custom fields. + * + * @since 3.3.0 + * + * @param string $key User meta key to retrieve. + * @return mixed Value of the given user meta key (if set). If `$key` is 'id', the user ID. + */ + public function __get( $key ) { + if ( 'id' === $key ) { + _deprecated_argument( + 'WP_User->id', + '2.1.0', + sprintf( + /* translators: %s: WP_User->ID */ + __( 'Use %s instead.' ), + 'WP_User->ID' + ) + ); + return $this->ID; + } + + if ( isset( $this->data->$key ) ) { + $value = $this->data->$key; + } else { + if ( isset( self::$back_compat_keys[ $key ] ) ) { + $key = self::$back_compat_keys[ $key ]; + } + $value = get_user_meta( $this->ID, $key, true ); + } + + if ( $this->filter ) { + $value = sanitize_user_field( $key, $value, $this->ID, $this->filter ); + } + + return $value; + } + + /** + * Magic method for setting custom user fields. + * + * This method does not update custom fields in the database. It only stores + * the value on the WP_User instance. + * + * @since 3.3.0 + * + * @param string $key User meta key. + * @param mixed $value User meta value. + */ + public function __set( $key, $value ) { + if ( 'id' === $key ) { + _deprecated_argument( + 'WP_User->id', + '2.1.0', + sprintf( + /* translators: %s: WP_User->ID */ + __( 'Use %s instead.' ), + 'WP_User->ID' + ) + ); + $this->ID = $value; + return; + } + + $this->data->$key = $value; + } + + /** + * Magic method for unsetting a certain custom field. + * + * @since 4.4.0 + * + * @param string $key User meta key to unset. + */ + public function __unset( $key ) { + if ( 'id' === $key ) { + _deprecated_argument( + 'WP_User->id', + '2.1.0', + sprintf( + /* translators: %s: WP_User->ID */ + __( 'Use %s instead.' ), + 'WP_User->ID' + ) + ); + } + + if ( isset( $this->data->$key ) ) { + unset( $this->data->$key ); + } + + if ( isset( self::$back_compat_keys[ $key ] ) ) { + unset( self::$back_compat_keys[ $key ] ); + } + } + + /** + * Determines whether the user exists in the database. + * + * @since 3.4.0 + * + * @return bool True if user exists in the database, false if not. + */ + public function exists() { + return ! empty( $this->ID ); + } + + /** + * Retrieves the value of a property or meta key. + * + * Retrieves from the users and usermeta table. + * + * @since 3.3.0 + * + * @param string $key Property + * @return mixed + */ + public function get( $key ) { + return $this->__get( $key ); + } + + /** + * Determines whether a property or meta key is set. + * + * Consults the users and usermeta tables. + * + * @since 3.3.0 + * + * @param string $key Property. + * @return bool + */ + public function has_prop( $key ) { + return $this->__isset( $key ); + } + + /** + * Returns an array representation. + * + * @since 3.5.0 + * + * @return array Array representation. + */ + public function to_array() { + return get_object_vars( $this->data ); + } + + /** + * Makes private/protected methods readable for backward compatibility. + * + * @since 4.3.0 + * + * @param string $name Method to call. + * @param array $arguments Arguments to pass when calling. + * @return mixed|false Return value of the callback, false otherwise. + */ + public function __call( $name, $arguments ) { + if ( '_init_caps' === $name ) { + return $this->_init_caps( ...$arguments ); + } + return false; + } + + /** + * Sets up capability object properties. + * + * Will set the value for the 'cap_key' property to current database table + * prefix, followed by 'capabilities'. Will then check to see if the + * property matching the 'cap_key' exists and is an array. If so, it will be + * used. + * + * @since 2.1.0 + * @deprecated 4.9.0 Use WP_User::for_site() + * + * @global wpdb $wpdb WordPress database abstraction object. + * + * @param string $cap_key Optional capability key + */ + protected function _init_caps( $cap_key = '' ) { + global $wpdb; + + _deprecated_function( __METHOD__, '4.9.0', 'WP_User::for_site()' ); + + if ( empty( $cap_key ) ) { + $this->cap_key = $wpdb->get_blog_prefix( $this->site_id ) . 'capabilities'; + } else { + $this->cap_key = $cap_key; + } + + $this->caps = $this->get_caps_data(); + + $this->get_role_caps(); + } + + /** + * Retrieves all of the capabilities of the user's roles, and merges them with + * individual user capabilities. + * + * All of the capabilities of the user's roles are merged with the user's individual + * capabilities. This means that the user can be denied specific capabilities that + * their role might have, but the user is specifically denied. + * + * @since 2.0.0 + * + * @return bool[] Array of key/value pairs where keys represent a capability name + * and boolean values represent whether the user has that capability. + */ + public function get_role_caps() { + $switch_site = false; + if ( is_multisite() && get_current_blog_id() !== $this->site_id ) { + $switch_site = true; + + switch_to_blog( $this->site_id ); + } + + $wp_roles = wp_roles(); + + // Filter out caps that are not role names and assign to $this->roles. + if ( is_array( $this->caps ) ) { + $this->roles = array_filter( array_keys( $this->caps ), array( $wp_roles, 'is_role' ) ); + } + + // Build $allcaps from role caps, overlay user's $caps. + $this->allcaps = array(); + foreach ( (array) $this->roles as $role ) { + $the_role = $wp_roles->get_role( $role ); + $this->allcaps = array_merge( (array) $this->allcaps, (array) $the_role->capabilities ); + } + $this->allcaps = array_merge( (array) $this->allcaps, (array) $this->caps ); + + if ( $switch_site ) { + restore_current_blog(); + } + + return $this->allcaps; + } + + /** + * Adds role to user. + * + * Updates the user's meta data option with capabilities and roles. + * + * @since 2.0.0 + * + * @param string $role Role name. + */ + public function add_role( $role ) { + if ( empty( $role ) ) { + return; + } + + if ( in_array( $role, $this->roles, true ) ) { + return; + } + + $this->caps[ $role ] = true; + update_user_meta( $this->ID, $this->cap_key, $this->caps ); + $this->get_role_caps(); + $this->update_user_level_from_caps(); + + /** + * Fires immediately after the user has been given a new role. + * + * @since 4.3.0 + * + * @param int $user_id The user ID. + * @param string $role The new role. + */ + do_action( 'add_user_role', $this->ID, $role ); + } + + /** + * Removes role from user. + * + * @since 2.0.0 + * + * @param string $role Role name. + */ + public function remove_role( $role ) { + if ( ! in_array( $role, $this->roles, true ) ) { + return; + } + + unset( $this->caps[ $role ] ); + update_user_meta( $this->ID, $this->cap_key, $this->caps ); + $this->get_role_caps(); + $this->update_user_level_from_caps(); + + /** + * Fires immediately after a role as been removed from a user. + * + * @since 4.3.0 + * + * @param int $user_id The user ID. + * @param string $role The removed role. + */ + do_action( 'remove_user_role', $this->ID, $role ); + } + + /** + * Sets the role of the user. + * + * This will remove the previous roles of the user and assign the user the + * new one. You can set the role to an empty string and it will remove all + * of the roles from the user. + * + * @since 2.0.0 + * + * @param string $role Role name. + */ + public function set_role( $role ) { + if ( 1 === count( $this->roles ) && current( $this->roles ) === $role ) { + return; + } + + foreach ( (array) $this->roles as $oldrole ) { + unset( $this->caps[ $oldrole ] ); + } + + $old_roles = $this->roles; + + if ( ! empty( $role ) ) { + $this->caps[ $role ] = true; + $this->roles = array( $role => true ); + } else { + $this->roles = array(); + } + + update_user_meta( $this->ID, $this->cap_key, $this->caps ); + $this->get_role_caps(); + $this->update_user_level_from_caps(); + + foreach ( $old_roles as $old_role ) { + if ( ! $old_role || $old_role === $role ) { + continue; + } + + /** This action is documented in wp-includes/class-wp-user.php */ + do_action( 'remove_user_role', $this->ID, $old_role ); + } + + if ( $role && ! in_array( $role, $old_roles, true ) ) { + /** This action is documented in wp-includes/class-wp-user.php */ + do_action( 'add_user_role', $this->ID, $role ); + } + + /** + * Fires after the user's role has changed. + * + * @since 2.9.0 + * @since 3.6.0 Added $old_roles to include an array of the user's previous roles. + * + * @param int $user_id The user ID. + * @param string $role The new role. + * @param string[] $old_roles An array of the user's previous roles. + */ + do_action( 'set_user_role', $this->ID, $role, $old_roles ); + } + + /** + * Chooses the maximum level the user has. + * + * Will compare the level from the $item parameter against the $max + * parameter. If the item is incorrect, then just the $max parameter value + * will be returned. + * + * Used to get the max level based on the capabilities the user has. This + * is also based on roles, so if the user is assigned the Administrator role + * then the capability 'level_10' will exist and the user will get that + * value. + * + * @since 2.0.0 + * + * @param int $max Max level of user. + * @param string $item Level capability name. + * @return int Max Level. + */ + public function level_reduction( $max, $item ) { + if ( preg_match( '/^level_(10|[0-9])$/i', $item, $matches ) ) { + $level = (int) $matches[1]; + return max( $max, $level ); + } else { + return $max; + } + } + + /** + * Updates the maximum user level for the user. + * + * Updates the 'user_level' user metadata (includes prefix that is the + * database table prefix) with the maximum user level. Gets the value from + * the all of the capabilities that the user has. + * + * @since 2.0.0 + * + * @global wpdb $wpdb WordPress database abstraction object. + */ + public function update_user_level_from_caps() { + global $wpdb; + $this->user_level = array_reduce( array_keys( $this->allcaps ), array( $this, 'level_reduction' ), 0 ); + update_user_meta( $this->ID, $wpdb->get_blog_prefix() . 'user_level', $this->user_level ); + } + + /** + * Adds capability and grant or deny access to capability. + * + * @since 2.0.0 + * + * @param string $cap Capability name. + * @param bool $grant Whether to grant capability to user. + */ + public function add_cap( $cap, $grant = true ) { + $this->caps[ $cap ] = $grant; + update_user_meta( $this->ID, $this->cap_key, $this->caps ); + $this->get_role_caps(); + $this->update_user_level_from_caps(); + } + + /** + * Removes capability from user. + * + * @since 2.0.0 + * + * @param string $cap Capability name. + */ + public function remove_cap( $cap ) { + if ( ! isset( $this->caps[ $cap ] ) ) { + return; + } + unset( $this->caps[ $cap ] ); + update_user_meta( $this->ID, $this->cap_key, $this->caps ); + $this->get_role_caps(); + $this->update_user_level_from_caps(); + } + + /** + * Removes all of the capabilities of the user. + * + * @since 2.1.0 + * + * @global wpdb $wpdb WordPress database abstraction object. + */ + public function remove_all_caps() { + global $wpdb; + $this->caps = array(); + delete_user_meta( $this->ID, $this->cap_key ); + delete_user_meta( $this->ID, $wpdb->get_blog_prefix() . 'user_level' ); + $this->get_role_caps(); + } + + /** + * Returns whether the user has the specified capability. + * + * This function also accepts an ID of an object to check against if the capability is a meta capability. Meta + * capabilities such as `edit_post` and `edit_user` are capabilities used by the `map_meta_cap()` function to + * map to primitive capabilities that a user or role has, such as `edit_posts` and `edit_others_posts`. + * + * Example usage: + * + * $user->has_cap( 'edit_posts' ); + * $user->has_cap( 'edit_post', $post->ID ); + * $user->has_cap( 'edit_post_meta', $post->ID, $meta_key ); + * + * While checking against a role in place of a capability is supported in part, this practice is discouraged as it + * may produce unreliable results. + * + * @since 2.0.0 + * @since 5.3.0 Formalized the existing and already documented `...$args` parameter + * by adding it to the function signature. + * + * @see map_meta_cap() + * + * @param string $cap Capability name. + * @param mixed ...$args Optional further parameters, typically starting with an object ID. + * @return bool Whether the user has the given capability, or, if an object ID is passed, whether the user has + * the given capability for that object. + */ + public function has_cap( $cap, ...$args ) { + if ( is_numeric( $cap ) ) { + _deprecated_argument( __FUNCTION__, '2.0.0', __( 'Usage of user levels is deprecated. Use capabilities instead.' ) ); + $cap = $this->translate_level_to_cap( $cap ); + } + + $caps = map_meta_cap( $cap, $this->ID, ...$args ); + + // Multisite super admin has all caps by definition, Unless specifically denied. + if ( is_multisite() && is_super_admin( $this->ID ) ) { + if ( in_array( 'do_not_allow', $caps, true ) ) { + return false; + } + return true; + } + + // Maintain BC for the argument passed to the "user_has_cap" filter. + $args = array_merge( array( $cap, $this->ID ), $args ); + + /** + * Dynamically filter a user's capabilities. + * + * @since 2.0.0 + * @since 3.7.0 Added the `$user` parameter. + * + * @param bool[] $allcaps Array of key/value pairs where keys represent a capability name + * and boolean values represent whether the user has that capability. + * @param string[] $caps Required primitive capabilities for the requested capability. + * @param array $args { + * Arguments that accompany the requested capability check. + * + * @type string $0 Requested capability. + * @type int $1 Concerned user ID. + * @type mixed ...$2 Optional second and further parameters, typically object ID. + * } + * @param WP_User $user The user object. + */ + $capabilities = apply_filters( 'user_has_cap', $this->allcaps, $caps, $args, $this ); + + // Everyone is allowed to exist. + $capabilities['exist'] = true; + + // Nobody is allowed to do things they are not allowed to do. + unset( $capabilities['do_not_allow'] ); + + // Must have ALL requested caps. + foreach ( (array) $caps as $cap ) { + if ( empty( $capabilities[ $cap ] ) ) { + return false; + } + } + + return true; + } + + /** + * Converts numeric level to level capability name. + * + * Prepends 'level_' to level number. + * + * @since 2.0.0 + * + * @param int $level Level number, 1 to 10. + * @return string + */ + public function translate_level_to_cap( $level ) { + return 'level_' . $level; + } + + /** + * Sets the site to operate on. Defaults to the current site. + * + * @since 3.0.0 + * @deprecated 4.9.0 Use WP_User::for_site() + * + * @param int $blog_id Optional. Site ID, defaults to current site. + */ + public function for_blog( $blog_id = '' ) { + _deprecated_function( __METHOD__, '4.9.0', 'WP_User::for_site()' ); + + $this->for_site( $blog_id ); + } + + /** + * Sets the site to operate on. Defaults to the current site. + * + * @since 4.9.0 + * + * @global wpdb $wpdb WordPress database abstraction object. + * + * @param int $site_id Site ID to initialize user capabilities for. Default is the current site. + */ + public function for_site( $site_id = '' ) { + global $wpdb; + + if ( ! empty( $site_id ) ) { + $this->site_id = absint( $site_id ); + } else { + $this->site_id = get_current_blog_id(); + } + + $this->cap_key = $wpdb->get_blog_prefix( $this->site_id ) . 'capabilities'; + + $this->caps = $this->get_caps_data(); + + $this->get_role_caps(); + } + + /** + * Gets the ID of the site for which the user's capabilities are currently initialized. + * + * @since 4.9.0 + * + * @return int Site ID. + */ + public function get_site_id() { + return $this->site_id; + } + + /** + * Gets the available user capabilities data. + * + * @since 4.9.0 + * + * @return bool[] List of capabilities keyed by the capability name, + * e.g. `array( 'edit_posts' => true, 'delete_posts' => false )`. + */ + private function get_caps_data() { + $caps = get_user_meta( $this->ID, $this->cap_key, true ); + + if ( ! is_array( $caps ) ) { + return array(); + } + + return $caps; + } +} -- cgit v1.2.3