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
groups / lib / core / class-groups-user.php

class-groups-user.php in Groups – Memberships and Access Control 4.7.1, at lib/core/class-groups-user.php

762 lines 25.0 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * class-groups-user.php
4 *
5 * Copyright (c) "kento" Karim Rahimpur www.itthinx.com
6 *
7 * This code is released under the GNU General Public License.
8 * See COPYRIGHT.txt and LICENSE.txt.
9 *
10 * This code is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * This header and all notices must be kept intact.
16 *
17 * @author Karim Rahimpur
18 * @package groups
19 * @since groups 1.0.0
20 */
21
22 if ( !defined( 'ABSPATH' ) ) {
23 exit;
24 }
25
26 // phpcs:disable PluginCheck.Security.DirectDB.UnescapedDBParameter, WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
27
28 require_once GROUPS_CORE_LIB . '/interface-i-capable.php';
29 require_once GROUPS_CORE_LIB . '/class-groups-capability.php';
30
31 /**
32 * User OPM.
33 */
34 class Groups_User implements I_Capable {
35
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 */
66 const CAPABILITY_IDS_BASE = 'capability_ids_base';
67
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 /**
89 * User object.
90 *
91 * @access private - Use $this->get_user() instead as this property will be made private in a future release of Groups
92 *
93 * @var WP_User
94 */
95 public $user = null;
96
97 /**
98 * Hook cache clearers to actions that can modify the capabilities.
99 */
100 public static function init() {
101 add_action( 'groups_created_user_group', array( __CLASS__, 'clear_cache' ) );
102 add_action( 'groups_updated_user_group', array( __CLASS__, 'clear_cache' ) );
103 add_action( 'groups_deleted_user_group', array( __CLASS__, 'clear_cache' ) );
104 add_action( 'groups_created_user_capability', array( __CLASS__, 'clear_cache' ) );
105 add_action( 'groups_updated_user_capability', array( __CLASS__, 'clear_cache' ) );
106 add_action( 'groups_deleted_user_capability', array( __CLASS__, 'clear_cache' ) );
107 add_action( 'groups_created_group_capability', array( __CLASS__, 'clear_cache_for_group' ) );
108 add_action( 'groups_updated_group_capability', array( __CLASS__, 'clear_cache_for_group' ) );
109 add_action( 'groups_deleted_group_capability', array( __CLASS__, 'clear_cache_for_group' ) );
110 }
111
112 /**
113 * Clear cache objects for the user.
114 *
115 * @param int $user_id
116 */
117 public static function clear_cache( $user_id ) {
118 // be lazy, clear the entries so they are rebuilt when requested
119 Groups_Cache::delete( self::CAPABILITIES . $user_id, self::CACHE_GROUP );
120 Groups_Cache::delete( self::CAPABILITIES_BASE . $user_id, self::CACHE_GROUP );
121 Groups_Cache::delete( self::CAPABILITIES_DEEP . $user_id, self::CACHE_GROUP );
122 Groups_Cache::delete( self::CAPABILITY_IDS . $user_id, self::CACHE_GROUP );
123 Groups_Cache::delete( self::CAPABILITY_IDS_BASE . $user_id, self::CACHE_GROUP );
124 Groups_Cache::delete( self::GROUP_IDS . $user_id, self::CACHE_GROUP );
125 Groups_Cache::delete( self::GROUP_IDS_BASE . $user_id, self::CACHE_GROUP );
126 Groups_Cache::delete( self::GROUPS . $user_id, self::CACHE_GROUP );
127 Groups_Cache::delete( self::GROUPS_BASE . $user_id, self::CACHE_GROUP );
128 }
129
130 /**
131 * Clear cache objects for all users in the group.
132 *
133 * @param int $group_id
134 */
135 public static function clear_cache_for_group( $group_id ) {
136 global $wpdb;
137 if ( $group = Groups_Group::read( $group_id ) ) {
138 // not using $group->users, as we don't need a lot of user objects created here
139 $user_group_table = _groups_get_tablename( 'user_group' );
140 $users = $wpdb->get_results( $wpdb->prepare(
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
142 Groups_Utility::id( $group_id )
143 ) );
144 if ( $users ) {
145 foreach ( $users as $user ) {
146 self::clear_cache( $user->ID );
147 }
148 }
149 }
150 }
151
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 /**
247 * Create, if $user_id = 0 an anonymous user is assumed.
248 *
249 * @param int $user_id
250 */
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 }
261 }
262 }
263
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 /**
368 * Retrieve a user property.
369 *
370 * Must be "capabilities", "groups" or a property of the WP_User class.
371 *
372 * @param string $name property's name
373 */
374 public function __get( $name ) {
375
376 global $wpdb;
377 $result = null;
378
379 if ( $this->user !== null ) {
380
381 switch ( $name ) {
382
383 case 'capability_ids' :
384 $cached = Groups_Cache::get( self::CAPABILITY_IDS_BASE . $this->user->ID, self::CACHE_GROUP );
385 if ( $cached !== null ) {
386 $result = $cached->get_value();
387 unset( $cached );
388 } else {
389 $user_capability_table = _groups_get_tablename( 'user_capability' );
390 $rows = $wpdb->get_results( $wpdb->prepare(
391 "SELECT capability_id FROM $user_capability_table WHERE user_id = %d", // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
392 Groups_Utility::id( $this->user->ID )
393 ) );
394 if ( $rows ) {
395 $result = array();
396 foreach ( $rows as $row ) {
397 $result[] = $row->capability_id;
398 }
399 }
400 Groups_Cache::set( self::CAPABILITY_IDS_BASE . $this->user->ID, $result, self::CACHE_GROUP );
401 }
402 break;
403
404 case 'capability_ids_deep' :
405 if ( $this->user !== null ) {
406 $cached = Groups_Cache::get( self::CAPABILITY_IDS . $this->user->ID, self::CACHE_GROUP );
407 if ( $cached !== null ) {
408 $capability_ids = $cached->get_value();
409 unset( $cached );
410 } else {
411 $this->init_cache( $capability_ids );
412 }
413 $result = $capability_ids;
414 }
415 break;
416
417 case 'group_ids' :
418 $cached = Groups_Cache::get( self::GROUP_IDS_BASE . $this->user->ID, self::CACHE_GROUP );
419 if ( $cached !== null ) {
420 $result = $cached->get_value();
421 unset( $cached );
422 } else {
423 $user_group_table = _groups_get_tablename( 'user_group' );
424 $rows = $wpdb->get_results( $wpdb->prepare(
425 "SELECT group_id FROM $user_group_table WHERE user_id = %d", // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
426 Groups_Utility::id( $this->user->ID )
427 ) );
428 if ( $rows ) {
429 $result = array();
430 foreach ( $rows as $row ) {
431 $result[] = $row->group_id;
432 }
433 }
434 Groups_Cache::set( self::GROUP_IDS_BASE . $this->user->ID, $result, self::CACHE_GROUP );
435 }
436 break;
437
438 case 'group_ids_deep' :
439 if ( $this->user !== null ) {
440 $cached = Groups_Cache::get( self::GROUP_IDS . $this->user->ID, self::CACHE_GROUP );
441 if ( $cached !== null ) {
442 $group_ids = $cached->get_value();
443 unset( $cached );
444 } else {
445 $this->init_cache( $capability_ids, $capabilities, $group_ids );
446 }
447 $result = $group_ids;
448 }
449 break;
450
451 case 'capabilities' :
452 $cached = Groups_Cache::get( self::CAPABILITIES_BASE . $this->user->ID, self::CACHE_GROUP );
453 if ( $cached !== null ) {
454 $result = $cached->get_value();
455 unset( $cached );
456 } else {
457 $user_capability_table = _groups_get_tablename( 'user_capability' );
458 $rows = $wpdb->get_results( $wpdb->prepare(
459 "SELECT capability_id FROM $user_capability_table WHERE user_id = %d", // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
460 Groups_Utility::id( $this->user->ID )
461 ) );
462 if ( $rows ) {
463 $result = array();
464 foreach ( $rows as $row ) {
465 $result[] = new Groups_Capability( $row->capability_id );
466 }
467 }
468 Groups_Cache::set( self::CAPABILITIES_BASE . $this->user->ID, $result, self::CACHE_GROUP );
469 }
470 break;
471
472 case 'capabilities_deep' :
473 if ( $this->user !== null ) {
474 // @since 3.6. provide cached objects
475 $cached = Groups_Cache::get( self::CAPABILITIES_DEEP . $this->user->ID, self::CACHE_GROUP );
476 if ( $cached !== null ) {
477 $result = $cached->get_value();
478 unset( $cached );
479 } else {
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 );
496 }
497 }
498 break;
499
500 case 'groups' :
501 $cached = Groups_Cache::get( self::GROUPS_BASE . $this->user->ID, self::CACHE_GROUP );
502 if ( $cached !== null ) {
503 $result = $cached->get_value();
504 unset( $cached );
505 } else {
506 $user_group_table = _groups_get_tablename( 'user_group' );
507 $rows = $wpdb->get_results( $wpdb->prepare(
508 "SELECT group_id FROM $user_group_table WHERE user_id = %d", // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
509 Groups_Utility::id( $this->user->ID )
510 ) );
511 if ( $rows ) {
512 $result = array();
513 foreach ( $rows as $row ) {
514 $result[] = new Groups_Group( $row->group_id );
515 }
516 }
517 Groups_Cache::set( self::GROUPS_BASE . $this->user->ID, $result, self::CACHE_GROUP );
518 }
519 break;
520
521 case 'groups_deep' :
522 $cached = Groups_Cache::get( self::GROUPS . $this->user->ID, self::CACHE_GROUP );
523 if ( $cached !== null ) {
524 $result = $cached->get_value();
525 unset( $cached );
526 } else {
527 $result = array();
528 foreach ( $this->group_ids_deep as $group_id ) { // @phpstan-ignore property.notFound
529 $result[] = new Groups_Group( $group_id );
530 }
531 Groups_Cache::set( self::GROUPS . $this->user->ID, $result, self::CACHE_GROUP );
532 }
533 break;
534
535 default:
536 $result = $this->user->$name;
537 }
538 }
539 return $result;
540 }
541
542 /**
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 *
550 * @see I_Capable::can()
551 */
552 public function can( $capability, $object = null, $args = null ) {
553
554 $result = false;
555
556 if ( $this->user !== null ) {
557 if ( _groups_admin_override( $this->user->ID ) ) {
558 $result = true;
559 } else {
560 // determine capability id
561 $capability_id = null;
562 if ( is_numeric( $capability ) ) {
563 $capability_id = Groups_Utility::id( $capability );
564 $cached = Groups_Cache::get( self::CAPABILITY_IDS . $this->user->ID, self::CACHE_GROUP );
565 if ( $cached !== null ) {
566 $capability_ids = $cached->get_value();
567 unset( $cached );
568 } else {
569 $this->init_cache( $capability_ids );
570 }
571 $result = in_array( $capability_id, $capability_ids );
572 } else if ( is_string( $capability ) ) {
573 $cached = Groups_Cache::get( self::CAPABILITIES . $this->user->ID, self::CACHE_GROUP );
574 if ( $cached !== null ) {
575 $capabilities = $cached->get_value();
576 unset( $cached );
577 } else {
578 $this->init_cache( $capability_ids, $capabilities );
579 }
580 $result = in_array( $capability, $capabilities );
581 }
582 }
583 }
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 ) );
599 return $result;
600 }
601
602 /**
603 * Returns true if the user belongs to the group.
604 *
605 * @param int $group_id
606 *
607 * @return boolean
608 */
609 public function is_member( $group_id ) {
610 $result = false;
611 if ( $this->user !== null ) {
612 if ( isset( $this->user->ID ) ) {
613 $user_group = Groups_User_Group::read(
614 Groups_Utility::id( $this->user->ID ),
615 Groups_Utility::id( $group_id )
616 );
617 $result = $user_group !== false;
618 unset( $user_group );
619 }
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 }
636 return $result;
637 }
638
639 /**
640 * Builds the cache entries for user groups and capabilities if needed.
641 * The cache entries are built only if they do not already exist.
642 * If you want them rebuilt, delete them before calling.
643 *
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
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
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
647 */
648 private function init_cache( &$capability_ids = null, &$capabilities = null, &$group_ids = null ) {
649
650 global $wpdb;
651
652 $capabilities = array();
653 $capability_ids = array();
654 $group_ids = array();
655
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' );
665
666 $limit = $wpdb->get_var( "SELECT COUNT(*) FROM $group_table" ); // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
667 if ( $limit === null ) {
668 $limit = 1;
669 }
670
671 // note that limits by blog_id for multisite are
672 // enforced when a user is added to a blog
673 $user_groups = $wpdb->get_results( $wpdb->prepare(
674 "SELECT group_id FROM $user_group_table WHERE user_id = %d", // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
675 Groups_Utility::id( $this->user->ID )
676 ) );
677 // get all capabilities directly assigned (those granted through
678 // groups are added below
679 $user_capabilities = $wpdb->get_results( $wpdb->prepare(
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
681 Groups_Utility::id( $this->user->ID )
682 ) );
683 if ( $user_capabilities ) {
684 foreach ( $user_capabilities as $user_capability ) {
685 $capabilities[] = $user_capability->capability;
686 $capability_ids[] = $user_capability->capability_id;
687 }
688 }
689
690 if ( apply_filters( 'groups_user_add_role_capabilities', true ) ) {
691 // Get all capabilities from the WP_User object.
692 $role_caps = $this->user->get_role_caps();
693 if ( !empty( $role_caps ) && is_array( $role_caps ) ) {
694 $caps = array();
695 foreach ( $role_caps as $role_cap => $has ) {
696 if ( $has && !in_array( $role_cap, $capabilities ) ) {
697 $caps[] = $role_cap;
698 }
699 }
700 if ( !empty( $caps ) ) {
701 // Retrieve the capabilities and only add those that are
702 // recognized. Note that this also effectively filters out
703 // all roles and that this is desired.
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 ) {
706 $capabilities[] = $role_capability->capability;
707 $capability_ids[] = $role_capability->capability_id;
708 }
709 }
710 }
711 }
712 }
713
714 // Get all groups the user belongs to directly or through
715 // inheritance along with their capabilities.
716 if ( $user_groups ) {
717 foreach ( $user_groups as $user_group ) {
718 $group_ids[] = Groups_Utility::id( $user_group->group_id );
719 }
720 if ( count( $group_ids ) > 0 ) {
721 $iterations = 0;
722 $old_group_ids_count = 0;
723 while( ( $iterations < $limit ) && ( count( $group_ids ) !== $old_group_ids_count ) ) {
724 $iterations++;
725 $old_group_ids_count = count( $group_ids );
726 $id_list = implode( ',', $group_ids );
727 $parent_group_ids = $wpdb->get_results(
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
729 );
730 if ( $parent_group_ids ) {
731 foreach ( $parent_group_ids as $parent_group_id ) {
732 $parent_group_id = Groups_Utility::id( $parent_group_id->parent_id );
733 if ( !in_array( $parent_group_id, $group_ids ) ) {
734 $group_ids[] = $parent_group_id;
735 }
736 }
737 }
738 }
739 $id_list = implode( ',', $group_ids );
740 $rows = $wpdb->get_results(
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
742 );
743 if ( count( $rows ) > 0 ) {
744 foreach ( $rows as $row ) {
745 if ( !in_array( $row->capability_id, $capability_ids ) ) {
746 $capabilities[] = $row->capability;
747 $capability_ids[] = $row->capability_id;
748 }
749 }
750 }
751
752 }
753 }
754 Groups_Cache::set( self::CAPABILITIES . $this->user->ID, $capabilities, self::CACHE_GROUP );
755 Groups_Cache::set( self::CAPABILITY_IDS . $this->user->ID, $capability_ids, self::CACHE_GROUP );
756 Groups_Cache::set( self::GROUP_IDS . $this->user->ID, $group_ids, self::CACHE_GROUP );
757 }
758 }
759
760 }
761 Groups_User::init();
762