Skip to:
Content

BuddyPress.org

source: trunk/src/bp-groups/classes/class-bp-groups-group.php @ 11071

Last change on this file since 11071 was 11071, checked in by boonebgorges, 6 years ago

Groups: Overhaul BP_Groups_Group::get() SQL query.

The new query follows the WP and BP convention of "split" queries: one
for the IDs matching the query parameters, and a second one for the
objects corresponding to the matched IDs. This configuration allows for
improved caching and better performance, especially on installations
with large numbers of groups.

The rewrite serves as a convenient excuse to address a number of
longtime annoyances with the way group queries work:

  • Previously, comma syntax was used for table joins, rather than the JOIN keyword. This required some string manipulation when using external tools for generating SQL fragments, such as WP_Tax_Query and WP_Meta_Query. See #5099, #5874. We now use the more standard JOIN syntax.
  • The logic for assembling the "total" query is overwhelmingly simpler.
  • Previously, group queries required three joined tables: the groups table, one groupmeta table (for last_activity), and a second groupmeta table (for total_member_count). After this changeset, these tables will only be joined when needed for sorting (orderby or type). The last_activity and total_member_count properties, when needed for display in a group loop, are lazyloaded from groupmeta - where they're precached by default (see update_meta_cache) - rather than pulled as part of the primary query, and are made available via __get() for backward compatibility.

See #5451, #5874. Fixes #5099.

File size: 60.1 KB
Line 
1<?php
2/**
3 * BuddyPress Groups Classes.
4 *
5 * @package BuddyPress
6 * @subpackage GroupsClasses
7 * @since 1.0.0
8 */
9
10// Exit if accessed directly.
11defined( 'ABSPATH' ) || exit;
12
13/**
14 * BuddyPress Group object.
15 *
16 * @since 1.6.0
17 */
18class BP_Groups_Group {
19
20        /**
21         * ID of the group.
22         *
23         * @since 1.6.0
24         * @var int
25         */
26        public $id;
27
28        /**
29         * User ID of the group's creator.
30         *
31         * @since 1.6.0
32         * @var int
33         */
34        public $creator_id;
35
36        /**
37         * Name of the group.
38         *
39         * @since 1.6.0
40         * @var string
41         */
42        public $name;
43
44        /**
45         * Group slug.
46         *
47         * @since 1.6.0
48         * @var string
49         */
50        public $slug;
51
52        /**
53         * Group description.
54         *
55         * @since 1.6.0
56         * @var string
57         */
58        public $description;
59
60        /**
61         * Group status.
62         *
63         * Core statuses are 'public', 'private', and 'hidden'.
64         *
65         * @since 1.6.0
66         * @var string
67         */
68        public $status;
69
70        /**
71         * Should (legacy) bbPress forums be enabled for this group?
72         *
73         * @since 1.6.0
74         * @var int
75         */
76        public $enable_forum;
77
78        /**
79         * Date the group was created.
80         *
81         * @since 1.6.0
82         * @var string
83         */
84        public $date_created;
85
86        /**
87         * Data about the group's admins.
88         *
89         * @since 1.6.0
90         * @var array
91         */
92        public $admins;
93
94        /**
95         * Data about the group's moderators.
96         *
97         * @since 1.6.0
98         * @var array
99         */
100        public $mods;
101
102        /**
103         * Total count of group members.
104         *
105         * @since 1.6.0
106         * @var int
107         */
108        protected $total_member_count;
109
110        /**
111         * Is the current user a member of this group?
112         *
113         * @since 1.2.0
114         * @var bool
115         */
116        public $is_member;
117
118        /**
119         * Does the current user have an outstanding invitation to this group?
120         *
121         * @since 1.9.0
122         * @var bool
123         */
124        public $is_invited;
125
126        /**
127         * Does the current user have a pending membership request to this group?
128         *
129         * @since 1.9.0
130         * @var bool
131         */
132        public $is_pending;
133
134        /**
135         * Timestamp of the last activity that happened in this group.
136         *
137         * @since 1.2.0
138         * @var string
139         */
140        protected $last_activity;
141
142        /**
143         * If this is a private or hidden group, does the current user have access?
144         *
145         * @since 1.6.0
146         * @var bool
147         */
148        public $user_has_access;
149
150        /**
151         * Raw arguments passed to the constructor.
152         *
153         * @since 2.0.0
154         * @var array
155         */
156        public $args;
157
158        /**
159         * Constructor method.
160         *
161         * @since 1.6.0
162         *
163         * @param int|null $id   Optional. If the ID of an existing group is provided,
164         *                       the object will be pre-populated with info about that group.
165         * @param array    $args {
166         *     Array of optional arguments.
167         *     @type bool $populate_extras Whether to fetch "extra" data about the group
168         *                                 (group admins/mods, access for the current user).
169         *                                 Default: false.
170         * }
171         */
172        public function __construct( $id = null, $args = array() ) {
173                $this->args = wp_parse_args( $args, array(
174                        'populate_extras' => false,
175                ) );
176
177                if ( !empty( $id ) ) {
178                        $this->id = (int) $id;
179                        $this->populate();
180                }
181        }
182
183        /**
184         * Set up data about the current group.
185         *
186         * @since 1.6.0
187         */
188        public function populate() {
189                global $wpdb;
190
191                // Get BuddyPress.
192                $bp    = buddypress();
193
194                // Check cache for group data.
195                $group = wp_cache_get( $this->id, 'bp_groups' );
196
197                // Cache missed, so query the DB.
198                if ( false === $group ) {
199                        $group = $wpdb->get_row( $wpdb->prepare( "SELECT g.* FROM {$bp->groups->table_name} g WHERE g.id = %d", $this->id ) );
200
201                        wp_cache_set( $this->id, $group, 'bp_groups' );
202                }
203
204                // No group found so set the ID and bail.
205                if ( empty( $group ) || is_wp_error( $group ) ) {
206                        $this->id = 0;
207                        return;
208                }
209
210                // Group found so setup the object variables.
211                $this->id           = (int) $group->id;
212                $this->creator_id   = (int) $group->creator_id;
213                $this->name         = stripslashes( $group->name );
214                $this->slug         = $group->slug;
215                $this->description  = stripslashes( $group->description );
216                $this->status       = $group->status;
217                $this->enable_forum = (int) $group->enable_forum;
218                $this->date_created = $group->date_created;
219
220                // Are we getting extra group data?
221                if ( ! empty( $this->args['populate_extras'] ) ) {
222
223                        /**
224                         * Filters the SQL prepared statement used to fetch group admins and mods.
225                         *
226                         * @since 1.5.0
227                         *
228                         * @param string $value SQL select statement used to fetch admins and mods.
229                         */
230                        $admin_mods = $wpdb->get_results( apply_filters( 'bp_group_admin_mods_user_join_filter', $wpdb->prepare( "SELECT u.ID as user_id, u.user_login, u.user_email, u.user_nicename, m.is_admin, m.is_mod FROM {$wpdb->users} u, {$bp->groups->table_name_members} m WHERE u.ID = m.user_id AND m.group_id = %d AND ( m.is_admin = 1 OR m.is_mod = 1 )", $this->id ) ) );
231
232                        // Add admins and moderators to their respective arrays.
233                        foreach ( (array) $admin_mods as $user ) {
234                                $user->user_id  = (int) $user->user_id;
235                                $user->is_admin = (int) $user->is_admin;
236                                $user->is_mod   = (int) $user->is_mod;
237
238                                if ( !empty( $user->is_admin ) ) {
239                                        $this->admins[] = $user;
240                                } else {
241                                        $this->mods[] = $user;
242                                }
243                        }
244
245                        // Set user-specific data.
246                        $user_id          = bp_loggedin_user_id();
247                        $this->is_member  = groups_is_user_member( $user_id, $this->id );
248                        $this->is_invited = groups_check_user_has_invite( $user_id, $this->id );
249                        $this->is_pending = groups_check_for_membership_request( $user_id, $this->id );
250
251                        // If this is a private or hidden group, does the current user have access?
252                        if ( ( 'private' === $this->status ) || ( 'hidden' === $this->status ) ) {
253
254                                // Assume user does not have access to hidden/private groups.
255                                $this->user_has_access = false;
256
257                                // Group members or community moderators have access.
258                                if ( ( $this->is_member && is_user_logged_in() ) || bp_current_user_can( 'bp_moderate' ) ) {
259                                        $this->user_has_access = true;
260                                }
261                        } else {
262                                $this->user_has_access = true;
263                        }
264                }
265        }
266
267        /**
268         * Save the current group to the database.
269         *
270         * @since 1.6.0
271         *
272         * @return bool True on success, false on failure.
273         */
274        public function save() {
275                global $wpdb;
276
277                $bp = buddypress();
278
279                $this->creator_id   = apply_filters( 'groups_group_creator_id_before_save',   $this->creator_id,   $this->id );
280                $this->name         = apply_filters( 'groups_group_name_before_save',         $this->name,         $this->id );
281                $this->slug         = apply_filters( 'groups_group_slug_before_save',         $this->slug,         $this->id );
282                $this->description  = apply_filters( 'groups_group_description_before_save',  $this->description,  $this->id );
283                $this->status       = apply_filters( 'groups_group_status_before_save',       $this->status,       $this->id );
284                $this->enable_forum = apply_filters( 'groups_group_enable_forum_before_save', $this->enable_forum, $this->id );
285                $this->date_created = apply_filters( 'groups_group_date_created_before_save', $this->date_created, $this->id );
286
287                /**
288                 * Fires before the current group item gets saved.
289                 *
290                 * Please use this hook to filter the properties above. Each part will be passed in.
291                 *
292                 * @since 1.0.0
293                 *
294                 * @param BP_Groups_Group $this Current instance of the group item being saved. Passed by reference.
295                 */
296                do_action_ref_array( 'groups_group_before_save', array( &$this ) );
297
298                // Groups need at least a name.
299                if ( empty( $this->name ) ) {
300                        return false;
301                }
302
303                // Set slug with group title if not passed.
304                if ( empty( $this->slug ) ) {
305                        $this->slug = sanitize_title( $this->name );
306                }
307
308                // Sanity check.
309                if ( empty( $this->slug ) ) {
310                        return false;
311                }
312
313                // Check for slug conflicts if creating new group.
314                if ( empty( $this->id ) ) {
315                        $this->slug = groups_check_slug( $this->slug );
316                }
317
318                if ( !empty( $this->id ) ) {
319                        $sql = $wpdb->prepare(
320                                "UPDATE {$bp->groups->table_name} SET
321                                        creator_id = %d,
322                                        name = %s,
323                                        slug = %s,
324                                        description = %s,
325                                        status = %s,
326                                        enable_forum = %d,
327                                        date_created = %s
328                                WHERE
329                                        id = %d
330                                ",
331                                        $this->creator_id,
332                                        $this->name,
333                                        $this->slug,
334                                        $this->description,
335                                        $this->status,
336                                        $this->enable_forum,
337                                        $this->date_created,
338                                        $this->id
339                        );
340                } else {
341                        $sql = $wpdb->prepare(
342                                "INSERT INTO {$bp->groups->table_name} (
343                                        creator_id,
344                                        name,
345                                        slug,
346                                        description,
347                                        status,
348                                        enable_forum,
349                                        date_created
350                                ) VALUES (
351                                        %d, %s, %s, %s, %s, %d, %s
352                                )",
353                                        $this->creator_id,
354                                        $this->name,
355                                        $this->slug,
356                                        $this->description,
357                                        $this->status,
358                                        $this->enable_forum,
359                                        $this->date_created
360                        );
361                }
362
363                if ( false === $wpdb->query($sql) )
364                        return false;
365
366                if ( empty( $this->id ) )
367                        $this->id = $wpdb->insert_id;
368
369                /**
370                 * Fires after the current group item has been saved.
371                 *
372                 * @since 1.0.0
373                 *
374                 * @param BP_Groups_Group $this Current instance of the group item that was saved. Passed by reference.
375                 */
376                do_action_ref_array( 'groups_group_after_save', array( &$this ) );
377
378                wp_cache_delete( $this->id, 'bp_groups' );
379
380                return true;
381        }
382
383        /**
384         * Delete the current group.
385         *
386         * @since 1.6.0
387         *
388         * @return bool True on success, false on failure.
389         */
390        public function delete() {
391                global $wpdb;
392
393                // Delete groupmeta for the group.
394                groups_delete_groupmeta( $this->id );
395
396                // Fetch the user IDs of all the members of the group.
397                $user_ids    = BP_Groups_Member::get_group_member_ids( $this->id );
398                $user_id_str = esc_sql( implode( ',', wp_parse_id_list( $user_ids ) ) );
399
400                // Modify group count usermeta for members.
401                $wpdb->query( "UPDATE {$wpdb->usermeta} SET meta_value = meta_value - 1 WHERE meta_key = 'total_group_count' AND user_id IN ( {$user_id_str} )" );
402
403                // Now delete all group member entries.
404                BP_Groups_Member::delete_all( $this->id );
405
406                /**
407                 * Fires before the deletion of a group.
408                 *
409                 * @since 1.2.0
410                 *
411                 * @param BP_Groups_Group $this     Current instance of the group item being deleted. Passed by reference.
412                 * @param array           $user_ids Array of user IDs that were members of the group.
413                 */
414                do_action_ref_array( 'bp_groups_delete_group', array( &$this, $user_ids ) );
415
416                wp_cache_delete( $this->id, 'bp_groups' );
417
418                $bp = buddypress();
419
420                // Finally remove the group entry from the DB.
421                if ( !$wpdb->query( $wpdb->prepare( "DELETE FROM {$bp->groups->table_name} WHERE id = %d", $this->id ) ) )
422                        return false;
423
424                return true;
425        }
426
427        /**
428         * Magic getter.
429         *
430         * @since 2.7.0
431         *
432         * @param string $key Property name.
433         * @return mixed
434         */
435        public function __get( $key ) {
436                switch ( $key ) {
437                        case 'last_activity' :
438                                return groups_get_groupmeta( $this->id, 'last_activity' );
439
440                        case 'total_member_count' :
441                                return (int) groups_get_groupmeta( $this->id, 'total_member_count' );
442
443                        default :
444                        break;
445                }
446        }
447
448        /**
449         * Magic issetter.
450         *
451         * Used to maintain backward compatibility for properties that are now
452         * accessible only via magic method.
453         *
454         * @since 2.7.0
455         *
456         * @param string $key Property name.
457         * @return bool
458         */
459        public function __isset( $key ) {
460                switch ( $key ) {
461                        case 'last_activity' :
462                        case 'total_member_count' :
463                                return true;
464
465                        default :
466                                return false;
467                }
468        }
469
470        /** Static Methods ****************************************************/
471
472        /**
473         * Get whether a group exists for a given slug.
474         *
475         * @since 1.6.0
476         *
477         * @param string      $slug       Slug to check.
478         * @param string|bool $table_name Optional. Name of the table to check
479         *                                against. Default: $bp->groups->table_name.
480         * @return int|null Group ID if found; null if not.
481         */
482        public static function group_exists( $slug, $table_name = false ) {
483                global $wpdb;
484
485                if ( empty( $table_name ) )
486                        $table_name = buddypress()->groups->table_name;
487
488                if ( empty( $slug ) )
489                        return false;
490
491                $query = $wpdb->get_var( $wpdb->prepare( "SELECT id FROM {$table_name} WHERE slug = %s", strtolower( $slug ) ) );
492
493                return is_numeric( $query ) ? (int) $query : $query;
494        }
495
496        /**
497         * Get the ID of a group by the group's slug.
498         *
499         * Alias of {@link BP_Groups_Group::group_exists()}.
500         *
501         * @since 1.6.0
502         *
503         * @param string $slug See {@link BP_Groups_Group::group_exists()}.
504         * @return string|null See {@link BP_Groups_Group::group_exists()}.
505         */
506        public static function get_id_from_slug( $slug ) {
507                return BP_Groups_Group::group_exists( $slug );
508        }
509
510        /**
511         * Get IDs of users with outstanding invites to a given group from a specified user.
512         *
513         * @since 1.6.0
514         *
515         * @param int $user_id ID of the inviting user.
516         * @param int $group_id ID of the group.
517         * @return array IDs of users who have been invited to the group by the
518         *               user but have not yet accepted.
519         */
520        public static function get_invites( $user_id, $group_id ) {
521                global $wpdb;
522
523                $bp = buddypress();
524
525                return $wpdb->get_col( $wpdb->prepare( "SELECT user_id FROM {$bp->groups->table_name_members} WHERE group_id = %d and is_confirmed = 0 AND inviter_id = %d", $group_id, $user_id ) );
526        }
527
528        /**
529         * Get a list of a user's groups, filtered by a search string.
530         *
531         * @since 1.6.0
532         *
533         * @param string   $filter  Search term. Matches against 'name' and
534         *                          'description' fields.
535         * @param int      $user_id ID of the user whose groups are being searched.
536         *                          Default: the displayed user.
537         * @param mixed    $order   Not used.
538         * @param int|null $limit   Optional. The max number of results to return.
539         *                          Default: null (no limit).
540         * @param int|null $page    Optional. The page offset of results to return.
541         *                          Default: null (no limit).
542         * @return false|array {
543         *     @type array $groups Array of matched and paginated group objects.
544         *     @type int   $total  Total count of groups matching the query.
545         * }
546         */
547        public static function filter_user_groups( $filter, $user_id = 0, $order = false, $limit = null, $page = null ) {
548                global $wpdb;
549
550                if ( empty( $user_id ) )
551                        $user_id = bp_displayed_user_id();
552
553                $search_terms_like = '%' . bp_esc_like( $filter ) . '%';
554
555                $pag_sql = $order_sql = $hidden_sql = '';
556
557                if ( !empty( $limit ) && !empty( $page ) )
558                        $pag_sql = $wpdb->prepare( " LIMIT %d, %d", intval( ( $page - 1 ) * $limit), intval( $limit ) );
559
560                // Get all the group ids for the current user's groups.
561                $gids = BP_Groups_Member::get_group_ids( $user_id );
562
563                if ( empty( $gids['groups'] ) )
564                        return false;
565
566                $bp = buddypress();
567
568                $gids = esc_sql( implode( ',', wp_parse_id_list( $gids['groups'] ) ) );
569
570                $paged_groups = $wpdb->get_results( $wpdb->prepare( "SELECT id as group_id FROM {$bp->groups->table_name} WHERE ( name LIKE %s OR description LIKE %s ) AND id IN ({$gids}) {$pag_sql}", $search_terms_like, $search_terms_like ) );
571                $total_groups = $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(id) FROM {$bp->groups->table_name} WHERE ( name LIKE %s OR description LIKE %s ) AND id IN ({$gids})", $search_terms_like, $search_terms_like ) );
572
573                return array( 'groups' => $paged_groups, 'total' => $total_groups );
574        }
575
576        /**
577         * Get a list of groups, filtered by a search string.
578         *
579         * @since 1.6.0
580         *
581         * @param string      $filter  Search term. Matches against 'name' and
582         *                             'description' fields.
583         * @param int|null    $limit   Optional. The max number of results to return.
584         *                             Default: null (no limit).
585         * @param int|null    $page    Optional. The page offset of results to return.
586         *                             Default: null (no limit).
587         * @param string|bool $sort_by Column to sort by. Default: false (default
588         *        sort).
589         * @param string|bool $order   ASC or DESC. Default: false (default sort).
590         * @return array {
591         *     @type array $groups Array of matched and paginated group objects.
592         *     @type int   $total  Total count of groups matching the query.
593         * }
594         */
595        public static function search_groups( $filter, $limit = null, $page = null, $sort_by = false, $order = false ) {
596                global $wpdb;
597
598                $search_terms_like = '%' . bp_esc_like( $filter ) . '%';
599
600                $pag_sql = $order_sql = $hidden_sql = '';
601
602                if ( !empty( $limit ) && !empty( $page ) )
603                        $pag_sql = $wpdb->prepare( " LIMIT %d, %d", intval( ( $page - 1 ) * $limit), intval( $limit ) );
604
605                if ( !empty( $sort_by ) && !empty( $order ) ) {
606                        $sort_by   = esc_sql( $sort_by );
607                        $order     = esc_sql( $order );
608                        $order_sql = "ORDER BY {$sort_by} {$order}";
609                }
610
611                if ( !bp_current_user_can( 'bp_moderate' ) )
612                        $hidden_sql = "AND status != 'hidden'";
613
614                $bp = buddypress();
615
616                $paged_groups = $wpdb->get_results( $wpdb->prepare( "SELECT id as group_id FROM {$bp->groups->table_name} WHERE ( name LIKE %s OR description LIKE %s ) {$hidden_sql} {$order_sql} {$pag_sql}", $search_terms_like, $search_terms_like ) );
617                $total_groups = $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(id) FROM {$bp->groups->table_name} WHERE ( name LIKE %s OR description LIKE %s ) {$hidden_sql}", $search_terms_like, $search_terms_like ) );
618
619                return array( 'groups' => $paged_groups, 'total' => $total_groups );
620        }
621
622        /**
623         * Check for the existence of a slug.
624         *
625         * @since 1.6.0
626         *
627         * @param string $slug Slug to check.
628         * @return string|null The slug, if found. Otherwise null.
629         */
630        public static function check_slug( $slug ) {
631                global $wpdb;
632
633                $bp = buddypress();
634
635                return $wpdb->get_var( $wpdb->prepare( "SELECT slug FROM {$bp->groups->table_name} WHERE slug = %s", $slug ) );
636        }
637
638        /**
639         * Get the slug for a given group ID.
640         *
641         * @since 1.6.0
642         *
643         * @param int $group_id ID of the group.
644         * @return string|null The slug, if found. Otherwise null.
645         */
646        public static function get_slug( $group_id ) {
647                global $wpdb;
648
649                $bp = buddypress();
650
651                return $wpdb->get_var( $wpdb->prepare( "SELECT slug FROM {$bp->groups->table_name} WHERE id = %d", $group_id ) );
652        }
653
654        /**
655         * Check whether a given group has any members.
656         *
657         * @since 1.6.0
658         *
659         * @param int $group_id ID of the group.
660         * @return bool True if the group has members, otherwise false.
661         */
662        public static function has_members( $group_id ) {
663                global $wpdb;
664
665                $bp = buddypress();
666
667                $members = $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(id) FROM {$bp->groups->table_name_members} WHERE group_id = %d", $group_id ) );
668
669                if ( empty( $members ) )
670                        return false;
671
672                return true;
673        }
674
675        /**
676         * Check whether a group has outstanding membership requests.
677         *
678         * @since 1.6.0
679         *
680         * @param int $group_id ID of the group.
681         * @return int|null The number of outstanding requests, or null if
682         *                  none are found.
683         */
684        public static function has_membership_requests( $group_id ) {
685                global $wpdb;
686
687                $bp = buddypress();
688
689                return $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(id) FROM {$bp->groups->table_name_members} WHERE group_id = %d AND is_confirmed = 0", $group_id ) );
690        }
691
692        /**
693         * Get outstanding membership requests for a group.
694         *
695         * @since 1.6.0
696         *
697         * @param int      $group_id ID of the group.
698         * @param int|null $limit    Optional. Max number of results to return.
699         *                           Default: null (no limit).
700         * @param int|null $page     Optional. Page offset of results returned. Default:
701         *                           null (no limit).
702         * @return array {
703         *     @type array $requests The requested page of located requests.
704         *     @type int   $total    Total number of requests outstanding for the
705         *                           group.
706         * }
707         */
708        public static function get_membership_requests( $group_id, $limit = null, $page = null ) {
709                global $wpdb;
710
711                if ( !empty( $limit ) && !empty( $page ) ) {
712                        $pag_sql = $wpdb->prepare( " LIMIT %d, %d", intval( ( $page - 1 ) * $limit), intval( $limit ) );
713                }
714
715                $bp = buddypress();
716
717                $paged_requests = $wpdb->get_results( $wpdb->prepare( "SELECT * FROM {$bp->groups->table_name_members} WHERE group_id = %d AND is_confirmed = 0 AND inviter_id = 0{$pag_sql}", $group_id ) );
718                $total_requests = $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(id) FROM {$bp->groups->table_name_members} WHERE group_id = %d AND is_confirmed = 0 AND inviter_id = 0", $group_id ) );
719
720                return array( 'requests' => $paged_requests, 'total' => $total_requests );
721        }
722
723        /**
724         * Query for groups.
725         *
726         * @see WP_Meta_Query::queries for a description of the 'meta_query'
727         *      parameter format.
728         *
729         * @since 1.6.0
730         * @since 2.6.0 Added `$group_type`, `$group_type__in`, and `$group_type__not_in` parameters.
731         *
732         * @param array $args {
733         *     Array of parameters. All items are optional.
734         *     @type string       $type               Optional. Shorthand for certain orderby/order combinations.
735         *                                            'newest', 'active', 'popular', 'alphabetical', 'random'.
736         *                                            When present, will override orderby and order params.
737         *                                            Default: null.
738         *     @type string       $orderby            Optional. Property to sort by. 'date_created', 'last_activity',
739         *                                            'total_member_count', 'name', 'random'. Default: 'date_created'.
740         *     @type string       $order              Optional. Sort order. 'ASC' or 'DESC'. Default: 'DESC'.
741         *     @type int          $per_page           Optional. Number of items to return per page of results.
742         *                                            Default: null (no limit).
743         *     @type int          $page               Optional. Page offset of results to return.
744         *                                            Default: null (no limit).
745         *     @type int          $user_id            Optional. If provided, results will be limited to groups
746         *                                            of which the specified user is a member. Default: null.
747         *     @type string       $search_terms       Optional. If provided, only groups whose names or descriptions
748         *                                            match the search terms will be returned. Default: false.
749         *     @type array|string $group_type         Array or comma-separated list of group types to limit results to.
750         *     @type array|string $group_type__in     Array or comma-separated list of group types to limit results to.
751         *     @type array|string $group_type__not_in Array or comma-separated list of group types that will be
752         *                                            excluded from results.
753         *     @type array        $meta_query         Optional. An array of meta_query conditions.
754         *                                            See {@link WP_Meta_Query::queries} for description.
755         *     @type array|string $value              Optional. Array or comma-separated list of group IDs. Results
756         *                                            will be limited to groups within the list. Default: false.
757         *     @type bool         $populate_extras    Whether to fetch additional information
758         *                                            (such as member count) about groups. Default: true.
759         *     @type array|string $exclude            Optional. Array or comma-separated list of group IDs.
760         *                                            Results will exclude the listed groups. Default: false.
761         *     @type bool         $update_meta_cache  Whether to pre-fetch groupmeta for the returned groups.
762         *                                            Default: true.
763         *     @type bool         $show_hidden        Whether to include hidden groups in results. Default: false.
764         * }
765         * @return array {
766         *     @type array $groups Array of group objects returned by the
767         *                         paginated query.
768         *     @type int   $total  Total count of all groups matching non-
769         *                         paginated query params.
770         * }
771         */
772        public static function get( $args = array() ) {
773                global $wpdb;
774
775                // Backward compatibility with old method of passing arguments.
776                if ( ! is_array( $args ) || func_num_args() > 1 ) {
777                        _deprecated_argument( __METHOD__, '1.7', sprintf( __( 'Arguments passed to %1$s should be in an associative array. See the inline documentation at %2$s for more details.', 'buddypress' ), __METHOD__, __FILE__ ) );
778
779                        $old_args_keys = array(
780                                0 => 'type',
781                                1 => 'per_page',
782                                2 => 'page',
783                                3 => 'user_id',
784                                4 => 'search_terms',
785                                5 => 'include',
786                                6 => 'populate_extras',
787                                7 => 'exclude',
788                                8 => 'show_hidden',
789                        );
790
791                        $func_args = func_get_args();
792                        $args      = bp_core_parse_args_array( $old_args_keys, $func_args );
793                }
794
795                $defaults = array(
796                        'type'               => null,
797                        'orderby'            => 'date_created',
798                        'order'              => 'DESC',
799                        'per_page'           => null,
800                        'page'               => null,
801                        'user_id'            => 0,
802                        'search_terms'       => false,
803                        'group_type'         => '',
804                        'group_type__in'     => '',
805                        'group_type__not_in' => '',
806                        'meta_query'         => false,
807                        'include'            => false,
808                        'populate_extras'    => true,
809                        'update_meta_cache'  => true,
810                        'exclude'            => false,
811                        'show_hidden'        => false,
812                );
813
814                $r = wp_parse_args( $args, $defaults );
815
816                $bp = buddypress();
817
818                $sql = array(
819                        'select'     => "SELECT DISTINCT g.id",
820                        'from'       => "{$bp->groups->table_name} g",
821                        'where'      => '',
822                        'orderby'    => '',
823                        'pagination' => '',
824                );
825
826
827                if ( ! empty( $r['user_id'] ) ) {
828                        $sql['from'] .= " JOIN {$bp->groups->table_name_members} m ON ( g.id = m.group_id )";
829                }
830
831                $where_conditions = array();
832
833                if ( empty( $r['show_hidden'] ) ) {
834                        $where_conditions['hidden'] = "g.status != 'hidden'";
835                }
836
837                if ( ! empty( $r['search_terms'] ) ) {
838                        $search_terms_like = '%' . bp_esc_like( $r['search_terms'] ) . '%';
839                        $where_conditions['search'] = $wpdb->prepare( "( g.name LIKE %s OR g.description LIKE %s )", $search_terms_like, $search_terms_like );
840                }
841
842                $meta_query_sql = self::get_meta_query_sql( $r['meta_query'] );
843
844                if ( ! empty( $meta_query_sql['join'] ) ) {
845                        $sql['from'] .= $meta_query_sql['join'];
846                }
847
848                if ( ! empty( $meta_query_sql['where'] ) ) {
849                        $where_conditions['meta'] = $meta_query_sql['where'];
850                }
851
852                // Only use 'group_type__in', if 'group_type' is not set.
853                if ( empty( $r['group_type'] ) && ! empty( $r['group_type__in']) ) {
854                        $r['group_type'] = $r['group_type__in'];
855                }
856
857                // Group types to exclude. This has priority over inclusions.
858                if ( ! empty( $r['group_type__not_in'] ) ) {
859                        $group_type_clause = self::get_sql_clause_for_group_types( $r['group_type__not_in'], 'NOT IN' );
860
861                // Group types to include.
862                } elseif ( ! empty( $r['group_type'] ) ) {
863                        $group_type_clause = self::get_sql_clause_for_group_types( $r['group_type'], 'IN' );
864                }
865
866                if ( ! empty( $group_type_clause ) ) {
867                        $where_conditions['group_type'] = $group_type_clause;
868                }
869
870                if ( ! empty( $r['user_id'] ) ) {
871                        $where_conditions['user'] = $wpdb->prepare( "m.user_id = %d AND m.is_confirmed = 1 AND m.is_banned = 0", $r['user_id'] );
872                }
873
874                if ( ! empty( $r['include'] ) ) {
875                        $include        = implode( ',', wp_parse_id_list( $r['include'] ) );
876                        $where_conditions['include'] = "g.id IN ({$include})";
877                }
878
879                if ( ! empty( $r['exclude'] ) ) {
880                        $exclude        = implode( ',', wp_parse_id_list( $r['exclude'] ) );
881                        $where_conditions['exclude'] = "g.id NOT IN ({$exclude})";
882                }
883
884                /* Order/orderby ********************************************/
885
886                $order   = $r['order'];
887                $orderby = $r['orderby'];
888
889                // If a 'type' parameter was passed, parse it and overwrite
890                // 'order' and 'orderby' params passed to the function.
891                if (  ! empty( $r['type'] ) ) {
892
893                        /**
894                         * Filters the 'type' parameter used to overwrite 'order' and 'orderby' values.
895                         *
896                         * @since 2.1.0
897                         *
898                         * @param array  $value Converted 'type' value for order and orderby.
899                         * @param string $value Parsed 'type' value for the get method.
900                         */
901                        $order_orderby = apply_filters( 'bp_groups_get_orderby', self::convert_type_to_order_orderby( $r['type'] ), $r['type'] );
902
903                        // If an invalid type is passed, $order_orderby will be
904                        // an array with empty values. In this case, we stick
905                        // with the default values of $order and $orderby.
906                        if ( ! empty( $order_orderby['order'] ) ) {
907                                $order = $order_orderby['order'];
908                        }
909
910                        if ( ! empty( $order_orderby['orderby'] ) ) {
911                                $orderby = $order_orderby['orderby'];
912                        }
913                }
914
915                // 'total_member_count' and 'last_activity' sorts require additional table joins.
916                if ( 'total_member_count' === $orderby ) {
917                        $sql['from'] .= " JOIN {$bp->groups->table_name_groupmeta} gm_total_member_count ON ( g.id = gm_total_member_count.group_id )";
918                        $where_conditions['total_member_count'] = "gm_total_member_count.meta_key = 'total_member_count'";
919                } elseif ( 'last_activity' === $orderby ) {
920
921                        $sql['from'] .= " JOIN {$bp->groups->table_name_groupmeta} gm_last_activity on ( g.id = gm_last_activity.group_id )";
922                        $where_conditions['last_activity'] = "gm_last_activity.meta_key = 'last_activity'";
923                }
924
925                // Sanitize 'order'.
926                $order = bp_esc_sql_order( $order );
927
928                /**
929                 * Filters the converted 'orderby' term.
930                 *
931                 * @since 2.1.0
932                 *
933                 * @param string $value   Converted 'orderby' term.
934                 * @param string $orderby Original orderby value.
935                 * @param string $value   Parsed 'type' value for the get method.
936                 */
937                $orderby = apply_filters( 'bp_groups_get_orderby_converted_by_term', self::convert_orderby_to_order_by_term( $orderby ), $orderby, $r['type'] );
938
939                // Random order is a special case.
940                if ( 'rand()' === $orderby ) {
941                        $sql['orderby'] = "ORDER BY rand()";
942                } else {
943                        $sql['orderby'] = "ORDER BY {$orderby} {$order}";
944                }
945
946                if ( ! empty( $r['per_page'] ) && ! empty( $r['page'] ) && $r['per_page'] != -1 ) {
947                        $sql['pagination'] = $wpdb->prepare( "LIMIT %d, %d", intval( ( $r['page'] - 1 ) * $r['per_page']), intval( $r['per_page'] ) );
948                }
949
950                $where = '';
951                if ( ! empty( $where_conditions ) ) {
952                        $sql['where'] = implode( ' AND ', $where_conditions );
953                        $where = "WHERE {$sql['where']}";
954                }
955
956                $paged_groups_sql = "{$sql['select']} FROM {$sql['from']} {$where} {$sql['orderby']} {$sql['pagination']}";
957
958                /**
959                 * Filters the pagination SQL statement.
960                 *
961                 * @since 1.5.0
962                 *
963                 * @param string $value Concatenated SQL statement.
964                 * @param array  $sql   Array of SQL parts before concatenation.
965                 * @param array  $r     Array of parsed arguments for the get method.
966                 */
967                $paged_groups_sql = apply_filters( 'bp_groups_get_paged_groups_sql', $paged_groups_sql, $sql, $r );
968                $paged_group_ids  = $wpdb->get_col( $paged_groups_sql );
969
970                $uncached_group_ids = bp_get_non_cached_ids( $paged_group_ids, 'bp_groups' );
971                if ( $uncached_group_ids ) {
972                        $group_ids_sql = implode( ',', array_map( 'intval', $uncached_group_ids ) );
973                        $group_data_objects = $wpdb->get_results( "SELECT g.* FROM {$bp->groups->table_name} g WHERE g.id IN ({$group_ids_sql})" );
974                        foreach ( $group_data_objects as $group_data_object ) {
975                                wp_cache_set( $group_data_object->id, $group_data_object, 'bp_groups' );
976                        }
977                }
978
979                $paged_groups = array();
980                foreach ( $paged_group_ids as $paged_group_id ) {
981                        $paged_groups[] = new BP_Groups_Group( $paged_group_id );
982                }
983
984                $total_groups_sql = "SELECT COUNT(DISTINCT g.id) FROM {$sql['from']} $where";
985
986                /**
987                 * Filters the SQL used to retrieve total group results.
988                 *
989                 * @since 1.5.0
990                 *
991                 * @param string $t_sql     Concatenated SQL statement used for retrieving total group results.
992                 * @param array  $total_sql Array of SQL parts for the query.
993                 * @param array  $r         Array of parsed arguments for the get method.
994                 */
995                $total_groups_sql = apply_filters( 'bp_groups_get_total_groups_sql', $total_groups_sql, $sql, $r );
996                $total_groups     = (int) $wpdb->get_var( $total_groups_sql );
997
998                $group_ids = array();
999                foreach ( (array) $paged_groups as $group ) {
1000                        $group_ids[] = $group->id;
1001                }
1002
1003                // Populate some extra information instead of querying each time in the loop.
1004                if ( !empty( $r['populate_extras'] ) ) {
1005                        $paged_groups = BP_Groups_Group::get_group_extras( $paged_groups, $group_ids, $r['type'] );
1006                }
1007
1008                // Grab all groupmeta.
1009                if ( ! empty( $r['update_meta_cache'] ) ) {
1010                        bp_groups_update_meta_cache( $group_ids );
1011                }
1012
1013                // Set up integer properties needing casting.
1014                $int_props = array(
1015                        'id', 'creator_id', 'enable_forum'
1016                );
1017
1018                // Integer casting.
1019                foreach ( $paged_groups as $key => $g ) {
1020                        foreach ( $int_props as $int_prop ) {
1021                                $paged_groups[ $key ]->{$int_prop} = (int) $paged_groups[ $key ]->{$int_prop};
1022                        }
1023                }
1024
1025                unset( $sql, $total_sql );
1026
1027                return array( 'groups' => $paged_groups, 'total' => $total_groups );
1028        }
1029
1030        /**
1031         * Get the SQL for the 'meta_query' param in BP_Activity_Activity::get()
1032         *
1033         * We use WP_Meta_Query to do the heavy lifting of parsing the
1034         * meta_query array and creating the necessary SQL clauses.
1035         *
1036         * @since 1.8.0
1037         *
1038         * @param array $meta_query An array of meta_query filters. See the
1039         *                          documentation for {@link WP_Meta_Query} for details.
1040         * @return array $sql_array 'join' and 'where' clauses.
1041         */
1042        protected static function get_meta_query_sql( $meta_query = array() ) {
1043                global $wpdb;
1044
1045                $sql_array = array(
1046                        'join'  => '',
1047                        'where' => '',
1048                );
1049
1050                if ( ! empty( $meta_query ) ) {
1051                        $groups_meta_query = new WP_Meta_Query( $meta_query );
1052
1053                        // WP_Meta_Query expects the table name at
1054                        // $wpdb->group.
1055                        $wpdb->groupmeta = buddypress()->groups->table_name_groupmeta;
1056
1057                        $meta_sql = $groups_meta_query->get_sql( 'group', 'g', 'id' );
1058                        $sql_array['join']  = $meta_sql['join'];
1059                        $sql_array['where'] = self::strip_leading_and( $meta_sql['where'] );
1060                }
1061
1062                return $sql_array;
1063        }
1064
1065        /**
1066         * Convert the 'type' parameter to 'order' and 'orderby'.
1067         *
1068         * @since 1.8.0
1069         *
1070         * @param string $type The 'type' shorthand param.
1071         *
1072         * @return array {
1073         *     @type string $order   SQL-friendly order string.
1074         *     @type string $orderby SQL-friendly orderby column name.
1075         * }
1076         */
1077        protected static function convert_type_to_order_orderby( $type = '' ) {
1078                $order = $orderby = '';
1079
1080                switch ( $type ) {
1081                        case 'newest' :
1082                                $order   = 'DESC';
1083                                $orderby = 'date_created';
1084                                break;
1085
1086                        case 'active' :
1087                                $order   = 'DESC';
1088                                $orderby = 'last_activity';
1089                                break;
1090
1091                        case 'popular' :
1092                                $order   = 'DESC';
1093                                $orderby = 'total_member_count';
1094                                break;
1095
1096                        case 'alphabetical' :
1097                                $order   = 'ASC';
1098                                $orderby = 'name';
1099                                break;
1100
1101                        case 'random' :
1102                                $order   = '';
1103                                $orderby = 'random';
1104                                break;
1105                }
1106
1107                return array( 'order' => $order, 'orderby' => $orderby );
1108        }
1109
1110        /**
1111         * Get a list of groups, sorted by those that have the most legacy forum topics.
1112         *
1113         * @since 1.6.0
1114         *
1115         * @param int|null          $limit           Optional. The max number of results to return.
1116         *                                           Default: null (no limit).
1117         * @param int|null          $page            Optional. The page offset of results to return.
1118         *                                           Default: null (no limit).
1119         * @param int               $user_id         Optional. If present, groups will be limited to
1120         *                                           those of which the specified user is a member.
1121         * @param string|bool       $search_terms    Optional. Limit groups to those whose name
1122         *                                           or description field contain the search string.
1123         * @param bool              $populate_extras Optional. Whether to fetch extra
1124         *                                           information about the groups. Default: true.
1125         * @param string|array|bool $exclude         Optional. Array or comma-separated list of group
1126         *                                           IDs to exclude from results.
1127         * @return array {
1128         *     @type array $groups Array of group objects returned by the
1129         *                         paginated query.
1130         *     @type int   $total  Total count of all groups matching non-
1131         *                         paginated query params.
1132         * }
1133         */
1134        public static function get_by_most_forum_topics( $limit = null, $page = null, $user_id = 0, $search_terms = false, $populate_extras = true, $exclude = false ) {
1135                global $wpdb, $bbdb;
1136
1137                if ( empty( $bbdb ) ) {
1138
1139                        /** This action is documented in bp-forums/bp-forums-screens */
1140                        do_action( 'bbpress_init' );
1141                }
1142
1143                if ( !empty( $limit ) && !empty( $page ) ) {
1144                        $pag_sql = $wpdb->prepare( " LIMIT %d, %d", intval( ( $page - 1 ) * $limit), intval( $limit ) );
1145                }
1146
1147                if ( !is_user_logged_in() || ( !bp_current_user_can( 'bp_moderate' ) && ( $user_id != bp_loggedin_user_id() ) ) )
1148                        $hidden_sql = " AND g.status != 'hidden'";
1149
1150                if ( !empty( $search_terms ) ) {
1151                        $search_terms_like = '%' . bp_esc_like( $search_terms ) . '%';
1152                        $search_sql        = $wpdb->prepare( ' AND ( g.name LIKE %s OR g.description LIKE %s ) ', $search_terms_like, $search_terms_like );
1153                }
1154
1155                if ( !empty( $exclude ) ) {
1156                        $exclude     = implode( ',', wp_parse_id_list( $exclude ) );
1157                        $exclude_sql = " AND g.id NOT IN ({$exclude})";
1158                }
1159
1160                $bp = buddypress();
1161
1162                if ( !empty( $user_id ) ) {
1163                        $user_id      = absint( esc_sql( $user_id ) );
1164                        $paged_groups = $wpdb->get_results( "SELECT DISTINCT g.*, gm1.meta_value as total_member_count, gm2.meta_value as last_activity FROM {$bp->groups->table_name_groupmeta} gm1, {$bp->groups->table_name_groupmeta} gm2, {$bp->groups->table_name_groupmeta} gm3, {$bp->groups->table_name_members} m, {$bbdb->forums} f, {$bp->groups->table_name} g WHERE g.id = m.group_id AND g.id = gm1.group_id AND g.id = gm2.group_id AND g.id = gm3.group_id AND gm2.meta_key = 'last_activity' AND gm1.meta_key = 'total_member_count' AND (gm3.meta_key = 'forum_id' AND gm3.meta_value = f.forum_id) AND f.topics > 0 {$hidden_sql} {$search_sql} AND m.user_id = {$user_id} AND m.is_confirmed = 1 AND m.is_banned = 0 {$exclude_sql} ORDER BY f.topics DESC {$pag_sql}" );
1165                        $total_groups = $wpdb->get_var( "SELECT COUNT(DISTINCT g.id) FROM {$bp->groups->table_name_groupmeta} gm1, {$bp->groups->table_name_groupmeta} gm2, {$bp->groups->table_name_groupmeta} gm3, {$bbdb->forums} f, {$bp->groups->table_name} g WHERE g.id = gm1.group_id AND g.id = gm2.group_id AND g.id = gm3.group_id AND gm2.meta_key = 'last_activity' AND gm1.meta_key = 'total_member_count' AND (gm3.meta_key = 'forum_id' AND gm3.meta_value = f.forum_id) AND f.topics > 0 {$hidden_sql} {$search_sql} AND m.user_id = {$user_id} AND m.is_confirmed = 1 AND m.is_banned = 0 {$exclude_sql}" );
1166                } else {
1167                        $paged_groups = $wpdb->get_results( "SELECT DISTINCT g.*, gm1.meta_value as total_member_count, gm2.meta_value as last_activity FROM {$bp->groups->table_name_groupmeta} gm1, {$bp->groups->table_name_groupmeta} gm2, {$bp->groups->table_name_groupmeta} gm3, {$bbdb->forums} f, {$bp->groups->table_name} g WHERE g.id = gm1.group_id AND g.id = gm2.group_id AND g.id = gm3.group_id AND gm2.meta_key = 'last_activity' AND gm1.meta_key = 'total_member_count' AND (gm3.meta_key = 'forum_id' AND gm3.meta_value = f.forum_id) AND f.topics > 0 {$hidden_sql} {$search_sql} {$exclude_sql} ORDER BY f.topics DESC {$pag_sql}" );
1168                        $total_groups = $wpdb->get_var( "SELECT COUNT(DISTINCT g.id) FROM {$bp->groups->table_name_groupmeta} gm1, {$bp->groups->table_name_groupmeta} gm2, {$bp->groups->table_name_groupmeta} gm3, {$bbdb->forums} f, {$bp->groups->table_name} g WHERE g.id = gm1.group_id AND g.id = gm2.group_id AND g.id = gm3.group_id AND gm2.meta_key = 'last_activity' AND gm1.meta_key = 'total_member_count' AND (gm3.meta_key = 'forum_id' AND gm3.meta_value = f.forum_id) AND f.topics > 0 {$hidden_sql} {$search_sql} {$exclude_sql}" );
1169                }
1170
1171                if ( !empty( $populate_extras ) ) {
1172                        foreach ( (array) $paged_groups as $group ) {
1173                                $group_ids[] = $group->id;
1174                        }
1175                        $paged_groups = BP_Groups_Group::get_group_extras( $paged_groups, $group_ids, 'newest' );
1176                }
1177
1178                return array( 'groups' => $paged_groups, 'total' => $total_groups );
1179        }
1180
1181        /**
1182         * Convert the 'orderby' param into a proper SQL term/column.
1183         *
1184         * @since 1.8.0
1185         *
1186         * @param string $orderby Orderby term as passed to get().
1187         * @return string $order_by_term SQL-friendly orderby term.
1188         */
1189        protected static function convert_orderby_to_order_by_term( $orderby ) {
1190                $order_by_term = '';
1191
1192                switch ( $orderby ) {
1193                        case 'date_created' :
1194                        default :
1195                                $order_by_term = 'g.date_created';
1196                                break;
1197
1198                        case 'last_activity' :
1199                                $order_by_term = 'gm_last_activity.meta_value';
1200                                break;
1201
1202                        case 'total_member_count' :
1203                                $order_by_term = 'CONVERT(gm_total_member_count.meta_value, SIGNED)';
1204                                break;
1205
1206                        case 'name' :
1207                                $order_by_term = 'g.name';
1208                                break;
1209
1210                        case 'random' :
1211                                $order_by_term = 'rand()';
1212                                break;
1213                }
1214
1215                return $order_by_term;
1216        }
1217
1218        /**
1219         * Get a list of groups, sorted by those that have the most legacy forum posts.
1220         *
1221         * @since 1.6.0
1222         *
1223         * @param int|null          $limit           Optional. The max number of results to return.
1224         *                                           Default: null (no limit).
1225         * @param int|null          $page            Optional. The page offset of results to return.
1226         *                                           Default: null (no limit).
1227         * @param string|bool       $search_terms    Optional. Limit groups to those whose name
1228         *                                           or description field contain the search string.
1229         * @param bool              $populate_extras Optional. Whether to fetch extra
1230         *                                           information about the groups. Default: true.
1231         * @param string|array|bool $exclude         Optional. Array or comma-separated list of group
1232         *                                           IDs to exclude from results.
1233         * @return array {
1234         *     @type array $groups Array of group objects returned by the
1235         *                         paginated query.
1236         *     @type int   $total  Total count of all groups matching non-
1237         *                         paginated query params.
1238         * }
1239         */
1240        public static function get_by_most_forum_posts( $limit = null, $page = null, $search_terms = false, $populate_extras = true, $exclude = false ) {
1241                global $wpdb, $bbdb;
1242
1243                if ( empty( $bbdb ) ) {
1244
1245                        /** This action is documented in bp-forums/bp-forums-screens */
1246                        do_action( 'bbpress_init' );
1247                }
1248
1249                if ( !empty( $limit ) && !empty( $page ) ) {
1250                        $pag_sql = $wpdb->prepare( " LIMIT %d, %d", intval( ( $page - 1 ) * $limit), intval( $limit ) );
1251                }
1252
1253                if ( !is_user_logged_in() || ( !bp_current_user_can( 'bp_moderate' ) && ( $user_id != bp_loggedin_user_id() ) ) )
1254                        $hidden_sql = " AND g.status != 'hidden'";
1255
1256                if ( !empty( $search_terms ) ) {
1257                        $search_terms_like = '%' . bp_esc_like( $search_terms ) . '%';
1258                        $search_sql        = $wpdb->prepare( ' AND ( g.name LIKE %s OR g.description LIKE %s ) ', $search_terms_like, $search_terms_like );
1259                }
1260
1261                if ( !empty( $exclude ) ) {
1262                        $exclude     = implode( ',', wp_parse_id_list( $exclude ) );
1263                        $exclude_sql = " AND g.id NOT IN ({$exclude})";
1264                }
1265
1266                $bp = buddypress();
1267
1268                if ( !empty( $user_id ) ) {
1269                        $user_id = esc_sql( $user_id );
1270                        $paged_groups = $wpdb->get_results( "SELECT DISTINCT g.*, gm1.meta_value as total_member_count, gm2.meta_value as last_activity FROM {$bp->groups->table_name_groupmeta} gm1, {$bp->groups->table_name_groupmeta} gm2, {$bp->groups->table_name_groupmeta} gm3, {$bp->groups->table_name_members} m, {$bbdb->forums} f, {$bp->groups->table_name} g WHERE g.id = m.group_id AND g.id = gm1.group_id AND g.id = gm2.group_id AND g.id = gm3.group_id AND gm2.meta_key = 'last_activity' AND gm1.meta_key = 'total_member_count' AND (gm3.meta_key = 'forum_id' AND gm3.meta_value = f.forum_id) {$hidden_sql} {$search_sql} AND m.user_id = {$user_id} AND m.is_confirmed = 1 AND m.is_banned = 0 {$exclude_sql} ORDER BY f.posts ASC {$pag_sql}" );
1271                        $total_groups = $wpdb->get_results( "SELECT COUNT(DISTINCT g.id) FROM {$bp->groups->table_name_groupmeta} gm1, {$bp->groups->table_name_groupmeta} gm2, {$bp->groups->table_name_groupmeta} gm3, {$bp->groups->table_name_members} m, {$bbdb->forums} f, {$bp->groups->table_name} g WHERE g.id = m.group_id AND g.id = gm1.group_id AND g.id = gm2.group_id AND g.id = gm3.group_id AND gm2.meta_key = 'last_activity' AND gm1.meta_key = 'total_member_count' AND (gm3.meta_key = 'forum_id' AND gm3.meta_value = f.forum_id) AND f.posts > 0 {$hidden_sql} {$search_sql} AND m.user_id = {$user_id} AND m.is_confirmed = 1 AND m.is_banned = 0 {$exclude_sql} " );
1272                } else {
1273                        $paged_groups = $wpdb->get_results( "SELECT DISTINCT g.*, gm1.meta_value as total_member_count, gm2.meta_value as last_activity FROM {$bp->groups->table_name_groupmeta} gm1, {$bp->groups->table_name_groupmeta} gm2, {$bp->groups->table_name_groupmeta} gm3, {$bbdb->forums} f, {$bp->groups->table_name} g WHERE g.id = gm1.group_id AND g.id = gm2.group_id AND g.id = gm3.group_id AND gm2.meta_key = 'last_activity' AND gm1.meta_key = 'total_member_count' AND (gm3.meta_key = 'forum_id' AND gm3.meta_value = f.forum_id) AND f.posts > 0 {$hidden_sql} {$search_sql} {$exclude_sql} ORDER BY f.posts ASC {$pag_sql}" );
1274                        $total_groups = $wpdb->get_var( "SELECT COUNT(DISTINCT g.id) FROM {$bp->groups->table_name_groupmeta} gm1, {$bp->groups->table_name_groupmeta} gm2, {$bp->groups->table_name_groupmeta} gm3, {$bbdb->forums} f, {$bp->groups->table_name} g WHERE g.id = gm1.group_id AND g.id = gm2.group_id AND g.id = gm3.group_id AND gm2.meta_key = 'last_activity' AND gm1.meta_key = 'total_member_count' AND (gm3.meta_key = 'forum_id' AND gm3.meta_value = f.forum_id) {$hidden_sql} {$search_sql} {$exclude_sql}" );
1275                }
1276
1277                if ( !empty( $populate_extras ) ) {
1278                        foreach ( (array) $paged_groups as $group ) {
1279                                $group_ids[] = $group->id;
1280                        }
1281                        $paged_groups = BP_Groups_Group::get_group_extras( $paged_groups, $group_ids, 'newest' );
1282                }
1283
1284                return array( 'groups' => $paged_groups, 'total' => $total_groups );
1285        }
1286
1287        /**
1288         * Get a list of groups whose names start with a given letter.
1289         *
1290         * @since 1.6.0
1291         *
1292         * @param string            $letter          The letter.
1293         * @param int|null          $limit           Optional. The max number of results to return.
1294         *                                           Default: null (no limit).
1295         * @param int|null          $page            Optional. The page offset of results to return.
1296         *                                           Default: null (no limit).
1297         * @param bool              $populate_extras Optional. Whether to fetch extra
1298         *                                           information about the groups. Default: true.
1299         * @param string|array|bool $exclude         Optional. Array or comma-separated list of group
1300         *                                           IDs to exclude from results.
1301         * @return false|array {
1302         *     @type array $groups Array of group objects returned by the
1303         *                         paginated query.
1304         *     @type int   $total  Total count of all groups matching non-
1305         *                         paginated query params.
1306         * }
1307         */
1308        public static function get_by_letter( $letter, $limit = null, $page = null, $populate_extras = true, $exclude = false ) {
1309                global $wpdb;
1310
1311                $pag_sql = $hidden_sql = $exclude_sql = '';
1312
1313                // Multibyte compliance.
1314                if ( function_exists( 'mb_strlen' ) ) {
1315                        if ( mb_strlen( $letter, 'UTF-8' ) > 1 || is_numeric( $letter ) || !$letter ) {
1316                                return false;
1317                        }
1318                } else {
1319                        if ( strlen( $letter ) > 1 || is_numeric( $letter ) || !$letter ) {
1320                                return false;
1321                        }
1322                }
1323
1324                $bp = buddypress();
1325
1326                if ( !empty( $exclude ) ) {
1327                        $exclude     = implode( ',', wp_parse_id_list( $exclude ) );
1328                        $exclude_sql = " AND g.id NOT IN ({$exclude})";
1329                }
1330
1331                if ( !bp_current_user_can( 'bp_moderate' ) )
1332                        $hidden_sql = " AND status != 'hidden'";
1333
1334                $letter_like = bp_esc_like( $letter ) . '%';
1335
1336                if ( !empty( $limit ) && !empty( $page ) ) {
1337                        $pag_sql      = $wpdb->prepare( " LIMIT %d, %d", intval( ( $page - 1 ) * $limit), intval( $limit ) );
1338                }
1339
1340                $total_groups = $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(DISTINCT g.id) FROM {$bp->groups->table_name_groupmeta} gm1, {$bp->groups->table_name_groupmeta} gm2, {$bp->groups->table_name} g WHERE g.id = gm1.group_id AND g.id = gm2.group_id AND gm2.meta_key = 'last_activity' AND gm1.meta_key = 'total_member_count' AND g.name LIKE %s {$hidden_sql} {$exclude_sql}", $letter_like ) );
1341
1342                $paged_groups = $wpdb->get_results( $wpdb->prepare( "SELECT g.*, gm1.meta_value as total_member_count, gm2.meta_value as last_activity FROM {$bp->groups->table_name_groupmeta} gm1, {$bp->groups->table_name_groupmeta} gm2, {$bp->groups->table_name} g WHERE g.id = gm1.group_id AND g.id = gm2.group_id AND gm2.meta_key = 'last_activity' AND gm1.meta_key = 'total_member_count' AND g.name LIKE %s {$hidden_sql} {$exclude_sql} ORDER BY g.name ASC {$pag_sql}", $letter_like ) );
1343
1344                if ( !empty( $populate_extras ) ) {
1345                        foreach ( (array) $paged_groups as $group ) {
1346                                $group_ids[] = $group->id;
1347                        }
1348                        $paged_groups = BP_Groups_Group::get_group_extras( $paged_groups, $group_ids, 'newest' );
1349                }
1350
1351                return array( 'groups' => $paged_groups, 'total' => $total_groups );
1352        }
1353
1354        /**
1355         * Get a list of random groups.
1356         *
1357         * Use BP_Groups_Group::get() with 'type' = 'random' instead.
1358         *
1359         * @since 1.6.0
1360         *
1361         * @param int|null          $limit           Optional. The max number of results to return.
1362         *                                           Default: null (no limit).
1363         * @param int|null          $page            Optional. The page offset of results to return.
1364         *                                           Default: null (no limit).
1365         * @param int               $user_id         Optional. If present, groups will be limited to
1366         *                                           those of which the specified user is a member.
1367         * @param string|bool       $search_terms    Optional. Limit groups to those whose name
1368         *                                           or description field contain the search string.
1369         * @param bool              $populate_extras Optional. Whether to fetch extra
1370         *                                           information about the groups. Default: true.
1371         * @param string|array|bool $exclude         Optional. Array or comma-separated list of group
1372         *                                           IDs to exclude from results.
1373         * @return array {
1374         *     @type array $groups Array of group objects returned by the
1375         *                         paginated query.
1376         *     @type int   $total  Total count of all groups matching non-
1377         *                         paginated query params.
1378         * }
1379         */
1380        public static function get_random( $limit = null, $page = null, $user_id = 0, $search_terms = false, $populate_extras = true, $exclude = false ) {
1381                global $wpdb;
1382
1383                $pag_sql = $hidden_sql = $search_sql = $exclude_sql = '';
1384
1385                if ( !empty( $limit ) && !empty( $page ) )
1386                        $pag_sql = $wpdb->prepare( " LIMIT %d, %d", intval( ( $page - 1 ) * $limit), intval( $limit ) );
1387
1388                if ( !is_user_logged_in() || ( !bp_current_user_can( 'bp_moderate' ) && ( $user_id != bp_loggedin_user_id() ) ) )
1389                        $hidden_sql = "AND g.status != 'hidden'";
1390
1391                if ( !empty( $search_terms ) ) {
1392                        $search_terms_like = '%' . bp_esc_like( $search_terms ) . '%';
1393                        $search_sql = $wpdb->prepare( " AND ( g.name LIKE %s OR g.description LIKE %s )", $search_terms_like, $search_terms_like );
1394                }
1395
1396                if ( !empty( $exclude ) ) {
1397                        $exclude     = wp_parse_id_list( $exclude );
1398                        $exclude     = esc_sql( implode( ',', $exclude ) );
1399                        $exclude_sql = " AND g.id NOT IN ({$exclude})";
1400                }
1401
1402                $bp = buddypress();
1403
1404                if ( !empty( $user_id ) ) {
1405                        $user_id = esc_sql( $user_id );
1406                        $paged_groups = $wpdb->get_results( "SELECT g.*, gm1.meta_value as total_member_count, gm2.meta_value as last_activity FROM {$bp->groups->table_name_groupmeta} gm1, {$bp->groups->table_name_groupmeta} gm2, {$bp->groups->table_name_members} m, {$bp->groups->table_name} g WHERE g.id = m.group_id AND g.id = gm1.group_id AND g.id = gm2.group_id AND gm2.meta_key = 'last_activity' AND gm1.meta_key = 'total_member_count' {$hidden_sql} {$search_sql} AND m.user_id = {$user_id} AND m.is_confirmed = 1 AND m.is_banned = 0 {$exclude_sql} ORDER BY rand() {$pag_sql}" );
1407                        $total_groups = $wpdb->get_var( "SELECT COUNT(DISTINCT m.group_id) FROM {$bp->groups->table_name_members} m LEFT JOIN {$bp->groups->table_name_groupmeta} gm ON m.group_id = gm.group_id INNER JOIN {$bp->groups->table_name} g ON m.group_id = g.id WHERE gm.meta_key = 'last_activity'{$hidden_sql} {$search_sql} AND m.user_id = {$user_id} AND m.is_confirmed = 1 AND m.is_banned = 0 {$exclude_sql}" );
1408                } else {
1409                        $paged_groups = $wpdb->get_results( "SELECT g.*, gm1.meta_value as total_member_count, gm2.meta_value as last_activity FROM {$bp->groups->table_name_groupmeta} gm1, {$bp->groups->table_name_groupmeta} gm2, {$bp->groups->table_name} g WHERE g.id = gm1.group_id AND g.id = gm2.group_id AND gm2.meta_key = 'last_activity' AND gm1.meta_key = 'total_member_count' {$hidden_sql} {$search_sql} {$exclude_sql} ORDER BY rand() {$pag_sql}" );
1410                        $total_groups = $wpdb->get_var( "SELECT COUNT(DISTINCT g.id) FROM {$bp->groups->table_name_groupmeta} gm INNER JOIN {$bp->groups->table_name} g ON gm.group_id = g.id WHERE gm.meta_key = 'last_activity'{$hidden_sql} {$search_sql} {$exclude_sql}" );
1411                }
1412
1413                if ( !empty( $populate_extras ) ) {
1414                        foreach ( (array) $paged_groups as $group ) {
1415                                $group_ids[] = $group->id;
1416                        }
1417                        $paged_groups = BP_Groups_Group::get_group_extras( $paged_groups, $group_ids, 'newest' );
1418                }
1419
1420                return array( 'groups' => $paged_groups, 'total' => $total_groups );
1421        }
1422
1423        /**
1424         * Fetch extra data for a list of groups.
1425         *
1426         * This method is used throughout the class, by methods that take a
1427         * $populate_extras parameter.
1428         *
1429         * Data fetched:
1430         *     - Logged-in user's status within each group (is_member,
1431         *       is_confirmed, is_pending, is_banned)
1432         *
1433         * @since 1.6.0
1434         *
1435         * @param array        $paged_groups Array of groups.
1436         * @param string|array $group_ids    Array or comma-separated list of IDs matching
1437         *                                   $paged_groups.
1438         * @param string|bool  $type         Not used.
1439         * @return array $paged_groups
1440         */
1441        public static function get_group_extras( &$paged_groups, &$group_ids, $type = false ) {
1442                $user_id = bp_loggedin_user_id();
1443
1444                foreach ( $paged_groups as &$group ) {
1445                        $group->is_member  = groups_is_user_member( $user_id, $group->id )  ? 1 : 0;
1446                        $group->is_invited = groups_is_user_invited( $user_id, $group->id ) ? 1 : 0;
1447                        $group->is_pending = groups_is_user_pending( $user_id, $group->id ) ? 1 : 0;
1448                        $group->is_banned  = (bool) groups_is_user_banned( $user_id, $group->id );
1449                }
1450
1451                return $paged_groups;
1452        }
1453
1454        /**
1455         * Delete all invitations to a given group.
1456         *
1457         * @since 1.6.0
1458         *
1459         * @param int $group_id ID of the group whose invitations are being deleted.
1460         * @return int|null Number of rows records deleted on success, null on
1461         *                  failure.
1462         */
1463        public static function delete_all_invites( $group_id ) {
1464                global $wpdb;
1465
1466                $bp = buddypress();
1467
1468                return $wpdb->query( $wpdb->prepare( "DELETE FROM {$bp->groups->table_name_members} WHERE group_id = %d AND invite_sent = 1", $group_id ) );
1469        }
1470
1471        /**
1472         * Get a total group count for the site.
1473         *
1474         * Will include hidden groups in the count only if
1475         * current_user_can( 'bp_moderate' ).
1476         *
1477         * @since 1.6.0
1478         *
1479         * @return int Group count.
1480         */
1481        public static function get_total_group_count() {
1482                global $wpdb;
1483
1484                $hidden_sql = '';
1485                if ( !bp_current_user_can( 'bp_moderate' ) )
1486                        $hidden_sql = "WHERE status != 'hidden'";
1487
1488                $bp = buddypress();
1489
1490                return $wpdb->get_var( "SELECT COUNT(id) FROM {$bp->groups->table_name} {$hidden_sql}" );
1491        }
1492
1493        /**
1494         * Get global count of forum topics in public groups (legacy forums).
1495         *
1496         * @since 1.6.0
1497         *
1498         * @param string $type Optional. If 'unreplied', count will be limited to
1499         *                     those topics that have received no replies.
1500         * @return int Forum topic count.
1501         */
1502        public static function get_global_forum_topic_count( $type ) {
1503                global $bbdb, $wpdb;
1504
1505                $bp = buddypress();
1506
1507                if ( 'unreplied' == $type )
1508                        $bp->groups->filter_sql = ' AND t.topic_posts = 1';
1509
1510                /**
1511                 * Filters the portion of the SQL related to global count of forum topics in public groups.
1512                 *
1513                 * See https://buddypress.trac.wordpress.org/ticket/4306.
1514                 *
1515                 * @since 1.6.0
1516                 *
1517                 * @param string $filter_sql SQL portion for the query.
1518                 * @param string $type       Type of forum topics to query for.
1519                 */
1520                $extra_sql = apply_filters( 'get_global_forum_topic_count_extra_sql', $bp->groups->filter_sql, $type );
1521
1522                // Make sure the $extra_sql begins with an AND.
1523                if ( 'AND' != substr( trim( strtoupper( $extra_sql ) ), 0, 3 ) )
1524                        $extra_sql = ' AND ' . $extra_sql;
1525
1526                return $wpdb->get_var( "SELECT COUNT(t.topic_id) FROM {$bbdb->topics} AS t, {$bp->groups->table_name} AS g LEFT JOIN {$bp->groups->table_name_groupmeta} AS gm ON g.id = gm.group_id WHERE (gm.meta_key = 'forum_id' AND gm.meta_value = t.forum_id) AND g.status = 'public' AND t.topic_status = '0' AND t.topic_sticky != '2' {$extra_sql} " );
1527        }
1528
1529        /**
1530         * Get the member count for a group.
1531         *
1532         * @since 1.6.0
1533         *
1534         * @param int $group_id Group ID.
1535         * @return int Count of confirmed members for the group.
1536         */
1537        public static function get_total_member_count( $group_id ) {
1538                global $wpdb;
1539
1540                $bp = buddypress();
1541
1542                return $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(id) FROM {$bp->groups->table_name_members} WHERE group_id = %d AND is_confirmed = 1 AND is_banned = 0", $group_id ) );
1543        }
1544
1545        /**
1546         * Get a total count of all topics of a given status, across groups/forums.
1547         *
1548         * @since 1.5.0
1549         *
1550         * @param string      $status       Which group type to count. 'public', 'private',
1551         *                                  'hidden', or 'all'. Default: 'public'.
1552         * @param string|bool $search_terms Provided search terms.
1553         * @return int The topic count
1554         */
1555        public static function get_global_topic_count( $status = 'public', $search_terms = false ) {
1556                global $bbdb, $wpdb;
1557
1558                switch ( $status ) {
1559                        case 'all' :
1560                                $status_sql = '';
1561                                break;
1562
1563                        case 'hidden' :
1564                                $status_sql = "AND g.status = 'hidden'";
1565                                break;
1566
1567                        case 'private' :
1568                                $status_sql = "AND g.status = 'private'";
1569                                break;
1570
1571                        case 'public' :
1572                        default :
1573                                $status_sql = "AND g.status = 'public'";
1574                                break;
1575                }
1576
1577                $bp = buddypress();
1578
1579                $sql = array();
1580
1581                $sql['select'] = "SELECT COUNT(t.topic_id)";
1582                $sql['from']   = "FROM {$bbdb->topics} AS t INNER JOIN {$bp->groups->table_name_groupmeta} AS gm ON t.forum_id = gm.meta_value INNER JOIN {$bp->groups->table_name} AS g ON gm.group_id = g.id";
1583                $sql['where']  = "WHERE gm.meta_key = 'forum_id' {$status_sql} AND t.topic_status = '0' AND t.topic_sticky != '2'";
1584
1585                if ( !empty( $search_terms ) ) {
1586                        $search_terms_like = '%' . bp_esc_like( $search_terms ) . '%';
1587                        $sql['where'] .= $wpdb->prepare( " AND ( t.topic_title LIKE %s )", $search_terms_like );
1588                }
1589
1590                return $wpdb->get_var( implode( ' ', $sql ) );
1591        }
1592
1593        /**
1594         * Get an array containing ids for each group type.
1595         *
1596         * A bit of a kludge workaround for some issues
1597         * with bp_has_groups().
1598         *
1599         * @since 1.7.0
1600         *
1601         * @return array
1602         */
1603        public static function get_group_type_ids() {
1604                global $wpdb;
1605
1606                $bp  = buddypress();
1607                $ids = array();
1608
1609                $ids['all']     = $wpdb->get_col( "SELECT id FROM {$bp->groups->table_name}" );
1610                $ids['public']  = $wpdb->get_col( "SELECT id FROM {$bp->groups->table_name} WHERE status = 'public'" );
1611                $ids['private'] = $wpdb->get_col( "SELECT id FROM {$bp->groups->table_name} WHERE status = 'private'" );
1612                $ids['hidden']  = $wpdb->get_col( "SELECT id FROM {$bp->groups->table_name} WHERE status = 'hidden'" );
1613
1614                return $ids;
1615        }
1616
1617        /**
1618         * Get SQL clause for group type(s).
1619         *
1620         * @since 2.6.0
1621         *
1622         * @param  string|array $group_types Group type(s).
1623         * @param  string       $operator    'IN' or 'NOT IN'.
1624         * @return string       $clause      SQL clause.
1625         */
1626        protected static function get_sql_clause_for_group_types( $group_types, $operator ) {
1627                global $wpdb;
1628
1629                // Sanitize operator.
1630                if ( 'NOT IN' !== $operator ) {
1631                        $operator = 'IN';
1632                }
1633
1634                // Parse and sanitize types.
1635                if ( ! is_array( $group_types ) ) {
1636                        $group_types = preg_split( '/[,\s+]/', $group_types );
1637                }
1638
1639                $types = array();
1640                foreach ( $group_types as $gt ) {
1641                        if ( bp_groups_get_group_type_object( $gt ) ) {
1642                                $types[] = $gt;
1643                        }
1644                }
1645
1646                $tax_query = new WP_Tax_Query( array(
1647                        array(
1648                                'taxonomy' => 'bp_group_type',
1649                                'field'    => 'name',
1650                                'operator' => $operator,
1651                                'terms'    => $types,
1652                        ),
1653                ) );
1654
1655                $site_id  = bp_get_taxonomy_term_site_id( 'bp_group_type' );
1656                $switched = false;
1657                if ( $site_id !== get_current_blog_id() ) {
1658                        switch_to_blog( $site_id );
1659                        $switched = true;
1660                }
1661
1662                $sql_clauses = $tax_query->get_sql( 'g', 'id' );
1663
1664                $clause = '';
1665
1666                // The no_results clauses are the same between IN and NOT IN.
1667                if ( false !== strpos( $sql_clauses['where'], '0 = 1' ) ) {
1668                        $clause = self::strip_leading_and( $sql_clauses['where'] );
1669
1670                // The tax_query clause generated for NOT IN can be used almost as-is.
1671                } elseif ( 'NOT IN' === $operator ) {
1672                        $clause = self::strip_leading_and( $sql_clauses['where'] );
1673
1674                // IN clauses must be converted to a subquery.
1675                } elseif ( preg_match( '/' . $wpdb->term_relationships . '\.term_taxonomy_id IN \([0-9, ]+\)/', $sql_clauses['where'], $matches ) ) {
1676                        $clause = " g.id IN ( SELECT object_id FROM $wpdb->term_relationships WHERE {$matches[0]} )";
1677                }
1678
1679                if ( $switched ) {
1680                        restore_current_blog();
1681                }
1682
1683                return $clause;
1684        }
1685
1686        /**
1687         * Strips the leading AND and any surrounding whitespace from a string.
1688         *
1689         * Used here to normalize SQL fragments generated by `WP_Meta_Query` and
1690         * other utility classes.
1691         *
1692         * @since 2.7.0
1693         *
1694         * @param string $s String.
1695         * @return string
1696         */
1697        protected static function strip_leading_and( $s ) {
1698                return preg_replace( '/^\s*AND\s*/', '', $s );
1699        }
1700}
Note: See TracBrowser for help on using the repository browser.