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-capability.php

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

631 lines 14.8 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * class-groups-capability.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 /**
29 * Capability OPM
30 */
31 class Groups_Capability {
32
33 /**
34 * @var string cache group
35 */
36 const CACHE_GROUP = 'groups';
37
38 /**
39 * @var string key
40 *
41 * @deprecated since 4.3.0
42 */
43 const READ_BY_CAPABILITY = 'read_by_capability';
44
45 /**
46 * @var string key
47 *
48 * @deprecated since 4.3.0
49 */
50 const READ_CAPABILITY_BY_ID = 'read_capability_by_id';
51
52 /**
53 * @var string key
54 */
55 const ID_MAP = 'map_capability_by_id';
56
57 /**
58 * @var string key
59 */
60 const NAME_MAP = 'map_capability_by_name';
61
62 /**
63 * Lock timeout in microseconds.
64 *
65 * @var int
66 */
67 const LOCK_TIMEOUT = 30000000;
68
69 /**
70 * Lock name.
71 *
72 * @var string
73 */
74 const LOCK = 'groups_capability_lock';
75
76 /**
77 * Mutex lock.
78 *
79 * @var Groups_Lock
80 */
81 private static $lock = null;
82
83 /**
84 * @var object persisted capability object
85 *
86 * @access private - do not access this property directly, the visibility will be made private in the future
87 */
88 public $capability = null;
89
90 /**
91 * Lock object.
92 *
93 * @throws Groups_Lock_Exception
94 *
95 * @return Groups_Lock
96 */
97 private static function get_lock() {
98 $lock = null;
99 if ( self::$lock !== null ) {
100 $lock = self::$lock;
101 } else {
102 $timeout = apply_filters( 'groups_capability_lock_timeout', self::LOCK_TIMEOUT );
103 if ( is_numeric( $timeout ) ) {
104 $timeout = max( 0, intval( $timeout ) );
105 } else {
106 $timeout = null;
107 }
108 $lock = new Groups_Lock( self::LOCK, $timeout );
109 self::$lock = $lock;
110 }
111 return $lock;
112 }
113
114 /**
115 * Mutex locked.
116 *
117 * @return boolean
118 */
119 private static function is_locked() {
120 return self::$lock !== null && self::$lock->is_locked();
121 }
122
123 /**
124 * Mutex reader.
125 *
126 * @return boolean
127 */
128 private static function reader() {
129 $locked = false;
130 try {
131 $lock = self::get_lock();
132 $locked = $lock->reader();
133 } catch ( Groups_Lock_Exception $lex ) {
134 if ( defined( 'GROUPS_DEBUG' ) && GROUPS_DEBUG ) {
135 Groups_Log::log(
136 sprintf(
137 'Capability read lock fail [%s] [%s]',
138 self::LOCK,
139 $lex->getMessage()
140 )
141 );
142 }
143 }
144 return $locked;
145 }
146
147 /**
148 * Mutex writer.
149 *
150 * @return boolean
151 */
152 private static function writer() {
153 $locked = false;
154 try {
155 $lock = self::get_lock();
156 $locked = $lock->writer();
157 } catch ( Groups_Lock_Exception $lex ) {
158 if ( defined( 'GROUPS_DEBUG' ) && GROUPS_DEBUG ) {
159 Groups_Log::log(
160 sprintf(
161 'Capability write lock fail [%s] [%s]',
162 self::LOCK,
163 $lex->getMessage()
164 )
165 );
166 }
167 }
168 return $locked;
169 }
170
171 /**
172 * Mutex release.
173 *
174 * @return boolean
175 */
176 private static function release() {
177 $released = false;
178 if ( self::$lock !== null ) {
179 $released = self::$lock->release();
180 }
181 return $released;
182 }
183
184 /**
185 * Create by capability id.
186 *
187 * Must have been persisted.
188 *
189 * @param int $capability_id
190 */
191 public function __construct( $capability_id ) {
192 $this->capability = self::read( $capability_id );
193 if ( $this->capability === false ) {
194 $this->capability = null;
195 }
196 }
197
198 /**
199 * Provides the object ID.
200 *
201 * @return int
202 */
203 public function get_id() {
204 return $this->get_capability_id();
205 }
206
207 /**
208 * Provides the object ID.
209 *
210 * @return int
211 */
212 public function get_capability_id() {
213 return $this->capability_id; // @phpstan-ignore property.notFound
214 }
215
216 /**
217 * Provides the literal capability.
218 *
219 * @return string
220 */
221 public function get_capability() {
222 $capability = '';
223 if (
224 $this->capability !== null &&
225 is_object( $this->capability ) &&
226 !empty( $this->capability->capability )
227 ) {
228 $capability = $this->capability->capability;
229 }
230 return $capability;
231 }
232
233 /**
234 * Provides the capability's class.
235 *
236 * @return string
237 */
238 public function get_class() {
239 return $this->class; // @phpstan-ignore property.notFound
240 }
241
242 /**
243 * Provides the capability's object.
244 *
245 * @return string
246 */
247 public function get_object() {
248 return $this->object; // @phpstan-ignore property.notFound
249 }
250
251 /**
252 * Provides the capability's name.
253 *
254 * @return string
255 */
256 public function get_name() {
257 return $this->name; // @phpstan-ignore property.notFound
258 }
259
260 /**
261 * Provides the capability's description.
262 *
263 * @return string
264 */
265 public function get_description() {
266 return $this->description; // @phpstan-ignore property.notFound
267 }
268
269 /**
270 * Provides the IDs of groups that have the capability.
271 *
272 * @return int[]
273 */
274 public function get_group_ids(){
275 return $this->group_ids; // @phpstan-ignore property.notFound
276 }
277
278 /**
279 * Provides the groups that have the capability.
280 *
281 * @return Groups_Group[]
282 */
283 public function get_groups() {
284 return $this->groups; // @phpstan-ignore property.notFound
285 }
286
287 /**
288 * Retrieve a property by name.
289 *
290 * Possible properties:
291 * - capability_id
292 * - capability
293 * - class
294 * - object
295 * - name
296 * - description
297 *
298 * - group_ids groups that have the capability
299 *
300 * @param string $name property's name
301 *
302 * @return mixed property value, will return null if property does not exist
303 */
304 public function __get( $name ) {
305
306 global $wpdb;
307
308 $result = null;
309 if ( $this->capability !== null ) {
310 switch ( $name ) {
311 case 'capability_id' :
312 case 'capability' :
313 case 'class' :
314 case 'object' :
315 case 'name' :
316 case 'description' :
317 $result = $this->capability->$name;
318 break;
319 case 'group_ids' :
320 $group_capability_table = _groups_get_tablename( 'group_capability' );
321 $rows = $wpdb->get_results( $wpdb->prepare(
322 "SELECT group_id FROM $group_capability_table WHERE capability_id = %d", // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
323 Groups_Utility::id( $this->capability->capability_id )
324 ) );
325 if ( $rows ) {
326 $result = array();
327 foreach ( $rows as $row ) {
328 $result[] = $row->group_id;
329 }
330 }
331 break;
332 case 'groups' :
333 $group_capability_table = _groups_get_tablename( 'group_capability' );
334 $rows = $wpdb->get_results( $wpdb->prepare(
335 "SELECT group_id FROM $group_capability_table WHERE capability_id = %d", // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
336 Groups_Utility::id( $this->capability->capability_id )
337 ) );
338 if ( $rows ) {
339 $result = array();
340 foreach ( $rows as $row ) {
341 $result[] = new Groups_Group( $row->group_id );
342 }
343 }
344 break;
345 }
346 }
347 return $result;
348 }
349
350 /**
351 * Persist a capability.
352 *
353 * Possible keys in $map:
354 *
355 * - "capability" (required) - unique capability label, max 20 characters
356 * - "class" (optional) - class the capability applies to, max 100 chars
357 * - "object" (optional) - identifies object of that class, max 100 chars
358 * - "name" (optional) - name it if you have to
359 * - "description" (optional) - dito
360 *
361 * @param array $map attributes, requires at least: "capability"
362 *
363 * @return int capability_id on success, otherwise false
364 */
365 public static function create( $map ) {
366
367 global $wpdb;
368
369 $result = false;
370
371 $capability = isset( $map['capability'] ) ? $map['capability'] : null;
372 $class = isset( $map['class'] ) ? $map['class'] : null;
373 $object = isset( $map['object'] ) ? $map['object'] : null;
374 $name = isset( $map['name'] ) ? $map['name'] : null;
375 $description = isset( $map['description'] ) ? $map['description'] : null;
376
377 if ( !empty( $capability ) ) {
378
379 self::writer();
380
381 if ( self::read_by_capability( $capability ) === false ) {
382 $data = array(
383 'capability' => $capability
384 );
385 $formats = array( '%s' );
386 if ( !empty( $class ) ) {
387 $data['class'] = $class;
388 $formats[] = '%s';
389 }
390 if ( !empty( $object ) ) {
391 $data['object'] = $object;
392 $formats[] = '%s';
393 }
394 if ( !empty( $name ) ) {
395 $data['name'] = $name;
396 $formats[] = '%s';
397 }
398 if ( !empty( $description ) ) {
399 $data['description'] = $description;
400 $formats[] = '%s';
401 }
402 $capability_table = _groups_get_tablename( 'capability' );
403 if ( $wpdb->insert( $capability_table, $data, $formats ) ) {
404 if ( $result = $wpdb->get_var( "SELECT LAST_INSERT_ID()" ) ) {
405 // refresh cache
406 Groups_Cache::delete( self::ID_MAP, self::CACHE_GROUP );
407 Groups_Cache::delete( self::NAME_MAP, self::CACHE_GROUP );
408 }
409 }
410 }
411
412 self::release();
413
414 if ( $result !== false ) {
415 do_action( 'groups_created_capability', $result );
416 }
417
418 }
419 return $result;
420 }
421
422 /**
423 * Retrieve a capability.
424 *
425 * Use Groups_Capability::read_by_capability() if you are trying to retrieve a capability by its name.
426 *
427 * @see Groups_Capability::read_by_capability()
428 * @param int $capability_id capability's id
429 *
430 * @return object upon success, otherwise false
431 */
432 public static function read( $capability_id ) {
433 global $wpdb;
434
435 $capability_id = Groups_Utility::id( $capability_id );
436 if ( $capability_id === false || $capability_id === 0 ) {
437 return false;
438 }
439
440 $result = false;
441
442 $is_locked = self::is_locked();
443 if ( !$is_locked ) {
444 self::writer();
445 }
446
447 $cached = Groups_Cache::get( self::ID_MAP, self::CACHE_GROUP );
448 if ( $cached !== null ) {
449 $map = $cached->get_value();
450 $result = $map[$capability_id] ?? false;
451 } else {
452 $map = array();
453 $name_map = array();
454 $capability_table = _groups_get_tablename( 'capability' );
455 $capabilities = $wpdb->get_results( "SELECT * FROM $capability_table" ); // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
456 if ( is_array( $capabilities ) ) {
457 foreach ( $capabilities as $capability ) {
458 $map[$capability->capability_id] = $capability; // numerical key is automatically cast to int
459 $name_map[$capability->capability] = $capability;
460 }
461 }
462 if ( isset( $map[$capability_id] ) ) {
463 $result = $map[$capability_id];
464 }
465 Groups_Cache::set( self::ID_MAP, $map, self::CACHE_GROUP );
466 Groups_Cache::set( self::NAME_MAP, $name_map, self::CACHE_GROUP );
467 }
468
469 if ( !$is_locked ) {
470 self::release();
471 }
472
473 return $result;
474 }
475
476 /**
477 * Retrieve a capability by its name.
478 *
479 * @param string $name capability name
480 *
481 * @return object upon success, otherwise false
482 */
483 public static function read_by_capability( $name ) {
484 global $wpdb;
485
486 $result = false;
487
488 $is_locked = self::is_locked();
489 if ( !$is_locked ) {
490 self::writer();
491 }
492
493 $cached = Groups_Cache::get( self::NAME_MAP, self::CACHE_GROUP );
494 if ( $cached !== null ) {
495 $name_map = $cached->get_value();
496 $result = $name_map[$name] ?? false;
497 } else {
498 $map = array();
499 $name_map = array();
500 $capability_table = _groups_get_tablename( 'capability' );
501 $capabilities = $wpdb->get_results( "SELECT * FROM $capability_table" ); // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
502 if ( is_array( $capabilities ) ) {
503 foreach ( $capabilities as $capability ) {
504 $map[$capability->capability_id] = $capability; // numerical key is automatically cast to int
505 $name_map[$capability->capability] = $capability;
506 }
507 }
508 if ( isset( $name_map[$name] ) ) {
509 $result = $name_map[$name];
510 }
511 Groups_Cache::set( self::ID_MAP, $map, self::CACHE_GROUP );
512 Groups_Cache::set( self::NAME_MAP, $name_map, self::CACHE_GROUP );
513 }
514
515 if ( !$is_locked ) {
516 self::release();
517 }
518
519 return $result;
520 }
521
522 /**
523 * Update capability.
524 *
525 * @param array $map capability attribute, must contain capability_id
526 *
527 * @return int capability_id on success, otherwise false
528 */
529 public static function update( $map ) {
530
531 global $wpdb;
532
533 $result = false;
534
535 $capability_id = isset( $map['capability_id'] ) ? $map['capability_id'] : null;
536 $capability = isset( $map['capability'] ) ? $map['capability'] : null;
537 $class = isset( $map['class'] ) ? $map['class'] : null;
538 $object = isset( $map['object'] ) ? $map['object'] : null;
539 $name = isset( $map['name'] ) ? $map['name'] : null;
540 $description = isset( $map['description'] ) ? $map['description'] : null;
541
542 if ( $capability_id !== null ) {
543
544 self::writer();
545
546 $capability_table = _groups_get_tablename( 'capability' );
547 $old_capability = Groups_Capability::read( $capability_id );
548 if ( $old_capability ) {
549 if ( $capability !== null ) {
550 $old_capability->capability = $capability;
551 }
552 if ( $class !== null ) {
553 $old_capability->class = $class;
554 }
555 if ( $object !== null ) {
556 $old_capability->object = $object;
557 }
558 if ( $name !== null ) {
559 $old_capability->name = $name;
560 }
561 if ( $description !== null ) {
562 $old_capability->description = $description;
563 }
564 $rows = $wpdb->query( $wpdb->prepare(
565 "UPDATE $capability_table SET capability = %s, class = %s, object = %s, name = %s, description = %s WHERE capability_id = %d", // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
566 $old_capability->capability,
567 $old_capability->class,
568 $old_capability->object,
569 $old_capability->name,
570 $old_capability->description,
571 Groups_Utility::id( $capability_id )
572 ) );
573 if ( ( $rows !== false ) ) {
574 $result = $capability_id;
575 Groups_Cache::delete( self::ID_MAP, self::CACHE_GROUP );
576 Groups_Cache::delete( self::NAME_MAP, self::CACHE_GROUP );
577 }
578 }
579
580 self::release();
581
582 if ( $result !== false ) {
583 do_action( 'groups_updated_capability', $result );
584 }
585
586 }
587 return $result;
588 }
589
590 /**
591 * Remove capability and its relations.
592 *
593 * @param int $capability_id
594 *
595 * @return int capability_id if successful, false otherwise
596 */
597 public static function delete( $capability_id ) {
598
599 global $wpdb;
600 $result = false;
601
602 self::writer();
603
604 // avoid nonsense requests
605 if ( $capability = Groups_Capability::read( $capability_id ) ) {
606 $capability_table = _groups_get_tablename( 'capability' );
607 // get rid of it
608 $rows = $wpdb->query( $wpdb->prepare(
609 "DELETE FROM $capability_table WHERE capability_id = %d", // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
610 Groups_Utility::id( $capability_id )
611 ) );
612 if ( $rows !== false && $rows > 0 ) {
613 $result = $capability_id;
614 if ( !empty( $capability->capability ) ) {
615 do_action( 'groups_deleted_capability_capability', $capability->capability );
616 }
617 Groups_Cache::delete( self::ID_MAP, self::CACHE_GROUP );
618 Groups_Cache::delete( self::NAME_MAP, self::CACHE_GROUP );
619 }
620 }
621
622 self::release();
623
624 if ( $result !== false ) {
625 do_action( 'groups_deleted_capability', $result );
626 }
627
628 return $result;
629 }
630 }
631