PluginProbe
Groups – Memberships and Access Control / 4.7.1
Groups – Memberships and Access Control v4.7.1
4.7.1 4.7.0 4.6.0 4.5.0 4.4.0 4.3.0 trunk 1.0.0-beta-1 1.0.0-beta-2 1.0.0-beta-3 1.0.0-beta-3b 1.0.0-beta-3c 1.0.0-beta-3d 1.1.4 1.1.5 1.10.0 1.10.1 1.10.2 1.10.3 1.11.0 1.11.1 1.11.2 1.11.3 1.12.0 1.13.0 All 131 releases
← All changes | lib/core/class-groups-user.php +378 -75 1.10.14.7.1 View file →
@@ -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 ) ) {