PluginProbe
Groups – Memberships and Access Control / 1.11.0
Groups – Memberships and Access Control v1.11.0
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-group.php

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

686 lines 20.4 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * class-groups-group.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 require_once( GROUPS_CORE_LIB . "/interface-i-capable.php" );
27
28 /**
29 * Group OPM.
30 */
31 class Groups_Group implements I_Capable {
32
33 const CACHE_GROUP = 'groups';
34 const READ_BY_NAME = 'read_by_name';
35
36 /**
37 * @var Object Persisted group.
38 */
39 var $group = null;
40
41 /**
42 * Create by group id.
43 * Must have been persisted.
44 * @param int $group_id
45 */
46 public function __construct( $group_id ) {
47 $this->group = self::read( $group_id );
48 }
49
50 /**
51 * Retrieve a property by name.
52 *
53 * Possible properties:
54 * - group_id
55 * - parent_id
56 * - creator_id
57 * - datetime
58 * - name
59 * - description
60 * - capabilities, returns an array of Groups_Capability
61 * - users, returns an array of Groups_User
62 *
63 * @param string $name property's name
64 * @return property value, will return null if property does not exist
65 */
66 public function __get( $name ) {
67 global $wpdb;
68 $result = null;
69 if ( $this->group !== null ) {
70 switch( $name ) {
71 case "group_id" :
72 case "parent_id" :
73 case "creator_id" :
74 case "datetime" :
75 case "name" :
76 case "description" :
77 $result = $this->group->$name;
78 break;
79 case "capabilities" :
80 $group_capability_table = _groups_get_tablename( "group_capability" );
81 $rows = $wpdb->get_results( $wpdb->prepare(
82 "SELECT capability_id FROM $group_capability_table WHERE group_id = %d",
83 Groups_Utility::id( $this->group->group_id )
84 ) );
85 if ( $rows ) {
86 $result = array();
87 foreach ( $rows as $row ) {
88 $result[] = new Groups_Capability( $row->capability_id );
89 }
90 }
91 break;
92 case 'capabilities_deep' :
93 $capability_ids = $this->capability_ids_deep;
94 $result = array();
95 foreach( $capability_ids as $capability_id ) {
96 $result[] = new Groups_Capability( $capability_id );
97 }
98 break;
99 case 'capability_ids_deep' :
100 $capability_ids = array();
101 $group_table = _groups_get_tablename( "group" );
102 $group_capability_table = _groups_get_tablename( "group_capability" );
103 // Find this group's and all its parent groups' capabilities.
104 $group_ids = array( Groups_Utility::id( $this->group->group_id ) );
105 $iterations = 0;
106 $old_group_ids_count = 0;
107 $all_groups = $wpdb->get_var( "SELECT COUNT(*) FROM $group_table" );
108 while( ( $iterations < $all_groups ) && ( count( $group_ids ) !== $old_group_ids_count ) ) {
109 $iterations++;
110 $old_group_ids_count = count( $group_ids );
111 $id_list = implode( ",", $group_ids );
112 $parent_group_ids = $wpdb->get_results(
113 "SELECT parent_id FROM $group_table WHERE parent_id IS NOT NULL AND group_id IN ($id_list)"
114 );
115 if ( $parent_group_ids ) {
116 foreach( $parent_group_ids as $parent_group_id ) {
117 $parent_group_id = Groups_Utility::id( $parent_group_id->parent_id );
118 if ( !in_array( $parent_group_id, $group_ids ) ) {
119 $group_ids[] = $parent_group_id;
120 }
121 }
122 }
123 }
124 if ( count( $group_ids ) > 0 ) {
125 $id_list = implode( ",", $group_ids );
126 $rows = $wpdb->get_results(
127 "SELECT DISTINCT capability_id FROM $group_capability_table WHERE group_id IN ($id_list)"
128 );
129 if ( $rows ) {
130 foreach ( $rows as $row ) {
131 $capability_ids[] = $row->capability_id;
132 }
133 }
134 }
135 $result = $capability_ids;
136 break;
137 case 'users' :
138 $user_group_table = _groups_get_tablename( "user_group" );
139 $users = $wpdb->get_results( $wpdb->prepare(
140 "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 Groups_Utility::id( $this->group->group_id )
142 ) );
143 if ( $users ) {
144 $result = array();
145 foreach( $users as $user ) {
146 $result[] = new Groups_User( $user->ID );
147 }
148 }
149 break;
150 }
151 }
152 return $result;
153 }
154
155 /**
156 * (non-PHPdoc)
157 * @see I_Capable::can()
158 */
159 public function can( $capability ) {
160
161 global $wpdb;
162 $result = false;
163
164 if ( $this->group !== null ) {
165
166 $group_table = _groups_get_tablename( "group" );
167 $capability_table = _groups_get_tablename( "capability" );
168 $group_capability_table = _groups_get_tablename( "group_capability" );
169
170 // determine capability id
171 $capability_id = null;
172 if ( is_numeric( $capability ) ) {
173 $capability_id = Groups_Utility::id( $capability );
174 } else if ( is_string( $capability ) ) {
175 $capability_id = $wpdb->get_var( $wpdb->prepare(
176 "SELECT capability_id FROM $capability_table WHERE capability = %s",
177 $capability
178 ) );
179 }
180
181 if ( $capability_id !== null ) {
182 // check if the group itself can
183 $result = ( Groups_Group_Capability::read( $this->group->group_id, $capability_id ) !== false );
184 if ( !$result ) {
185 // find all parent groups and include in the group's
186 // upward hierarchy to see if any of these can
187 $group_ids = array( $this->group->group_id );
188 $iterations = 0;
189 $old_group_ids_count = 0;
190 $all_groups = $wpdb->get_var( "SELECT COUNT(*) FROM $group_table" );
191 while( ( $iterations < $all_groups ) && ( count( $group_ids ) !== $old_group_ids_count ) ) {
192 $iterations++;
193 $old_group_ids_count = count( $group_ids );
194 $id_list = implode( ",", $group_ids );
195 $parent_group_ids = $wpdb->get_results(
196 "SELECT parent_id FROM $group_table WHERE parent_id IS NOT NULL AND group_id IN ($id_list)"
197 );
198 if ( $parent_group_ids ) {
199 foreach( $parent_group_ids as $parent_group_id ) {
200 $parent_group_id = Groups_Utility::id( $parent_group_id->parent_id );
201 if ( !in_array( $parent_group_id, $group_ids ) ) {
202 $group_ids[] = $parent_group_id;
203 }
204 }
205 }
206 }
207 if ( count( $group_ids ) > 0 ) {
208 $id_list = implode( ",", $group_ids );
209 $rows = $wpdb->get_results( $wpdb->prepare(
210 "SELECT capability_id FROM $group_capability_table WHERE capability_id = %d AND group_id IN ($id_list)",
211 Groups_Utility::id( $capability_id )
212 ) );
213
214 if ( count( $rows ) > 0 ) {
215 $result = true;
216 }
217 }
218 }
219 }
220 }
221 $result = apply_filters_ref_array( "groups_group_can", array( $result, &$this, $capability ) );
222 return $result;
223 }
224
225 /**
226 * Persist a group.
227 *
228 * Parameters:
229 * - name (required) - the group's name
230 * - creator_id (optional) - defaults to the current user's id
231 * - datetime (optional) - defaults to now
232 * - description (optional)
233 * - parent_id (optional)
234 *
235 * @param array $map attributes
236 * @return group_id on success, otherwise false
237 */
238 public static function create( $map ) {
239 global $wpdb;
240 extract( $map );
241 $result = false;
242 $error = false;
243
244 if ( !empty( $name ) ) {
245
246 $group_table = _groups_get_tablename( "group" );
247
248 $data = array( 'name' => $name );
249 $formats = array( '%s' );
250 if ( !isset( $creator_id ) ) {
251 $creator_id = get_current_user_id();
252 }
253 if ( isset( $creator_id ) ) {
254 $data['creator_id'] = Groups_Utility::id( $creator_id );
255 $formats[] = '%d';
256 }
257 if ( !isset( $datetime ) ) {
258 $datetime = date( 'Y-m-d H:i:s', time() );
259 }
260 if ( isset( $datetime ) ) {
261 $data['datetime'] = $datetime;
262 $formats[] = '%s';
263 }
264 if ( !empty( $description ) ) {
265 $data['description'] = $description;
266 $formats[] = '%s';
267 }
268 if ( !empty( $parent_id ) ) {
269 // only allow to set an existing parent group (that is from the same blog)
270 $parent_group_id = $wpdb->get_var( $wpdb->prepare(
271 "SELECT group_id FROM $group_table WHERE group_id = %d",
272 Groups_Utility::id( $parent_id )
273 ) );
274 if ( $parent_group_id === $parent_id ) {
275 $data['parent_id'] = Groups_Utility::id( $parent_id );
276 $formats[] = '%d';
277 } else {
278 $error = true;
279 }
280 }
281 // no duplicate names
282 $duplicate = Groups_Group::read_by_name( $name );
283 if ( $duplicate ) {
284 $error = true;
285 }
286 if ( !$error ) {
287 if ( $wpdb->insert( $group_table, $data, $formats ) ) {
288 if ( $result = $wpdb->get_var( "SELECT LAST_INSERT_ID()" ) ) {
289 // must clear cache for this name in case it has been requested previously as it now exists
290 Groups_Cache::delete( self::READ_BY_NAME . '_' . $name, self::CACHE_GROUP );
291 do_action( "groups_created_group", $result );
292 }
293 }
294 }
295 }
296 return $result;
297 }
298
299 /**
300 * Retrieve a group.
301 *
302 * @param int $group_id group's id
303 * @return object upon success, otherwise false
304 */
305 public static function read( $group_id ) {
306 global $wpdb;
307 $result = false;
308
309 $group_table = _groups_get_tablename( 'group' );
310 $group = $wpdb->get_row( $wpdb->prepare(
311 "SELECT * FROM $group_table WHERE group_id = %d",
312 Groups_Utility::id( $group_id )
313 ) );
314 if ( isset( $group->group_id ) ) {
315 $result = $group;
316 }
317 return $result;
318 }
319
320 /**
321 * Retrieve a group by name.
322 *
323 * @param string $name the group's name
324 * @return object upon success, otherwise false
325 */
326 public static function read_by_name( $name ) {
327 global $wpdb;
328 $cached = Groups_Cache::get( self::READ_BY_NAME . '_' . $name, self::CACHE_GROUP );
329 if ( $cached !== null ) {
330 $result = $cached->value;
331 unset( $cached );
332 } else {
333 $result = false;
334 $group_table = _groups_get_tablename( 'group' );
335 $group = $wpdb->get_row( $wpdb->prepare(
336 "SELECT * FROM $group_table WHERE name = %s",
337 $name
338 ) );
339 if ( isset( $group->group_id ) ) {
340 $result = $group;
341 }
342 Groups_Cache::set( self::READ_BY_NAME . '_' . $name, $result, self::CACHE_GROUP );
343 }
344 return $result;
345 }
346
347 /**
348 * Update group.
349 *
350 * @param array $map group attribute, must contain group_id
351 * @return group_id on success, otherwise false
352 */
353 public static function update( $map ) {
354
355 global $wpdb;
356 extract( $map );
357 $result = false;
358
359 if ( isset( $group_id ) && !empty( $name ) ) {
360 $old_group = Groups_Group::read( $group_id );
361 $group_table = _groups_get_tablename( 'group' );
362 if ( !isset( $description ) || ( $description === null ) ) {
363 $description = '';
364 }
365 $wpdb->query( $wpdb->prepare(
366 "UPDATE $group_table SET name = %s, description = %s WHERE group_id = %d",
367 $name,
368 $description,
369 Groups_Utility::id( $group_id )
370 ) );
371 if ( empty( $parent_id ) ) {
372 $wpdb->query( $wpdb->prepare(
373 "UPDATE $group_table SET parent_id = NULL WHERE group_id = %d",
374 Groups_Utility::id( $group_id )
375 ) );
376 } else {
377
378 // Prohibit circular dependencies:
379 // This group cannot have a parent that is its successor
380 // at any level in its successor hierarchy.
381 // S(g) : successor of group g
382 // S*(g) : successors of group g, any level deep
383 // P(g) : parent of g
384 // ---
385 // It must hold: !( P(g) in S*(g) )
386
387 // Find all successors of this group
388 $groups = $wpdb->get_var( "SELECT COUNT(*) FROM $group_table" );
389 if ( $groups !== null ) {
390 $group_ids = array();
391 $group_ids[] = Groups_Utility::id( $group_id );
392 $iterations = 0;
393 $old_group_ids_count = 0;
394 while( ( $iterations < $groups ) && ( count( $group_ids ) > 0 ) && ( count( $group_ids ) !== $old_group_ids_count ) ) {
395
396 $iterations++;
397 $old_group_ids_count = count( $group_ids );
398
399 $id_list = implode( ",", $group_ids );
400 // We can trust ourselves here, no need to use prepare()
401 // but careful if this query is modified!
402 $successor_group_ids = $wpdb->get_results(
403 "SELECT group_id FROM $group_table WHERE parent_id IS NOT NULL AND parent_id IN ($id_list)"
404 );
405 if ( $successor_group_ids ) {
406 foreach( $successor_group_ids as $successor_group_id ) {
407 $successor_group_id = Groups_Utility::id( $successor_group_id->group_id );
408 if ( !in_array( $successor_group_id, $group_ids ) ) {
409 $group_ids[] = $successor_group_id;
410 }
411 }
412 }
413 }
414 // only add if condition holds
415 if ( !in_array( Groups_Utility::id( $parent_id ), $group_ids ) ) {
416 $wpdb->query( $wpdb->prepare(
417 "UPDATE $group_table SET parent_id = %d WHERE group_id = %d",
418 Groups_Utility::id( $parent_id),
419 Groups_Utility::id( $group_id )
420 ) );
421 }
422 }
423 }
424 $result = $group_id;
425 if ( !empty( $name ) ) {
426 Groups_Cache::delete( self::READ_BY_NAME . '_' . $name, self::CACHE_GROUP );
427 }
428 if ( !empty( $old_group ) && !empty( $old_group->name ) ) {
429 Groups_Cache::delete( self::READ_BY_NAME . '_' . $old_group->name, self::CACHE_GROUP );
430 }
431 do_action( "groups_updated_group", $result );
432 }
433 return $result;
434 }
435
436 /**
437 * Remove group and its relations.
438 *
439 * @param int $group_id
440 * @return group_id if successful, false otherwise
441 */
442 public static function delete( $group_id ) {
443
444 global $wpdb;
445 $result = false;
446
447 if ( $group = self::read( $group_id ) ) {
448
449 // delete group-capabilities
450 $group_capability_table = _groups_get_tablename( 'group_capability' );
451 $wpdb->query( $wpdb->prepare(
452 "DELETE FROM $group_capability_table WHERE group_id = %d",
453 Groups_Utility::id( $group->group_id )
454 ) );
455
456 // delete group-users
457 $user_group_table = _groups_get_tablename( 'user_group' );
458 $wpdb->query( $wpdb->prepare(
459 "DELETE FROM $user_group_table WHERE group_id = %d",
460 $group->group_id
461 ) );
462
463 // set parent_id to null where this group is parent
464 $group_table = _groups_get_tablename( 'group' );
465 $wpdb->query( $wpdb->prepare(
466 "UPDATE $group_table SET parent_id = NULL WHERE parent_id = %d",
467 $group->group_id
468 ) );
469
470 // delete group
471 if ( $wpdb->query( $wpdb->prepare(
472 "DELETE FROM $group_table WHERE group_id = %d",
473 $group->group_id
474 ) ) ) {
475 $result = $group->group_id;
476 if ( !empty( $group->name ) ) {
477 Groups_Cache::delete( self::READ_BY_NAME . '_' . $group->name, self::CACHE_GROUP );
478 }
479 do_action( "groups_deleted_group", $result );
480 }
481 }
482 return $result;
483 }
484
485 /**
486 * Returns an array of group IDs.
487 *
488 * If no arguments are passed, IDs for all existing groups are returned.
489 *
490 * @param array $args
491 * - ['order_by'] string a Groups_Group property
492 * - ['order'] string ASC or DESC. Only applied if 'order_by' is set.
493 * - ['parent_id'] int retrieve groups whose parent is indicated by this ID
494 * - ['include'] array|string with one or more IDs of groups to include, separated by comma
495 * - ['include_by_name'] array|string with one ore more group names of groups to include, separated by comma
496 * - ['exclude'] array|string with one or more IDs of groups to exclude, separated by comma
497 * - ['exclude_by_name'] array|string with one ore more group names of groups to exclude, separated by comma
498 *
499 * @return array of int with group IDs
500 *
501 * @since groups 1.4.9
502 */
503 public static function get_group_ids( $args = array() ) {
504 $result = array();
505 $args['fields'] = 'group_id';
506 $groups = self::get_groups( $args );
507 if ( sizeof( $groups ) > 0 ) {
508 foreach ( $groups as $group ) {
509 $result[] = $group->group_id;
510 }
511 }
512 return $result;
513 }
514
515 /**
516 * Returns an array of database results by querying the group table.
517 *
518 * @param Array $args
519 * - ['fields'] string with fields to get separated by comma. If empty then get all fields.
520 * - ['order_by'] string a Groups_Group property
521 * - ['order'] string ASC or DESC. Only applied if 'order_by' is set.
522 * - ['parent_id'] int retrieve groups whose parent is indicated by this ID
523 * - ['include'] array|string with one or more IDs of groups to include, separated by comma
524 * - ['include_by_name'] array|string with one ore more group names of groups to include, separated by comma
525 * - ['exclude'] array|string with one or more IDs of groups to exclude, separated by comma
526 * - ['exclude_by_name'] array|string with one ore more group names of groups to exclude, separated by comma
527 *
528 * @return array of int with group IDs
529 *
530 * @since groups 1.4.9
531 */
532 public static function get_groups( $args = array() ) {
533 global $wpdb;
534
535 extract( $args );
536
537 if ( !isset( $fields ) ) {
538 $fields = "*";
539 } else {
540 $array_fields = explode( ',', sanitize_text_field( $fields ) );
541 $fields = "";
542 foreach ( $array_fields as $field ) {
543 switch( trim( $field ) ) {
544 case 'group_id' :
545 case 'parent_id' :
546 case 'creator_id' :
547 case 'datetime' :
548 case 'name' :
549 case 'description' :
550 $fields .= ',' . trim( $field );
551 break;
552 }
553 }
554 if ( strlen( $fields ) > 0 ) {
555 $fields = substr( $fields, 1 );
556 }
557 }
558
559 if ( !isset( $order_by ) ) {
560 $order_by = "";
561 } else {
562 $order_by = sanitize_text_field( $order_by );
563 switch( trim( $field ) ) {
564 case 'group_id' :
565 case 'parent_id' :
566 case 'creator_id' :
567 case 'datetime' :
568 case 'name' :
569 case 'description' :
570 $order = '';
571 if ( !isset( $order ) || ( !( $order == 'ASC' ) && !( $order == 'DESC' ) ) ) {
572 $order = 'DESC';
573 }
574 $order_by = $wpdb->prepare( " ORDER BY %s $order ", array( $order_by ) );
575 break;
576 default :
577 $order_by = '';
578 break;
579 }
580 }
581
582 $where = '';
583 if ( isset( $parent_id ) ) {
584 $parent_id = sanitize_text_field( $parent_id );
585 if ( is_numeric ( $parent_id ) ) {
586 $where .= $wpdb->prepare( " WHERE parent_id=%s ", array( $parent_id ) );
587 }
588 }
589
590 //
591 // include by group ID
592 //
593 $where_include = '';
594 $include = !empty( $include ) ? $include : null;
595 if ( !empty( $include ) && !is_array( $include ) && is_string( $include ) ) {
596 $include = explode( ',', $include );
597 }
598 if ( count( $include ) > 0 ) {
599 $include = implode( ',', array_map( 'intval', array_map( 'trim', $include ) ) );
600 if ( strlen( $include ) > 0 ) {
601 $where_include = " group_id IN ($include) ";
602 }
603 }
604
605 //
606 // include by group name
607 //
608 $where_include_by_name = '';
609 $include_by_name = !empty( $include_by_name ) ? $include_by_name : null;
610 if ( !empty( $include_by_name ) && !is_array( $include_by_name ) && is_string( $include_by_name ) ) {
611 $include_by_name = explode( ',', $include_by_name );
612 }
613 if ( count( $include_by_name ) > 0 ) {
614 $include_by_name = "'" . implode( "','", array_map( 'esc_sql', array_map( 'trim', $include_by_name ) ) ) . "'";
615 if ( strlen( $include_by_name ) > 0 ) {
616 $where_include_by_name = " name IN ($include_by_name) ";
617 }
618 }
619
620 // adding includes ...
621 if ( ( $where_include !== '' ) || ( $where_include_by_name !== '' ) ) {
622 if ( $where == '' ) {
623 $where .= " WHERE ";
624 } else {
625 $where .= " AND ";
626 }
627 }
628 if ( ( $where_include !== "" ) && ( $where_include_by_name !== "" ) ) {
629 $where .= "(";
630 }
631 if ( $where_include !== "" ) {
632 $where .= $where_include;
633 }
634 if ( ( $where_include !== "" ) && ( $where_include_by_name !== "" ) ) {
635 $where .= " OR ";
636 }
637 if ( $where_include_by_name !== "" ) {
638 $where .= $where_include_by_name;
639 }
640 if ( ( $where_include !== "" ) && ( $where_include_by_name !== "" ) ) {
641 $where .= ")";
642 }
643
644 //
645 // exclude
646 //
647 $exclude = !empty( $exclude ) ? $exclude : null;
648 if ( !empty( $exclude ) && !is_array( $exclude ) && is_string( $exclude ) ) {
649 $exclude = explode( ',', $exclude );
650 }
651 if ( count( $exclude ) > 0 ) {
652 $exclude = implode( ',', array_map( 'intval', array_map( 'trim', $exclude ) ) );
653 if ( strlen( $exclude ) > 0 ) {
654 if ( empty( $where ) ) {
655 $where = " WHERE group_id NOT IN ($exclude) ";
656 } else {
657 $where .= " AND group_id NOT IN ($exclude) ";
658 }
659 }
660 }
661
662 //
663 // exclude by group name
664 //
665 $exclude_by_name = !empty( $exclude_by_name ) ? $exclude_by_name : null;
666 if ( !empty( $exclude_by_name ) && !is_array( $exclude_by_name ) && is_string( $exclude_by_name ) ) {
667 $exclude_by_name = explode( ',', $exclude_by_name );
668 }
669 if ( count( $exclude_by_name ) > 0 ) {
670 $exclude_by_name = "'" . implode( "','", array_map( 'esc_sql', array_map( 'trim', $exclude_by_name ) ) ) . "'";
671 if ( strlen( $exclude_by_name ) > 0 ) {
672 if ( empty( $where ) ) {
673 $where = " WHERE name NOT IN ($exclude_by_name) ";
674 } else {
675 $where .= " AND name NOT IN ($exclude_by_name) ";
676 }
677 }
678 }
679
680 $groups_table = _groups_get_tablename( 'group' );
681 $groups = $wpdb->get_results( "SELECT $fields FROM $groups_table $where $order_by" );
682
683 return $groups;
684 }
685 }
686