| @@ -22,32 +22,78 @@ | ||
| 22 | 22 | if ( !defined( 'ABSPATH' ) ) { |
| 23 | 23 | exit; |
| 24 | 24 | } |
| 25 | 25 | |
| 26 | -require_once( GROUPS_CORE_LIB . "/interface-i-capable.php" ); | |
| 27 | -require_once( GROUPS_CORE_LIB . "/class-groups-capability.php" ); | |
| 26 | +// phpcs:disable PluginCheck.Security.DirectDB.UnescapedDBParameter, WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching | |
| 28 | 27 | |
| 28 | +require_once GROUPS_CORE_LIB . '/interface-i-capable.php'; | |
| 29 | +require_once GROUPS_CORE_LIB . '/class-groups-capability.php'; | |
| 30 | + | |
| 29 | 31 | /** |
| 30 | 32 | * User OPM. |
| 31 | 33 | */ |
| 32 | 34 | class Groups_User implements I_Capable { |
| 33 | 35 | |
| 34 | - const CACHE_GROUP = 'groups'; | |
| 35 | - const CAPABILITIES = 'capabilities'; | |
| 36 | - const CAPABILITIES_BASE = 'capabilities_base'; | |
| 37 | - const CAPABILITY_IDS = 'capability_ids'; | |
| 36 | + /** | |
| 37 | + * @var string cache group key | |
| 38 | + */ | |
| 39 | + const CACHE_GROUP = 'groups'; | |
| 40 | + | |
| 41 | + /** | |
| 42 | + * @var string cache key prefix | |
| 43 | + */ | |
| 44 | + const CAPABILITIES = 'capabilities'; | |
| 45 | + | |
| 46 | + /** | |
| 47 | + * @var string cache key prefix | |
| 48 | + */ | |
| 49 | + const CAPABILITIES_BASE = 'capabilities_base'; | |
| 50 | + | |
| 51 | + /** | |
| 52 | + * @var string cache key prefix | |
| 53 | + * | |
| 54 | + * @since 3.6.0 | |
| 55 | + */ | |
| 56 | + const CAPABILITIES_DEEP = 'capabilities_deep'; | |
| 57 | + | |
| 58 | + /** | |
| 59 | + * @var string cache key prefix | |
| 60 | + */ | |
| 61 | + const CAPABILITY_IDS = 'capability_ids'; | |
| 62 | + | |
| 63 | + /** | |
| 64 | + * @var string cache key prefix | |
| 65 | + */ | |
| 38 | 66 | const CAPABILITY_IDS_BASE = 'capability_ids_base'; |
| 39 | - const GROUP_IDS = 'group_ids'; | |
| 40 | - const GROUP_IDS_BASE = 'group_ids_base'; | |
| 41 | - const GROUPS = 'groups'; | |
| 42 | - const GROUPS_BASE = 'groups_base'; | |
| 43 | 67 | |
| 44 | 68 | /** |
| 69 | + * @var string cache key prefix | |
| 70 | + */ | |
| 71 | + const GROUP_IDS = 'group_ids'; | |
| 72 | + | |
| 73 | + /** | |
| 74 | + * @var string cache key prefix | |
| 75 | + */ | |
| 76 | + const GROUP_IDS_BASE = 'group_ids_base'; | |
| 77 | + | |
| 78 | + /** | |
| 79 | + * @var string cache key prefix | |
| 80 | + */ | |
| 81 | + const GROUPS = 'groups'; | |
| 82 | + | |
| 83 | + /** | |
| 84 | + * @var string cache key prefix | |
| 85 | + */ | |
| 86 | + const GROUPS_BASE = 'groups_base'; | |
| 87 | + | |
| 88 | + /** | |
| 45 | 89 | * User object. |
| 46 | - * | |
| 90 | + * | |
| 91 | + * @access private - Use $this->get_user() instead as this property will be made private in a future release of Groups | |
| 92 | + * | |
| 47 | 93 | * @var WP_User |
| 48 | 94 | */ |
| 49 | - var $user = null; | |
| 95 | + public $user = null; | |
| 50 | 96 | |
| 51 | 97 | /** |
| 52 | 98 | * Hook cache clearers to actions that can modify the capabilities. |
| 53 | 99 | */ |
| @@ -64,8 +110,9 @@ | ||
| 64 | 110 | } |
| 65 | 111 | |
| 66 | 112 | /** |
| 67 | 113 | * Clear cache objects for the user. |
| 114 | + * | |
| 68 | 115 | * @param int $user_id |
| 69 | 116 | */ |
| 70 | 117 | public static function clear_cache( $user_id ) { |
| 71 | 118 | // be lazy, clear the entries so they are rebuilt when requested |
| @@ -70,8 +117,9 @@ | ||
| 70 | 117 | public static function clear_cache( $user_id ) { |
| 71 | 118 | // be lazy, clear the entries so they are rebuilt when requested |
| 72 | 119 | Groups_Cache::delete( self::CAPABILITIES . $user_id, self::CACHE_GROUP ); |
| 73 | 120 | Groups_Cache::delete( self::CAPABILITIES_BASE . $user_id, self::CACHE_GROUP ); |
| 121 | + Groups_Cache::delete( self::CAPABILITIES_DEEP . $user_id, self::CACHE_GROUP ); | |
| 74 | 122 | Groups_Cache::delete( self::CAPABILITY_IDS . $user_id, self::CACHE_GROUP ); |
| 75 | 123 | Groups_Cache::delete( self::CAPABILITY_IDS_BASE . $user_id, self::CACHE_GROUP ); |
| 76 | 124 | Groups_Cache::delete( self::GROUP_IDS . $user_id, self::CACHE_GROUP ); |
| 77 | 125 | Groups_Cache::delete( self::GROUP_IDS_BASE . $user_id, self::CACHE_GROUP ); |
| @@ -80,21 +128,22 @@ | ||
| 80 | 128 | } |
| 81 | 129 | |
| 82 | 130 | /** |
| 83 | 131 | * Clear cache objects for all users in the group. |
| 84 | - * @param unknown_type $group_id | |
| 132 | + * | |
| 133 | + * @param int $group_id | |
| 85 | 134 | */ |
| 86 | 135 | public static function clear_cache_for_group( $group_id ) { |
| 87 | 136 | global $wpdb; |
| 88 | 137 | if ( $group = Groups_Group::read( $group_id ) ) { |
| 89 | 138 | // not using $group->users, as we don't need a lot of user objects created here |
| 90 | - $user_group_table = _groups_get_tablename( "user_group" ); | |
| 139 | + $user_group_table = _groups_get_tablename( 'user_group' ); | |
| 91 | 140 | $users = $wpdb->get_results( $wpdb->prepare( |
| 92 | - "SELECT ID FROM $wpdb->users LEFT JOIN $user_group_table ON $wpdb->users.ID = $user_group_table.user_id WHERE $user_group_table.group_id = %d", | |
| 141 | + "SELECT ID FROM $wpdb->users LEFT JOIN $user_group_table ON $wpdb->users.ID = $user_group_table.user_id WHERE $user_group_table.group_id = %d", // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared | |
| 93 | 142 | Groups_Utility::id( $group_id ) |
| 94 | 143 | ) ); |
| 95 | 144 | if ( $users ) { |
| 96 | - foreach( $users as $user ) { | |
| 145 | + foreach ( $users as $user ) { | |
| 97 | 146 | self::clear_cache( $user->ID ); |
| 98 | 147 | } |
| 99 | 148 | } |
| 100 | 149 | } |
| @@ -100,23 +149,227 @@ | ||
| 100 | 149 | } |
| 101 | 150 | } |
| 102 | 151 | |
| 103 | 152 | /** |
| 153 | + * Convenience method equivalent to $this->is_member( $group_id ) without having to instantiate a Groups_User object outside first. | |
| 154 | + * | |
| 155 | + * @since 2.20.0 | |
| 156 | + * | |
| 157 | + * @param int|\WP_User $user user ID or user object | |
| 158 | + * @param int $group_id group ID | |
| 159 | + * | |
| 160 | + * @return boolean | |
| 161 | + */ | |
| 162 | + public static function user_is_member( $user, $group_id ) { | |
| 163 | + $is_member = false; | |
| 164 | + $user_id = null; | |
| 165 | + if ( is_numeric( $user ) ) { | |
| 166 | + $user_id = max( 0, intval( $user ) ); | |
| 167 | + } else if ( $user instanceof \WP_User ) { | |
| 168 | + $user_id = $user->ID; | |
| 169 | + } | |
| 170 | + if ( $user_id !== null ) { | |
| 171 | + $groups_user = new Groups_User( $user_id ); | |
| 172 | + $is_member = $groups_user->is_member( $group_id ); | |
| 173 | + } | |
| 174 | + return $is_member; | |
| 175 | + } | |
| 176 | + | |
| 177 | + /** | |
| 178 | + * Whether the current user has the capability. | |
| 179 | + * | |
| 180 | + * @since 3.0.0 | |
| 181 | + * | |
| 182 | + * @param string $capability capability name | |
| 183 | + * @param mixed ...$args optional parameters, typically an object ID | |
| 184 | + * | |
| 185 | + * @return boolean | |
| 186 | + */ | |
| 187 | + public static function current_user_can( $capability, ...$args ) { | |
| 188 | + // | |
| 189 | + // The global $current_user is determined in the privately scoped function _wp_get_current_user() defined in wp-includes/user.php. | |
| 190 | + // The only call to that function is made in wp_get_current_user(), defined in wp-includes/pluggable.php. | |
| 191 | + // | |
| 192 | + // A call to current_user_can() defined in wp-includes/capabilities.php which is using wp_get_current_user() | |
| 193 | + // which is defined in wp-includes/pluggable.php. The latter is simply wrapping a call to _wp_get_current_user(). | |
| 194 | + // The wp-includes/pluggable.php is loaded after all plugins have been loaded in a loop in wp-settings.php, whereas | |
| 195 | + // wp-includes/user.php is loaded before. So if a plugin makes use of current_user_can() during the loop in wp-settings.php | |
| 196 | + // which loads all plugins, the function wp_get_current_user() is not yet defined and a call to current_user_can() ends | |
| 197 | + // up creating an error. The function wp_get_current_user() can be defined by a plugin in which case the native | |
| 198 | + // WordPress version is not used. Thus we should not simply load wp-includes/pluggable.php in the "plugin-loading-loop" | |
| 199 | + // as that would void the ability of overriding the wp_get_current_user() function via a plugin as the standard is loaded | |
| 200 | + // before that loop ends loading all plugins. | |
| 201 | + // | |
| 202 | + // So if wp_get_current_user() is not yet defined, the global $current_user is not yet determined. | |
| 203 | + // Because of that, a call to current_user_can() should assume the logged out or anonymous user. | |
| 204 | + // This is the correct way to handle the situation when wp_get_current_user() is not defined yet, | |
| 205 | + // and we SHOULD NOT load wp-includes/pluggable.php. | |
| 206 | + // | |
| 207 | + $user = 0; | |
| 208 | + if ( function_exists( 'wp_get_current_user' ) ) { | |
| 209 | + $user = wp_get_current_user(); | |
| 210 | + } | |
| 211 | + return self::user_can( $user, $capability, ...$args ); | |
| 212 | + } | |
| 213 | + | |
| 214 | + /** | |
| 215 | + * Whether the given user has the capability. | |
| 216 | + * | |
| 217 | + * @since 3.0.0 | |
| 218 | + * | |
| 219 | + * @param int|WP_User $user user ID or object | |
| 220 | + * @param string $capability capability name | |
| 221 | + * @param mixed ...$args optional parameters, typically an object ID | |
| 222 | + * | |
| 223 | + * @return boolean | |
| 224 | + */ | |
| 225 | + public static function user_can( $user, $capability, ...$args ) { | |
| 226 | + // If $user is not an object, user_can() will call get_userdata() which is defined in wp-includes/pluggable.php. | |
| 227 | + if ( function_exists( 'user_can' ) && function_exists( 'get_userdata' ) ) { | |
| 228 | + return user_can( $user, $capability, ...$args ); | |
| 229 | + } | |
| 230 | + // So we will have just the same problem as with current_user_can() unless we avoid getting functions involved which are not yet defined. | |
| 231 | + // user_can() calls $user->has_cap() to produce the result, same here: | |
| 232 | + if ( !( $user instanceof WP_User ) ) { | |
| 233 | + if ( is_numeric( $user ) ) { | |
| 234 | + $user_id = intval( $user ); | |
| 235 | + $user = new WP_User( $user_id ); | |
| 236 | + } else { | |
| 237 | + $user = new WP_User( 0 ); | |
| 238 | + } | |
| 239 | + } | |
| 240 | + if ( $user instanceof WP_User ) { | |
| 241 | + return $user->has_cap( $capability, ...$args ); | |
| 242 | + } | |
| 243 | + return false; | |
| 244 | + } | |
| 245 | + | |
| 246 | + /** | |
| 104 | 247 | * Create, if $user_id = 0 an anonymous user is assumed. |
| 105 | 248 | * |
| 106 | 249 | * @param int $user_id |
| 107 | 250 | */ |
| 108 | - public function __construct( $user_id ) { | |
| 109 | - if ( Groups_Utility::id( $user_id ) ) { | |
| 110 | - $this->user = get_user_by( "id", $user_id ); | |
| 111 | - } else { | |
| 112 | - $this->user = new WP_User( 0 ); | |
| 251 | + public function __construct( $user_id = null ) { | |
| 252 | + if ( $user_id !== null ) { | |
| 253 | + if ( Groups_Utility::id( $user_id ) ) { | |
| 254 | + $this->user = get_user_by( 'id', $user_id ); | |
| 255 | + if ( !$this->user ) { | |
| 256 | + $this->user = new WP_User( 0 ); | |
| 257 | + } | |
| 258 | + } else { | |
| 259 | + $this->user = new WP_User( 0 ); | |
| 260 | + } | |
| 113 | 261 | } |
| 114 | 262 | } |
| 115 | 263 | |
| 116 | 264 | /** |
| 265 | + * Provide the related WP_User object. | |
| 266 | + * | |
| 267 | + * @return WP_User | |
| 268 | + */ | |
| 269 | + public function get_user() { | |
| 270 | + return $this->user; | |
| 271 | + } | |
| 272 | + | |
| 273 | + /** | |
| 274 | + * Set the related WP_User object. | |
| 275 | + * | |
| 276 | + * @param WP_User $user the user object | |
| 277 | + */ | |
| 278 | + public function set_user( $user ) { | |
| 279 | + if ( $user instanceof WP_User ) { | |
| 280 | + $this->user = $user; | |
| 281 | + } | |
| 282 | + } | |
| 283 | + | |
| 284 | + /** | |
| 285 | + * Provide the ID of the related WP_User object. | |
| 286 | + * | |
| 287 | + * @return int|null | |
| 288 | + */ | |
| 289 | + public function get_user_id() { | |
| 290 | + $user_id = null; | |
| 291 | + if ( $this->user !== null && $this->user instanceof WP_User ) { | |
| 292 | + $user_id = $this->user->ID; | |
| 293 | + } | |
| 294 | + return $user_id; | |
| 295 | + } | |
| 296 | + | |
| 297 | + /** | |
| 298 | + * Provides the capabilities of the object. | |
| 299 | + * | |
| 300 | + * @return Groups_Capability[] | |
| 301 | + */ | |
| 302 | + public function get_capabilities() { | |
| 303 | + return $this->capabilities; // @phpstan-ignore property.notFound | |
| 304 | + } | |
| 305 | + | |
| 306 | + /** | |
| 307 | + * Provides the IDs of the capabilities of this object. | |
| 308 | + * | |
| 309 | + * @return int[] | |
| 310 | + */ | |
| 311 | + public function get_capability_ids() { | |
| 312 | + return $this->capability_ids; // @phpstan-ignore property.notFound | |
| 313 | + } | |
| 314 | + | |
| 315 | + /** | |
| 316 | + * Provides the capabilities of the object, those of the user's roles and those of the object's groups, including from ancestor groups. | |
| 317 | + * | |
| 318 | + * @return Groups_Capability[] | |
| 319 | + */ | |
| 320 | + public function get_capabilities_deep() { | |
| 321 | + return $this->capabilities_deep; // @phpstan-ignore property.notFound | |
| 322 | + } | |
| 323 | + | |
| 324 | + /** | |
| 325 | + * Provides the IDs of the capabilities of object, those of the user's roles and those of the object's groups, including from ancestor groups. | |
| 326 | + * | |
| 327 | + * @return int[] | |
| 328 | + */ | |
| 329 | + public function get_capability_ids_deep() { | |
| 330 | + return $this->capability_ids_deep; // @phpstan-ignore property.notFound | |
| 331 | + } | |
| 332 | + | |
| 333 | + /** | |
| 334 | + * Provides the groups this object relates to. | |
| 335 | + * | |
| 336 | + * @return Groups_Group[] | |
| 337 | + */ | |
| 338 | + public function get_groups() { | |
| 339 | + return $this->groups; // @phpstan-ignore property.notFound | |
| 340 | + } | |
| 341 | + | |
| 342 | + /** | |
| 343 | + * Provides the groups that this object relates to and includes their ancestors. | |
| 344 | + * | |
| 345 | + * @return Groups_Group[] | |
| 346 | + */ | |
| 347 | + public function get_groups_deep() { | |
| 348 | + return $this->groups_deep; // @phpstan-ignore property.notFound | |
| 349 | + } | |
| 350 | + | |
| 351 | + /** | |
| 352 | + * Provides the IDs of the groups that this object relates to. | |
| 353 | + * | |
| 354 | + * @return int[] | |
| 355 | + */ | |
| 356 | + public function get_group_ids() { | |
| 357 | + return $this->group_ids; // @phpstan-ignore property.notFound | |
| 358 | + } | |
| 359 | + | |
| 360 | + /** | |
| 361 | + * Provide the IDs of the groups that this object relates to and from ancestor groups. | |
| 362 | + */ | |
| 363 | + public function get_group_ids_deep() { | |
| 364 | + return $this->group_ids_deep; // @phpstan-ignore property.notFound | |
| 365 | + } | |
| 366 | + | |
| 367 | + /** | |
| 117 | 368 | * Retrieve a user property. |
| 369 | + * | |
| 118 | 370 | * Must be "capabilities", "groups" or a property of the WP_User class. |
| 371 | + * | |
| 119 | 372 | * @param string $name property's name |
| 120 | 373 | */ |
| 121 | 374 | public function __get( $name ) { |
| 122 | 375 | |
| @@ -129,14 +382,14 @@ | ||
| 129 | 382 | |
| 130 | 383 | case 'capability_ids' : |
| 131 | 384 | $cached = Groups_Cache::get( self::CAPABILITY_IDS_BASE . $this->user->ID, self::CACHE_GROUP ); |
| 132 | 385 | if ( $cached !== null ) { |
| 133 | - $result = $cached->value; | |
| 386 | + $result = $cached->get_value(); | |
| 134 | 387 | unset( $cached ); |
| 135 | 388 | } else { |
| 136 | - $user_capability_table = _groups_get_tablename( "user_capability" ); | |
| 389 | + $user_capability_table = _groups_get_tablename( 'user_capability' ); | |
| 137 | 390 | $rows = $wpdb->get_results( $wpdb->prepare( |
| 138 | - "SELECT capability_id FROM $user_capability_table WHERE user_id = %d", | |
| 391 | + "SELECT capability_id FROM $user_capability_table WHERE user_id = %d", // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared | |
| 139 | 392 | Groups_Utility::id( $this->user->ID ) |
| 140 | 393 | ) ); |
| 141 | 394 | if ( $rows ) { |
| 142 | 395 | $result = array(); |
| @@ -151,9 +404,9 @@ | ||
| 151 | 404 | case 'capability_ids_deep' : |
| 152 | 405 | if ( $this->user !== null ) { |
| 153 | 406 | $cached = Groups_Cache::get( self::CAPABILITY_IDS . $this->user->ID, self::CACHE_GROUP ); |
| 154 | 407 | if ( $cached !== null ) { |
| 155 | - $capability_ids = $cached->value; | |
| 408 | + $capability_ids = $cached->get_value(); | |
| 156 | 409 | unset( $cached ); |
| 157 | 410 | } else { |
| 158 | 411 | $this->init_cache( $capability_ids ); |
| 159 | 412 | } |
| @@ -163,19 +416,19 @@ | ||
| 163 | 416 | |
| 164 | 417 | case 'group_ids' : |
| 165 | 418 | $cached = Groups_Cache::get( self::GROUP_IDS_BASE . $this->user->ID, self::CACHE_GROUP ); |
| 166 | 419 | if ( $cached !== null ) { |
| 167 | - $result = $cached->value; | |
| 420 | + $result = $cached->get_value(); | |
| 168 | 421 | unset( $cached ); |
| 169 | 422 | } else { |
| 170 | - $user_group_table = _groups_get_tablename( "user_group" ); | |
| 423 | + $user_group_table = _groups_get_tablename( 'user_group' ); | |
| 171 | 424 | $rows = $wpdb->get_results( $wpdb->prepare( |
| 172 | - "SELECT group_id FROM $user_group_table WHERE user_id = %d", | |
| 425 | + "SELECT group_id FROM $user_group_table WHERE user_id = %d", // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared | |
| 173 | 426 | Groups_Utility::id( $this->user->ID ) |
| 174 | 427 | ) ); |
| 175 | 428 | if ( $rows ) { |
| 176 | 429 | $result = array(); |
| 177 | - foreach( $rows as $row ) { | |
| 430 | + foreach ( $rows as $row ) { | |
| 178 | 431 | $result[] = $row->group_id; |
| 179 | 432 | } |
| 180 | 433 | } |
| 181 | 434 | Groups_Cache::set( self::GROUP_IDS_BASE . $this->user->ID, $result, self::CACHE_GROUP ); |
| @@ -185,9 +438,9 @@ | ||
| 185 | 438 | case 'group_ids_deep' : |
| 186 | 439 | if ( $this->user !== null ) { |
| 187 | 440 | $cached = Groups_Cache::get( self::GROUP_IDS . $this->user->ID, self::CACHE_GROUP ); |
| 188 | 441 | if ( $cached !== null ) { |
| 189 | - $group_ids = $cached->value; | |
| 442 | + $group_ids = $cached->get_value(); | |
| 190 | 443 | unset( $cached ); |
| 191 | 444 | } else { |
| 192 | 445 | $this->init_cache( $capability_ids, $capabilities, $group_ids ); |
| 193 | 446 | } |
| @@ -197,14 +450,14 @@ | ||
| 197 | 450 | |
| 198 | 451 | case 'capabilities' : |
| 199 | 452 | $cached = Groups_Cache::get( self::CAPABILITIES_BASE . $this->user->ID, self::CACHE_GROUP ); |
| 200 | 453 | if ( $cached !== null ) { |
| 201 | - $result = $cached->value; | |
| 454 | + $result = $cached->get_value(); | |
| 202 | 455 | unset( $cached ); |
| 203 | 456 | } else { |
| 204 | - $user_capability_table = _groups_get_tablename( "user_capability" ); | |
| 457 | + $user_capability_table = _groups_get_tablename( 'user_capability' ); | |
| 205 | 458 | $rows = $wpdb->get_results( $wpdb->prepare( |
| 206 | - "SELECT capability_id FROM $user_capability_table WHERE user_id = %d", | |
| 459 | + "SELECT capability_id FROM $user_capability_table WHERE user_id = %d", // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared | |
| 207 | 460 | Groups_Utility::id( $this->user->ID ) |
| 208 | 461 | ) ); |
| 209 | 462 | if ( $rows ) { |
| 210 | 463 | $result = array(); |
| @@ -217,16 +470,31 @@ | ||
| 217 | 470 | break; |
| 218 | 471 | |
| 219 | 472 | case 'capabilities_deep' : |
| 220 | 473 | if ( $this->user !== null ) { |
| 221 | - $cached = Groups_Cache::get( self::CAPABILITIES . $this->user->ID, self::CACHE_GROUP ); | |
| 474 | + // @since 3.6. provide cached objects | |
| 475 | + $cached = Groups_Cache::get( self::CAPABILITIES_DEEP . $this->user->ID, self::CACHE_GROUP ); | |
| 222 | 476 | if ( $cached !== null ) { |
| 223 | - $capabilities = $cached->value; | |
| 477 | + $result = $cached->get_value(); | |
| 224 | 478 | unset( $cached ); |
| 225 | 479 | } else { |
| 226 | - $this->init_cache( $capability_ids, $capabilities ); | |
| 480 | + $cached = Groups_Cache::get( self::CAPABILITIES . $this->user->ID, self::CACHE_GROUP ); | |
| 481 | + if ( $cached !== null ) { | |
| 482 | + $capabilities = $cached->get_value(); | |
| 483 | + unset( $cached ); | |
| 484 | + } else { | |
| 485 | + $this->init_cache( $capability_ids, $capabilities ); | |
| 486 | + } | |
| 487 | + // @since 3.6.0 provide expected return type Groups_Capability[] | |
| 488 | + $result = array(); | |
| 489 | + foreach ( $capabilities as $capability ) { | |
| 490 | + $capobj = Groups_Capability::read_by_capability( $capability ); | |
| 491 | + if ( $capobj ) { | |
| 492 | + $result[] = new Groups_Capability( $capobj->capability_id ); | |
| 493 | + } | |
| 494 | + } | |
| 495 | + Groups_Cache::set( self::CAPABILITIES_DEEP . $this->user->ID, $result, self::CACHE_GROUP ); | |
| 227 | 496 | } |
| 228 | - $result = $capabilities; | |
| 229 | 497 | } |
| 230 | 498 | break; |
| 231 | 499 | |
| 232 | 500 | case 'groups' : |
| @@ -231,19 +499,19 @@ | ||
| 231 | 499 | |
| 232 | 500 | case 'groups' : |
| 233 | 501 | $cached = Groups_Cache::get( self::GROUPS_BASE . $this->user->ID, self::CACHE_GROUP ); |
| 234 | 502 | if ( $cached !== null ) { |
| 235 | - $result = $cached->value; | |
| 503 | + $result = $cached->get_value(); | |
| 236 | 504 | unset( $cached ); |
| 237 | 505 | } else { |
| 238 | - $user_group_table = _groups_get_tablename( "user_group" ); | |
| 506 | + $user_group_table = _groups_get_tablename( 'user_group' ); | |
| 239 | 507 | $rows = $wpdb->get_results( $wpdb->prepare( |
| 240 | - "SELECT group_id FROM $user_group_table WHERE user_id = %d", | |
| 508 | + "SELECT group_id FROM $user_group_table WHERE user_id = %d", // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared | |
| 241 | 509 | Groups_Utility::id( $this->user->ID ) |
| 242 | 510 | ) ); |
| 243 | 511 | if ( $rows ) { |
| 244 | 512 | $result = array(); |
| 245 | - foreach( $rows as $row ) { | |
| 513 | + foreach ( $rows as $row ) { | |
| 246 | 514 | $result[] = new Groups_Group( $row->group_id ); |
| 247 | 515 | } |
| 248 | 516 | } |
| 249 | 517 | Groups_Cache::set( self::GROUPS_BASE . $this->user->ID, $result, self::CACHE_GROUP ); |
| @@ -252,13 +520,13 @@ | ||
| 252 | 520 | |
| 253 | 521 | case 'groups_deep' : |
| 254 | 522 | $cached = Groups_Cache::get( self::GROUPS . $this->user->ID, self::CACHE_GROUP ); |
| 255 | 523 | if ( $cached !== null ) { |
| 256 | - $result = $cached->value; | |
| 524 | + $result = $cached->get_value(); | |
| 257 | 525 | unset( $cached ); |
| 258 | 526 | } else { |
| 259 | 527 | $result = array(); |
| 260 | - foreach( $this->group_ids_deep as $group_id ) { | |
| 528 | + foreach ( $this->group_ids_deep as $group_id ) { // @phpstan-ignore property.notFound | |
| 261 | 529 | $result[] = new Groups_Group( $group_id ); |
| 262 | 530 | } |
| 263 | 531 | Groups_Cache::set( self::GROUPS . $this->user->ID, $result, self::CACHE_GROUP ); |
| 264 | 532 | } |
| @@ -271,21 +539,23 @@ | ||
| 271 | 539 | return $result; |
| 272 | 540 | } |
| 273 | 541 | |
| 274 | 542 | /** |
| 275 | - * (non-PHPdoc) | |
| 543 | + * Use only to check for primitive capabilities. | |
| 544 | + * | |
| 545 | + * Use Groups_User::user_can() instead with parameters supplied. | |
| 546 | + * | |
| 547 | + * @param mixed $object not supported | |
| 548 | + * @param array $args not supported | |
| 549 | + * | |
| 276 | 550 | * @see I_Capable::can() |
| 277 | 551 | */ |
| 278 | - public function can( $capability ) { | |
| 552 | + public function can( $capability, $object = null, $args = null ) { | |
| 279 | 553 | |
| 280 | - global $wpdb; | |
| 281 | 554 | $result = false; |
| 282 | 555 | |
| 283 | 556 | if ( $this->user !== null ) { |
| 284 | - if ( | |
| 285 | - get_option( GROUPS_ADMINISTRATOR_ACCESS_OVERRIDE, GROUPS_ADMINISTRATOR_ACCESS_OVERRIDE_DEFAULT ) && | |
| 286 | - user_can( $this->user->ID, 'administrator' ) // just using $this->user would raise a warning on 3.2.1 | |
| 287 | - ) { | |
| 557 | + if ( _groups_admin_override( $this->user->ID ) ) { | |
| 288 | 558 | $result = true; |
| 289 | 559 | } else { |
| 290 | 560 | // determine capability id |
| 291 | 561 | $capability_id = null; |
| @@ -292,9 +562,9 @@ | ||
| 292 | 562 | if ( is_numeric( $capability ) ) { |
| 293 | 563 | $capability_id = Groups_Utility::id( $capability ); |
| 294 | 564 | $cached = Groups_Cache::get( self::CAPABILITY_IDS . $this->user->ID, self::CACHE_GROUP ); |
| 295 | 565 | if ( $cached !== null ) { |
| 296 | - $capability_ids = $cached->value; | |
| 566 | + $capability_ids = $cached->get_value(); | |
| 297 | 567 | unset( $cached ); |
| 298 | 568 | } else { |
| 299 | 569 | $this->init_cache( $capability_ids ); |
| 300 | 570 | } |
| @@ -301,9 +571,9 @@ | ||
| 301 | 571 | $result = in_array( $capability_id, $capability_ids ); |
| 302 | 572 | } else if ( is_string( $capability ) ) { |
| 303 | 573 | $cached = Groups_Cache::get( self::CAPABILITIES . $this->user->ID, self::CACHE_GROUP ); |
| 304 | 574 | if ( $cached !== null ) { |
| 305 | - $capabilities = $cached->value; | |
| 575 | + $capabilities = $cached->get_value(); | |
| 306 | 576 | unset( $cached ); |
| 307 | 577 | } else { |
| 308 | 578 | $this->init_cache( $capability_ids, $capabilities ); |
| 309 | 579 | } |
| @@ -310,16 +580,31 @@ | ||
| 310 | 580 | $result = in_array( $capability, $capabilities ); |
| 311 | 581 | } |
| 312 | 582 | } |
| 313 | 583 | } |
| 314 | - $result = apply_filters_ref_array( "groups_user_can", array( $result, &$this, $capability ) ); | |
| 584 | + /** | |
| 585 | + * Filter whether the user has the capability. | |
| 586 | + * | |
| 587 | + * @since 3.0.0 $object | |
| 588 | + * @since 3.0.0 $args | |
| 589 | + * | |
| 590 | + * @param boolean $result | |
| 591 | + * @param Groups_User $group_user | |
| 592 | + * @param string $capability | |
| 593 | + * @param mixed $object | |
| 594 | + * @param mixed $args | |
| 595 | + * | |
| 596 | + * @return boolean | |
| 597 | + */ | |
| 598 | + $result = apply_filters_ref_array( 'groups_user_can', array( $result, &$this, $capability, $object, $args ) ); | |
| 315 | 599 | return $result; |
| 316 | 600 | } |
| 317 | 601 | |
| 318 | 602 | /** |
| 319 | 603 | * Returns true if the user belongs to the group. |
| 320 | - * | |
| 604 | + * | |
| 321 | 605 | * @param int $group_id |
| 606 | + * | |
| 322 | 607 | * @return boolean |
| 323 | 608 | */ |
| 324 | 609 | public function is_member( $group_id ) { |
| 325 | 610 | $result = false; |
| @@ -332,8 +617,23 @@ | ||
| 332 | 617 | $result = $user_group !== false; |
| 333 | 618 | unset( $user_group ); |
| 334 | 619 | } |
| 335 | 620 | } |
| 621 | + /** | |
| 622 | + * Allow to modify the result of whether the user belongs to a given group. | |
| 623 | + * | |
| 624 | + * @since 2.20.0 | |
| 625 | + * | |
| 626 | + * @param boolean $result whether the user belongs to the group | |
| 627 | + * @param Groups_User $object this object | |
| 628 | + * @param int $group_id the group ID | |
| 629 | + * | |
| 630 | + * @return boolean $filtered_result | |
| 631 | + */ | |
| 632 | + $filtered_result = apply_filters( 'groups_user_is_member', $result, $this, $group_id ); | |
| 633 | + if ( is_bool( $result ) ) { | |
| 634 | + $result = $filtered_result; | |
| 635 | + } | |
| 336 | 636 | return $result; |
| 337 | 637 | } |
| 338 | 638 | |
| 339 | 639 | /** |
| @@ -339,9 +639,9 @@ | ||
| 339 | 639 | /** |
| 340 | 640 | * Builds the cache entries for user groups and capabilities if needed. |
| 341 | 641 | * The cache entries are built only if they do not already exist. |
| 342 | 642 | * If you want them rebuilt, delete them before calling. |
| 343 | - * | |
| 643 | + * | |
| 344 | 644 | * @param array $capability_ids carries the capability ids for the user on return, but only if cache entries have been built; will provide an empty array by default |
| 345 | 645 | * @param array $capabilities carries the capabilities for the user on return, but only if cache entries have been built; will provide an empty array by default |
| 346 | 646 | * @param array $group_ids carries the group ids for the user on return, but only if cache entries have been built; will provide an empty array by default |
| 347 | 647 | */ |
| @@ -352,16 +652,19 @@ | ||
| 352 | 652 | $capabilities = array(); |
| 353 | 653 | $capability_ids = array(); |
| 354 | 654 | $group_ids = array(); |
| 355 | 655 | |
| 356 | - if ( ( $this->user !== null ) && ( Groups_Cache::get( self::GROUP_IDS . $this->user->ID, self::CACHE_GROUP ) === null ) ) { | |
| 357 | - $group_table = _groups_get_tablename( "group" ); | |
| 358 | - $capability_table = _groups_get_tablename( "capability" ); | |
| 359 | - $group_capability_table = _groups_get_tablename( "group_capability" ); | |
| 360 | - $user_group_table = _groups_get_tablename( "user_group" ); | |
| 361 | - $user_capability_table = _groups_get_tablename( "user_capability" ); | |
| 656 | + if ( | |
| 657 | + ( $this->user !== null ) && | |
| 658 | + ( Groups_Cache::get( self::GROUP_IDS . $this->user->ID, self::CACHE_GROUP ) === null ) | |
| 659 | + ) { | |
| 660 | + $group_table = _groups_get_tablename( 'group' ); | |
| 661 | + $capability_table = _groups_get_tablename( 'capability' ); | |
| 662 | + $group_capability_table = _groups_get_tablename( 'group_capability' ); | |
| 663 | + $user_group_table = _groups_get_tablename( 'user_group' ); | |
| 664 | + $user_capability_table = _groups_get_tablename( 'user_capability' ); | |
| 362 | 665 | |
| 363 | - $limit = $wpdb->get_var( "SELECT COUNT(*) FROM $group_table" ); | |
| 666 | + $limit = $wpdb->get_var( "SELECT COUNT(*) FROM $group_table" ); // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared | |
| 364 | 667 | if ( $limit === null ) { |
| 365 | 668 | $limit = 1; |
| 366 | 669 | } |
| 367 | 670 | |
| @@ -367,19 +670,19 @@ | ||
| 367 | 670 | |
| 368 | 671 | // note that limits by blog_id for multisite are |
| 369 | 672 | // enforced when a user is added to a blog |
| 370 | 673 | $user_groups = $wpdb->get_results( $wpdb->prepare( |
| 371 | - "SELECT group_id FROM $user_group_table WHERE user_id = %d", | |
| 674 | + "SELECT group_id FROM $user_group_table WHERE user_id = %d", // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared | |
| 372 | 675 | Groups_Utility::id( $this->user->ID ) |
| 373 | 676 | ) ); |
| 374 | 677 | // get all capabilities directly assigned (those granted through |
| 375 | 678 | // groups are added below |
| 376 | 679 | $user_capabilities = $wpdb->get_results( $wpdb->prepare( |
| 377 | - "SELECT c.capability_id, c.capability FROM $user_capability_table uc LEFT JOIN $capability_table c ON c.capability_id = uc.capability_id WHERE user_id = %d", | |
| 680 | + "SELECT c.capability_id, c.capability FROM $user_capability_table uc LEFT JOIN $capability_table c ON c.capability_id = uc.capability_id WHERE user_id = %d", // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared | |
| 378 | 681 | Groups_Utility::id( $this->user->ID ) |
| 379 | 682 | ) ); |
| 380 | 683 | if ( $user_capabilities ) { |
| 381 | - foreach( $user_capabilities as $user_capability ) { | |
| 684 | + foreach ( $user_capabilities as $user_capability ) { | |
| 382 | 685 | $capabilities[] = $user_capability->capability; |
| 383 | 686 | $capability_ids[] = $user_capability->capability_id; |
| 384 | 687 | } |
| 385 | 688 | } |
| @@ -388,11 +691,11 @@ | ||
| 388 | 691 | // Get all capabilities from the WP_User object. |
| 389 | 692 | $role_caps = $this->user->get_role_caps(); |
| 390 | 693 | if ( !empty( $role_caps ) && is_array( $role_caps ) ) { |
| 391 | 694 | $caps = array(); |
| 392 | - foreach( $role_caps as $role_cap => $has ) { | |
| 695 | + foreach ( $role_caps as $role_cap => $has ) { | |
| 393 | 696 | if ( $has && !in_array( $role_cap, $capabilities ) ) { |
| 394 | - $caps[] = "'" . $role_cap . "'"; | |
| 697 | + $caps[] = $role_cap; | |
| 395 | 698 | } |
| 396 | 699 | } |
| 397 | 700 | if ( !empty( $caps ) ) { |
| 398 | 701 | // Retrieve the capabilities and only add those that are |
| @@ -397,10 +700,10 @@ | ||
| 397 | 700 | if ( !empty( $caps ) ) { |
| 398 | 701 | // Retrieve the capabilities and only add those that are |
| 399 | 702 | // recognized. Note that this also effectively filters out |
| 400 | 703 | // all roles and that this is desired. |
| 401 | - if ( $role_capabilities = $wpdb->get_results( "SELECT capability_id, capability FROM $capability_table c WHERE capability IN (" . implode( ',', $caps ) . ")" ) ) { | |
| 402 | - foreach( $role_capabilities as $role_capability ) { | |
| 704 | + if ( $role_capabilities = $wpdb->get_results( "SELECT capability_id, capability FROM $capability_table c WHERE capability IN ('" . implode( "','", esc_sql( $caps ) ) . "')" ) ) { // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared | |
| 705 | + foreach ( $role_capabilities as $role_capability ) { | |
| 403 | 706 | $capabilities[] = $role_capability->capability; |
| 404 | 707 | $capability_ids[] = $role_capability->capability_id; |
| 405 | 708 | } |
| 406 | 709 | } |
| @@ -410,9 +713,9 @@ | ||
| 410 | 713 | |
| 411 | 714 | // Get all groups the user belongs to directly or through |
| 412 | 715 | // inheritance along with their capabilities. |
| 413 | 716 | if ( $user_groups ) { |
| 414 | - foreach( $user_groups as $user_group ) { | |
| 717 | + foreach ( $user_groups as $user_group ) { | |
| 415 | 718 | $group_ids[] = Groups_Utility::id( $user_group->group_id ); |
| 416 | 719 | } |
| 417 | 720 | if ( count( $group_ids ) > 0 ) { |
| 418 | 721 | $iterations = 0; |
| @@ -419,14 +722,14 @@ | ||
| 419 | 722 | $old_group_ids_count = 0; |
| 420 | 723 | while( ( $iterations < $limit ) && ( count( $group_ids ) !== $old_group_ids_count ) ) { |
| 421 | 724 | $iterations++; |
| 422 | 725 | $old_group_ids_count = count( $group_ids ); |
| 423 | - $id_list = implode( ",", $group_ids ); | |
| 726 | + $id_list = implode( ',', $group_ids ); | |
| 424 | 727 | $parent_group_ids = $wpdb->get_results( |
| 425 | - "SELECT parent_id FROM $group_table WHERE parent_id IS NOT NULL AND group_id IN ($id_list)" | |
| 728 | + "SELECT parent_id FROM $group_table WHERE parent_id IS NOT NULL AND group_id IN ($id_list)" // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared | |
| 426 | 729 | ); |
| 427 | 730 | if ( $parent_group_ids ) { |
| 428 | - foreach( $parent_group_ids as $parent_group_id ) { | |
| 731 | + foreach ( $parent_group_ids as $parent_group_id ) { | |
| 429 | 732 | $parent_group_id = Groups_Utility::id( $parent_group_id->parent_id ); |
| 430 | 733 | if ( !in_array( $parent_group_id, $group_ids ) ) { |
| 431 | 734 | $group_ids[] = $parent_group_id; |
| 432 | 735 | } |
| @@ -432,11 +735,11 @@ | ||
| 432 | 735 | } |
| 433 | 736 | } |
| 434 | 737 | } |
| 435 | 738 | } |
| 436 | - $id_list = implode( ",", $group_ids ); | |
| 739 | + $id_list = implode( ',', $group_ids ); | |
| 437 | 740 | $rows = $wpdb->get_results( |
| 438 | - "SELECT $group_capability_table.capability_id, $capability_table.capability FROM $group_capability_table LEFT JOIN $capability_table ON $group_capability_table.capability_id = $capability_table.capability_id WHERE group_id IN ($id_list)" | |
| 741 | + "SELECT $group_capability_table.capability_id, $capability_table.capability FROM $group_capability_table LEFT JOIN $capability_table ON $group_capability_table.capability_id = $capability_table.capability_id WHERE group_id IN ($id_list)" // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared | |
| 439 | 742 | ); |
| 440 | 743 | if ( count( $rows ) > 0 ) { |
| 441 | 744 | foreach ( $rows as $row ) { |
| 442 | 745 | if ( !in_array( $row->capability_id, $capability_ids ) ) { |