Skip to:
Content

BuddyPress.org


Ignore:
Timestamp:
02/15/2015 12:48:56 AM (11 years ago)
Author:
djpaul
Message:

Split each component's classes file, and move each individual class into its own file.

While historically manageable, the previous approach of having the majority of each component's classes in the same file is growing unwieldly with each new version of BuddyPress, and causing an avoidable increase in code complexity.

The existing -classes.php files are now a loading wrapper for the components' classes.

This change only affect classes that were explicitly declared within the -classes.php files. Any other classes, for example those in some components' template functions files, remain untouched for now.

Fixes #6083

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/bp-groups/bp-groups-classes.php

    r9471 r9485  
    11<?php
    2 
    32/**
    43 * BuddyPress Groups Classes
     
    1110defined( 'ABSPATH' ) || exit;
    1211
    13 /**
    14  * BuddyPress Group object.
    15  */
    16 class BP_Groups_Group {
    17 
    18         /**
    19          * ID of the group.
    20          *
    21          * @access public
    22          * @var int
    23          */
    24         public $id;
    25 
    26         /**
    27          * User ID of the group's creator.
    28          *
    29          * @access public
    30          * @var int
    31          */
    32         public $creator_id;
    33 
    34         /**
    35          * Name of the group.
    36          *
    37          * @access public
    38          * @var string
    39          */
    40         public $name;
    41 
    42         /**
    43          * Group slug.
    44          *
    45          * @access public
    46          * @var string
    47          */
    48         public $slug;
    49 
    50         /**
    51          * Group description.
    52          *
    53          * @access public
    54          * @var string
    55          */
    56         public $description;
    57 
    58         /**
    59          * Group status.
    60          *
    61          * Core statuses are 'public', 'private', and 'hidden'.
    62          *
    63          * @access public
    64          * @var string
    65          */
    66         public $status;
    67 
    68         /**
    69          * Should (legacy) bbPress forums be enabled for this group?
    70          *
    71          * @access public
    72          * @var int
    73          */
    74         public $enable_forum;
    75 
    76         /**
    77          * Date the group was created.
    78          *
    79          * @access public
    80          * @var string
    81          */
    82         public $date_created;
    83 
    84         /**
    85          * Data about the group's admins.
    86          *
    87          * @access public
    88          * @var array
    89          */
    90         public $admins;
    91 
    92         /**
    93          * Data about the group's moderators.
    94          *
    95          * @access public
    96          * @var array
    97          */
    98         public $mods;
    99 
    100         /**
    101          * Total count of group members.
    102          *
    103          * @access public
    104          * @var int
    105          */
    106         public $total_member_count;
    107 
    108         /**
    109          * Is the current user a member of this group?
    110          *
    111          * @since BuddyPress (1.2.0)
    112          * @var bool
    113          */
    114         public $is_member;
    115 
    116         /**
    117          * Does the current user have an outstanding invitation to this group?
    118          *
    119          * @since BuddyPress (1.9.0)
    120          * @var bool
    121          */
    122         public $is_invited;
    123 
    124         /**
    125          * Does the current user have a pending membership request to this group?
    126          *
    127          * @since BuddyPress (1.9.0)
    128          * @var bool
    129          */
    130         public $is_pending;
    131 
    132         /**
    133          * Timestamp of the last activity that happened in this group.
    134          *
    135          * @since BuddyPress (1.2.0)
    136          * @var string
    137          */
    138         public $last_activity;
    139 
    140         /**
    141          * If this is a private or hidden group, does the current user have access?
    142          *
    143          * @since BuddyPress (1.6.0)
    144          * @var bool
    145          */
    146         public $user_has_access;
    147 
    148         /**
    149          * Raw arguments passed to the constructor.
    150          *
    151          * @since BuddyPress (2.0.0)
    152          * @var array
    153          */
    154         public $args;
    155 
    156         /**
    157          * Constructor method.
    158          *
    159          * @param int $id Optional. If the ID of an existing group is provided,
    160          *        the object will be pre-populated with info about that group.
    161          * @param array $args {
    162          *     Array of optional arguments.
    163          *     @type bool $populate_extras Whether to fetch "extra" data about
    164          *           the group (group admins/mods, access for the current user).
    165          *           Default: false.
    166          * }
    167          */
    168         public function __construct( $id = null, $args = array() ) {
    169                 $this->args = wp_parse_args( $args, array(
    170                         'populate_extras' => false,
    171                 ) );
    172 
    173                 if ( !empty( $id ) ) {
    174                         $this->id = $id;
    175                         $this->populate();
    176                 }
    177         }
    178 
    179         /**
    180          * Set up data about the current group.
    181          */
    182         public function populate() {
    183                 global $wpdb;
    184 
    185                 // Get BuddyPress
    186                 $bp    = buddypress();
    187 
    188                 // Check cache for group data
    189                 $group = wp_cache_get( $this->id, 'bp_groups' );
    190 
    191                 // Cache missed, so query the DB
    192                 if ( false === $group ) {
    193                         $group = $wpdb->get_row( $wpdb->prepare( "SELECT g.* FROM {$bp->groups->table_name} g WHERE g.id = %d", $this->id ) );
    194 
    195                         wp_cache_set( $this->id, $group, 'bp_groups' );
    196                 }
    197 
    198                 // No group found so set the ID and bail
    199                 if ( empty( $group ) || is_wp_error( $group ) ) {
    200                         $this->id = 0;
    201                         return;
    202                 }
    203 
    204                 // Group found so setup the object variables
    205                 $this->id           = $group->id;
    206                 $this->creator_id   = $group->creator_id;
    207                 $this->name         = stripslashes( $group->name );
    208                 $this->slug         = $group->slug;
    209                 $this->description  = stripslashes( $group->description );
    210                 $this->status       = $group->status;
    211                 $this->enable_forum = $group->enable_forum;
    212                 $this->date_created = $group->date_created;
    213 
    214                 // Are we getting extra group data?
    215                 if ( ! empty( $this->args['populate_extras'] ) ) {
    216 
    217                         // Get group admins and mods
    218                         $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 ) ) );
    219 
    220                         // Add admins and moderators to their respective arrays
    221                         foreach ( (array) $admin_mods as $user ) {
    222                                 if ( !empty( $user->is_admin ) ) {
    223                                         $this->admins[] = $user;
    224                                 } else {
    225                                         $this->mods[] = $user;
    226                                 }
    227                         }
    228 
    229                         // Set up some specific group vars from meta. Excluded
    230                         // from the bp_groups cache because it's cached independently
    231                         $this->last_activity      = groups_get_groupmeta( $this->id, 'last_activity' );
    232                         $this->total_member_count = groups_get_groupmeta( $this->id, 'total_member_count' );
    233 
    234                         // Set user-specific data
    235                         $user_id          = bp_loggedin_user_id();
    236                         $this->is_member  = BP_Groups_Member::check_is_member( $user_id, $this->id );
    237                         $this->is_invited = BP_Groups_Member::check_has_invite( $user_id, $this->id );
    238                         $this->is_pending = BP_Groups_Member::check_for_membership_request( $user_id, $this->id );
    239 
    240                         // If this is a private or hidden group, does the current user have access?
    241                         if ( ( 'private' === $this->status ) || ( 'hidden' === $this->status ) ) {
    242 
    243                                 // Assume user does not have access to hidden/private groups
    244                                 $this->user_has_access = false;
    245 
    246                                 // Group members or community moderators have access
    247                                 if ( ( $this->is_member && is_user_logged_in() ) || bp_current_user_can( 'bp_moderate' ) ) {
    248                                         $this->user_has_access = true;
    249                                 }
    250                         } else {
    251                                 $this->user_has_access = true;
    252                         }
    253                 }
    254         }
    255 
    256         /**
    257          * Save the current group to the database.
    258          *
    259          * @return bool True on success, false on failure.
    260          */
    261         public function save() {
    262                 global $wpdb;
    263 
    264                 $bp = buddypress();
    265 
    266                 $this->creator_id   = apply_filters( 'groups_group_creator_id_before_save',   $this->creator_id,   $this->id );
    267                 $this->name         = apply_filters( 'groups_group_name_before_save',         $this->name,         $this->id );
    268                 $this->slug         = apply_filters( 'groups_group_slug_before_save',         $this->slug,         $this->id );
    269                 $this->description  = apply_filters( 'groups_group_description_before_save',  $this->description,  $this->id );
    270                 $this->status       = apply_filters( 'groups_group_status_before_save',       $this->status,       $this->id );
    271                 $this->enable_forum = apply_filters( 'groups_group_enable_forum_before_save', $this->enable_forum, $this->id );
    272                 $this->date_created = apply_filters( 'groups_group_date_created_before_save', $this->date_created, $this->id );
    273 
    274                 do_action_ref_array( 'groups_group_before_save', array( &$this ) );
    275 
    276                 // Groups need at least a name
    277                 if ( empty( $this->name ) ) {
    278                         return false;
    279                 }
    280 
    281                 // Set slug with group title if not passed
    282                 if ( empty( $this->slug ) ) {
    283                         $this->slug = sanitize_title( $this->name );
    284                 }
    285 
    286                 // Sanity check
    287                 if ( empty( $this->slug ) ) {
    288                         return false;
    289                 }
    290 
    291                 // Check for slug conflicts if creating new group
    292                 if ( empty( $this->id ) ) {
    293                         $this->slug = groups_check_slug( $this->slug );
    294                 }
    295 
    296                 if ( !empty( $this->id ) ) {
    297                         $sql = $wpdb->prepare(
    298                                 "UPDATE {$bp->groups->table_name} SET
    299                                         creator_id = %d,
    300                                         name = %s,
    301                                         slug = %s,
    302                                         description = %s,
    303                                         status = %s,
    304                                         enable_forum = %d,
    305                                         date_created = %s
    306                                 WHERE
    307                                         id = %d
    308                                 ",
    309                                         $this->creator_id,
    310                                         $this->name,
    311                                         $this->slug,
    312                                         $this->description,
    313                                         $this->status,
    314                                         $this->enable_forum,
    315                                         $this->date_created,
    316                                         $this->id
    317                         );
    318                 } else {
    319                         $sql = $wpdb->prepare(
    320                                 "INSERT INTO {$bp->groups->table_name} (
    321                                         creator_id,
    322                                         name,
    323                                         slug,
    324                                         description,
    325                                         status,
    326                                         enable_forum,
    327                                         date_created
    328                                 ) VALUES (
    329                                         %d, %s, %s, %s, %s, %d, %s
    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                         );
    339                 }
    340 
    341                 if ( false === $wpdb->query($sql) )
    342                         return false;
    343 
    344                 if ( empty( $this->id ) )
    345                         $this->id = $wpdb->insert_id;
    346 
    347                 do_action_ref_array( 'groups_group_after_save', array( &$this ) );
    348 
    349                 wp_cache_delete( $this->id, 'bp_groups' );
    350 
    351                 return true;
    352         }
    353 
    354         /**
    355          * Delete the current group.
    356          *
    357          * @return bool True on success, false on failure.
    358          */
    359         public function delete() {
    360                 global $wpdb;
    361 
    362                 // Delete groupmeta for the group
    363                 groups_delete_groupmeta( $this->id );
    364 
    365                 // Fetch the user IDs of all the members of the group
    366                 $user_ids    = BP_Groups_Member::get_group_member_ids( $this->id );
    367                 $user_id_str = esc_sql( implode( ',', wp_parse_id_list( $user_ids ) ) );
    368 
    369                 // Modify group count usermeta for members
    370                 $wpdb->query( "UPDATE {$wpdb->usermeta} SET meta_value = meta_value - 1 WHERE meta_key = 'total_group_count' AND user_id IN ( {$user_id_str} )" );
    371 
    372                 // Now delete all group member entries
    373                 BP_Groups_Member::delete_all( $this->id );
    374 
    375                 do_action_ref_array( 'bp_groups_delete_group', array( &$this, $user_ids ) );
    376 
    377                 wp_cache_delete( $this->id, 'bp_groups' );
    378 
    379                 $bp = buddypress();
    380 
    381                 // Finally remove the group entry from the DB
    382                 if ( !$wpdb->query( $wpdb->prepare( "DELETE FROM {$bp->groups->table_name} WHERE id = %d", $this->id ) ) )
    383                         return false;
    384 
    385                 return true;
    386         }
    387 
    388         /** Static Methods ****************************************************/
    389 
    390         /**
    391          * Get whether a group exists for a given slug.
    392          *
    393          * @param string $slug Slug to check.
    394          * @param string $table_name Optional. Name of the table to check
    395          *        against. Default: $bp->groups->table_name.
    396          * @return string|null ID of the group, if one is found, else null.
    397          */
    398         public static function group_exists( $slug, $table_name = false ) {
    399                 global $wpdb;
    400 
    401                 if ( empty( $table_name ) )
    402                         $table_name = buddypress()->groups->table_name;
    403 
    404                 if ( empty( $slug ) )
    405                         return false;
    406 
    407                 return $wpdb->get_var( $wpdb->prepare( "SELECT id FROM {$table_name} WHERE slug = %s", strtolower( $slug ) ) );
    408         }
    409 
    410         /**
    411          * Get the ID of a group by the group's slug.
    412          *
    413          * Alias of {@link BP_Groups_Group::group_exists()}.
    414          *
    415          * @param string $slug See {@link BP_Groups_Group::group_exists()}.
    416          * @return string|null See {@link BP_Groups_Group::group_exists()}.
    417          */
    418         public static function get_id_from_slug( $slug ) {
    419                 return BP_Groups_Group::group_exists( $slug );
    420         }
    421 
    422         /**
    423          * Get IDs of users with outstanding invites to a given group from a specified user.
    424          *
    425          * @param int $user_id ID of the inviting user.
    426          * @param int $group_id ID of the group.
    427          * @return array IDs of users who have been invited to the group by the
    428          *         user but have not yet accepted.
    429          */
    430         public static function get_invites( $user_id, $group_id ) {
    431                 global $wpdb;
    432 
    433                 $bp = buddypress();
    434 
    435                 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 ) );
    436         }
    437 
    438         /**
    439          * Get a list of a user's groups, filtered by a search string.
    440          *
    441          * @param string $filter Search term. Matches against 'name' and
    442          *        'description' fields.
    443          * @param int $user_id ID of the user whose groups are being searched.
    444          *        Default: the displayed user.
    445          * @param mixed $order Not used.
    446          * @param int $limit Optional. The max number of results to return.
    447          *        Default: null (no limit).
    448          * @param int $page Optional. The page offset of results to return.
    449          *        Default: null (no limit).
    450          * @return array {
    451          *     @type array $groups Array of matched and paginated group objects.
    452          *     @type int $total Total count of groups matching the query.
    453          * }
    454          */
    455         public static function filter_user_groups( $filter, $user_id = 0, $order = false, $limit = null, $page = null ) {
    456                 global $wpdb;
    457 
    458                 if ( empty( $user_id ) )
    459                         $user_id = bp_displayed_user_id();
    460 
    461                 $search_terms_like = bp_esc_like( $filter ) . '%';
    462 
    463                 $pag_sql = $order_sql = $hidden_sql = '';
    464 
    465                 if ( !empty( $limit ) && !empty( $page ) )
    466                         $pag_sql = $wpdb->prepare( " LIMIT %d, %d", intval( ( $page - 1 ) * $limit), intval( $limit ) );
    467 
    468                 // Get all the group ids for the current user's groups.
    469                 $gids = BP_Groups_Member::get_group_ids( $user_id );
    470 
    471                 if ( empty( $gids['groups'] ) )
    472                         return false;
    473 
    474                 $bp = buddypress();
    475 
    476                 $gids = esc_sql( implode( ',', wp_parse_id_list( $gids['groups'] ) ) );
    477 
    478                 $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 ) );
    479                 $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 ) );
    480 
    481                 return array( 'groups' => $paged_groups, 'total' => $total_groups );
    482         }
    483 
    484         /**
    485          * Get a list of groups, filtered by a search string.
    486          *
    487          * @param string $filter Search term. Matches against 'name' and
    488          *        'description' fields.
    489          * @param int $limit Optional. The max number of results to return.
    490          *        Default: null (no limit).
    491          * @param int $page Optional. The page offset of results to return.
    492          *        Default: null (no limit).
    493          * @param string $sort_by Column to sort by. Default: false (default
    494          *        sort).
    495          * @param string $order ASC or DESC. Default: false (default sort).
    496          * @return array {
    497          *     @type array $groups Array of matched and paginated group objects.
    498          *     @type int $total Total count of groups matching the query.
    499          * }
    500          */
    501         public static function search_groups( $filter, $limit = null, $page = null, $sort_by = false, $order = false ) {
    502                 global $wpdb;
    503 
    504                 $search_terms_like = '%' . bp_esc_like( $filter ) . '%';
    505 
    506                 $pag_sql = $order_sql = $hidden_sql = '';
    507 
    508                 if ( !empty( $limit ) && !empty( $page ) )
    509                         $pag_sql = $wpdb->prepare( " LIMIT %d, %d", intval( ( $page - 1 ) * $limit), intval( $limit ) );
    510 
    511                 if ( !empty( $sort_by ) && !empty( $order ) ) {
    512                         $sort_by   = esc_sql( $sort_by );
    513                         $order     = esc_sql( $order );
    514                         $order_sql = "ORDER BY {$sort_by} {$order}";
    515                 }
    516 
    517                 if ( !bp_current_user_can( 'bp_moderate' ) )
    518                         $hidden_sql = "AND status != 'hidden'";
    519 
    520                 $bp = buddypress();
    521 
    522                 $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 ) );
    523                 $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 ) );
    524 
    525                 return array( 'groups' => $paged_groups, 'total' => $total_groups );
    526         }
    527 
    528         /**
    529          * Check for the existence of a slug.
    530          *
    531          * @param string $slug Slug to check.
    532          * @return string|null The slug, if found. Otherwise null.
    533          */
    534         public static function check_slug( $slug ) {
    535                 global $wpdb;
    536 
    537                 $bp = buddypress();
    538 
    539                 return $wpdb->get_var( $wpdb->prepare( "SELECT slug FROM {$bp->groups->table_name} WHERE slug = %s", $slug ) );
    540         }
    541 
    542         /**
    543          * Get the slug for a given group ID.
    544          *
    545          * @param int $group_id ID of the group.
    546          * @return string|null The slug, if found. Otherwise null.
    547          */
    548         public static function get_slug( $group_id ) {
    549                 global $wpdb;
    550 
    551                 $bp = buddypress();
    552 
    553                 return $wpdb->get_var( $wpdb->prepare( "SELECT slug FROM {$bp->groups->table_name} WHERE id = %d", $group_id ) );
    554         }
    555 
    556         /**
    557          * Check whether a given group has any members.
    558          *
    559          * @param int $group_id ID of the group.
    560          * @return bool True if the group has members, otherwise false.
    561          */
    562         public static function has_members( $group_id ) {
    563                 global $wpdb;
    564 
    565                 $bp = buddypress();
    566 
    567                 $members = $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(id) FROM {$bp->groups->table_name_members} WHERE group_id = %d", $group_id ) );
    568 
    569                 if ( empty( $members ) )
    570                         return false;
    571 
    572                 return true;
    573         }
    574 
    575         /**
    576          * Check whether a group has outstanding membership requests.
    577          *
    578          * @param int $group_id ID of the group.
    579          * @return int|null The number of outstanding requests, or null if
    580          *         none are found.
    581          */
    582         public static function has_membership_requests( $group_id ) {
    583                 global $wpdb;
    584 
    585                 $bp = buddypress();
    586 
    587                 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 ) );
    588         }
    589 
    590         /**
    591          * Get outstanding membership requests for a group.
    592          *
    593          * @param int $group_id ID of the group.
    594          * @param int $limit Optional. Max number of results to return.
    595          *        Default: null (no limit).
    596          * @param int $page Optional. Page offset of results returned. Default:
    597          *        null (no limit).
    598          * @return array {
    599          *     @type array $requests The requested page of located requests.
    600          *     @type int $total Total number of requests outstanding for the
    601          *           group.
    602          * }
    603          */
    604         public static function get_membership_requests( $group_id, $limit = null, $page = null ) {
    605                 global $wpdb;
    606 
    607                 if ( !empty( $limit ) && !empty( $page ) ) {
    608                         $pag_sql = $wpdb->prepare( " LIMIT %d, %d", intval( ( $page - 1 ) * $limit), intval( $limit ) );
    609                 }
    610 
    611                 $bp = buddypress();
    612 
    613                 $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 ) );
    614                 $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 ) );
    615 
    616                 return array( 'requests' => $paged_requests, 'total' => $total_requests );
    617         }
    618 
    619         /**
    620          * Query for groups.
    621          *
    622          * @see WP_Meta_Query::queries for a description of the 'meta_query'
    623          *      parameter format.
    624          *
    625          * @param array {
    626          *     Array of parameters. All items are optional.
    627          *     @type string $type Optional. Shorthand for certain orderby/
    628          *           order combinations. 'newest', 'active', 'popular',
    629          *           'alphabetical', 'random'. When present, will override
    630          *           orderby and order params. Default: null.
    631          *     @type string $orderby Optional. Property to sort by.
    632          *           'date_created', 'last_activity', 'total_member_count',
    633          *           'name', 'random'. Default: 'date_created'.
    634          *     @type string $order Optional. Sort order. 'ASC' or 'DESC'.
    635          *           Default: 'DESC'.
    636          *     @type int $per_page Optional. Number of items to return per page
    637          *           of results. Default: null (no limit).
    638          *     @type int $page Optional. Page offset of results to return.
    639          *           Default: null (no limit).
    640          *     @type int $user_id Optional. If provided, results will be limited
    641          *           to groups of which the specified user is a member. Default:
    642          *           null.
    643          *     @type string $search_terms Optional. If provided, only groups
    644          *           whose names or descriptions match the search terms will be
    645          *           returned. Default: false.
    646          *     @type array $meta_query Optional. An array of meta_query
    647          *           conditions. See {@link WP_Meta_Query::queries} for
    648          *           description.
    649          *     @type array|string Optional. Array or comma-separated list of
    650          *           group IDs. Results will be limited to groups within the
    651          *           list. Default: false.
    652          *     @type bool $populate_extras Whether to fetch additional
    653          *           information (such as member count) about groups. Default:
    654          *           true.
    655          *     @type array|string $exclude Optional. Array or comma-separated
    656          *           list of group IDs. Results will exclude the listed groups.
    657          *           Default: false.
    658          *     @type bool $update_meta_cache Whether to pre-fetch groupmeta for
    659          *           the returned groups. Default: true.
    660          *     @type bool $show_hidden Whether to include hidden groups in
    661          *           results. Default: false.
    662          * }
    663          * @return array {
    664          *     @type array $groups Array of group objects returned by the
    665          *           paginated query.
    666          *     @type int $total Total count of all groups matching non-
    667          *           paginated query params.
    668          * }
    669          */
    670         public static function get( $args = array() ) {
    671                 global $wpdb;
    672 
    673                 // Backward compatibility with old method of passing arguments
    674                 if ( ! is_array( $args ) || func_num_args() > 1 ) {
    675                         _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__ ) );
    676 
    677                         $old_args_keys = array(
    678                                 0 => 'type',
    679                                 1 => 'per_page',
    680                                 2 => 'page',
    681                                 3 => 'user_id',
    682                                 4 => 'search_terms',
    683                                 5 => 'include',
    684                                 6 => 'populate_extras',
    685                                 7 => 'exclude',
    686                                 8 => 'show_hidden',
    687                         );
    688 
    689                         $func_args = func_get_args();
    690                         $args      = bp_core_parse_args_array( $old_args_keys, $func_args );
    691                 }
    692 
    693                 $defaults = array(
    694                         'type'              => null,
    695                         'orderby'           => 'date_created',
    696                         'order'             => 'DESC',
    697                         'per_page'          => null,
    698                         'page'              => null,
    699                         'user_id'           => 0,
    700                         'search_terms'      => false,
    701                         'meta_query'        => false,
    702                         'include'           => false,
    703                         'populate_extras'   => true,
    704                         'update_meta_cache' => true,
    705                         'exclude'           => false,
    706                         'show_hidden'       => false,
    707                 );
    708 
    709                 $r = wp_parse_args( $args, $defaults );
    710 
    711                 $bp = buddypress();
    712 
    713                 $sql       = array();
    714                 $total_sql = array();
    715 
    716                 $sql['select'] = "SELECT DISTINCT g.id, g.*, gm1.meta_value AS total_member_count, gm2.meta_value AS last_activity";
    717                 $sql['from']   = " FROM {$bp->groups->table_name_groupmeta} gm1, {$bp->groups->table_name_groupmeta} gm2,";
    718 
    719                 if ( ! empty( $r['user_id'] ) ) {
    720                         $sql['members_from'] = " {$bp->groups->table_name_members} m,";
    721                 }
    722 
    723                 $sql['group_from'] = " {$bp->groups->table_name} g WHERE";
    724 
    725                 if ( ! empty( $r['user_id'] ) ) {
    726                         $sql['user_where'] = " g.id = m.group_id AND";
    727                 }
    728 
    729                 $sql['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'";
    730 
    731                 if ( empty( $r['show_hidden'] ) ) {
    732                         $sql['hidden'] = " AND g.status != 'hidden'";
    733                 }
    734 
    735                 if ( ! empty( $r['search_terms'] ) ) {
    736                         $search_terms_like = '%' . bp_esc_like( $r['search_terms'] ) . '%';
    737                         $sql['search'] = $wpdb->prepare( " AND ( g.name LIKE %s OR g.description LIKE %s )", $search_terms_like, $search_terms_like );
    738                 }
    739 
    740                 $meta_query_sql = self::get_meta_query_sql( $r['meta_query'] );
    741 
    742                 if ( ! empty( $meta_query_sql['join'] ) ) {
    743                         $sql['from'] .= $meta_query_sql['join'];
    744                 }
    745 
    746                 if ( ! empty( $meta_query_sql['where'] ) ) {
    747                         $sql['meta'] = $meta_query_sql['where'];
    748                 }
    749 
    750                 if ( ! empty( $r['user_id'] ) ) {
    751                         $sql['user'] = $wpdb->prepare( " AND m.user_id = %d AND m.is_confirmed = 1 AND m.is_banned = 0", $r['user_id'] );
    752                 }
    753 
    754                 if ( ! empty( $r['include'] ) ) {
    755                         $include        = implode( ',', wp_parse_id_list( $r['include'] ) );
    756                         $sql['include'] = " AND g.id IN ({$include})";
    757                 }
    758 
    759                 if ( ! empty( $r['exclude'] ) ) {
    760                         $exclude        = implode( ',', wp_parse_id_list( $r['exclude'] ) );
    761                         $sql['exclude'] = " AND g.id NOT IN ({$exclude})";
    762                 }
    763 
    764                 /** Order/orderby ********************************************/
    765 
    766                 $order   = $r['order'];
    767                 $orderby = $r['orderby'];
    768 
    769                 // If a 'type' parameter was passed, parse it and overwrite
    770                 // 'order' and 'orderby' params passed to the function
    771                 if (  ! empty( $r['type'] ) ) {
    772                         $order_orderby = apply_filters( 'bp_groups_get_orderby', self::convert_type_to_order_orderby( $r['type'] ), $r['type'] );
    773 
    774                         // If an invalid type is passed, $order_orderby will be
    775                         // an array with empty values. In this case, we stick
    776                         // with the default values of $order and $orderby
    777                         if ( ! empty( $order_orderby['order'] ) ) {
    778                                 $order = $order_orderby['order'];
    779                         }
    780 
    781                         if ( ! empty( $order_orderby['orderby'] ) ) {
    782                                 $orderby = $order_orderby['orderby'];
    783                         }
    784                 }
    785 
    786                 // Sanitize 'order'
    787                 $order = bp_esc_sql_order( $order );
    788 
    789                 // Convert 'orderby' into the proper ORDER BY term
    790                 $orderby = apply_filters( 'bp_groups_get_orderby_converted_by_term', self::convert_orderby_to_order_by_term( $orderby ), $orderby, $r['type'] );
    791 
    792                 // Random order is a special case
    793                 if ( 'rand()' === $orderby ) {
    794                         $sql[] = "ORDER BY rand()";
    795                 } else {
    796                         $sql[] = "ORDER BY {$orderby} {$order}";
    797                 }
    798 
    799                 if ( ! empty( $r['per_page'] ) && ! empty( $r['page'] ) && $r['per_page'] != -1 ) {
    800                         $sql['pagination'] = $wpdb->prepare( "LIMIT %d, %d", intval( ( $r['page'] - 1 ) * $r['per_page']), intval( $r['per_page'] ) );
    801                 }
    802 
    803                 // Get paginated results
    804                 $paged_groups_sql = apply_filters( 'bp_groups_get_paged_groups_sql', join( ' ', (array) $sql ), $sql, $r );
    805                 $paged_groups     = $wpdb->get_results( $paged_groups_sql );
    806 
    807                 $total_sql['select'] = "SELECT COUNT(DISTINCT g.id) FROM {$bp->groups->table_name} g, {$bp->groups->table_name_groupmeta} gm";
    808 
    809                 if ( ! empty( $r['user_id'] ) ) {
    810                         $total_sql['select'] .= ", {$bp->groups->table_name_members} m";
    811                 }
    812 
    813                 if ( ! empty( $sql['hidden'] ) ) {
    814                         $total_sql['where'][] = "g.status != 'hidden'";
    815                 }
    816 
    817                 if ( ! empty( $sql['search'] ) ) {
    818                         $total_sql['where'][] = $wpdb->prepare( "( g.name LIKE %s OR g.description LIKE %s )", $search_terms_like, $search_terms_like );
    819                 }
    820 
    821                 if ( ! empty( $r['user_id'] ) ) {
    822                         $total_sql['where'][] = $wpdb->prepare( "m.group_id = g.id AND m.user_id = %d AND m.is_confirmed = 1 AND m.is_banned = 0", $r['user_id'] );
    823                 }
    824 
    825                 // Temporary implementation of meta_query for total count
    826                 // See #5099
    827                 if ( ! empty( $meta_query_sql['where'] ) ) {
    828                         // Join the groupmeta table
    829                         $total_sql['select'] .= ", ". substr( $meta_query_sql['join'], 0, -2 );
    830 
    831                         // Modify the meta_query clause from paged_sql for our syntax
    832                         $meta_query_clause = preg_replace( '/^\s*AND/', '', $meta_query_sql['where'] );
    833                         $total_sql['where'][] = $meta_query_clause;
    834                 }
    835 
    836                 // Already escaped in the paginated results block
    837                 if ( ! empty( $include ) ) {
    838                         $total_sql['where'][] = "g.id IN ({$include})";
    839                 }
    840 
    841                 // Already escaped in the paginated results block
    842                 if ( ! empty( $exclude ) ) {
    843                         $total_sql['where'][] = "g.id NOT IN ({$exclude})";
    844                 }
    845 
    846                 $total_sql['where'][] = "g.id = gm.group_id";
    847                 $total_sql['where'][] = "gm.meta_key = 'last_activity'";
    848 
    849                 $t_sql = $total_sql['select'];
    850 
    851                 if ( ! empty( $total_sql['where'] ) ) {
    852                         $t_sql .= " WHERE " . join( ' AND ', (array) $total_sql['where'] );
    853                 }
    854 
    855                 // Get total group results
    856                 $total_groups_sql = apply_filters( 'bp_groups_get_total_groups_sql', $t_sql, $total_sql, $r );
    857                 $total_groups     = $wpdb->get_var( $total_groups_sql );
    858 
    859                 $group_ids = array();
    860                 foreach ( (array) $paged_groups as $group ) {
    861                         $group_ids[] = $group->id;
    862                 }
    863 
    864                 // Populate some extra information instead of querying each time in the loop
    865                 if ( !empty( $r['populate_extras'] ) ) {
    866                         $paged_groups = BP_Groups_Group::get_group_extras( $paged_groups, $group_ids, $r['type'] );
    867                 }
    868 
    869                 // Grab all groupmeta
    870                 if ( ! empty( $r['update_meta_cache'] ) ) {
    871                         bp_groups_update_meta_cache( $group_ids );
    872                 }
    873 
    874                 unset( $sql, $total_sql );
    875 
    876                 return array( 'groups' => $paged_groups, 'total' => $total_groups );
    877         }
    878 
    879         /**
    880          * Get the SQL for the 'meta_query' param in BP_Activity_Activity::get()
    881          *
    882          * We use WP_Meta_Query to do the heavy lifting of parsing the
    883          * meta_query array and creating the necessary SQL clauses. However,
    884          * since BP_Activity_Activity::get() builds its SQL differently than
    885          * WP_Query, we have to alter the return value (stripping the leading
    886          * AND keyword from the 'where' clause).
    887          *
    888          * @since BuddyPress (1.8.0)
    889          * @access protected
    890          *
    891          * @param array $meta_query An array of meta_query filters. See the
    892          *        documentation for {@link WP_Meta_Query} for details.
    893          * @return array $sql_array 'join' and 'where' clauses.
    894          */
    895         protected static function get_meta_query_sql( $meta_query = array() ) {
    896                 global $wpdb;
    897 
    898                 $sql_array = array(
    899                         'join'  => '',
    900                         'where' => '',
    901                 );
    902 
    903                 if ( ! empty( $meta_query ) ) {
    904                         $groups_meta_query = new WP_Meta_Query( $meta_query );
    905 
    906                         // WP_Meta_Query expects the table name at
    907                         // $wpdb->group
    908                         $wpdb->groupmeta = buddypress()->groups->table_name_groupmeta;
    909 
    910                         $meta_sql = $groups_meta_query->get_sql( 'group', 'g', 'id' );
    911 
    912                         // BP_Groups_Group::get uses the comma syntax for table
    913                         // joins, which means that we have to do some regex to
    914                         // convert the INNER JOIN and move the ON clause to a
    915                         // WHERE condition
    916                         //
    917                         // @todo It may be better in the long run to refactor
    918                         // the more general query syntax to accord better with
    919                         // BP/WP convention
    920                         preg_match_all( '/JOIN (.+?) ON/', $meta_sql['join'], $matches_a );
    921                         preg_match_all( '/ON \((.+?)\)/', $meta_sql['join'], $matches_b );
    922 
    923                         if ( ! empty( $matches_a[1] ) && ! empty( $matches_b[1] ) ) {
    924                                 $sql_array['join']  = implode( ',', $matches_a[1] ) . ', ';
    925                                 $sql_array['where'] = $meta_sql['where'] . ' AND ' . implode( ' AND ', $matches_b[1] );
    926                         }
    927                 }
    928 
    929                 return $sql_array;
    930         }
    931 
    932         /**
    933          * Convert the 'type' parameter to 'order' and 'orderby'.
    934          *
    935          * @since BuddyPress (1.8.0)
    936          * @access protected
    937          *
    938          * @param string $type The 'type' shorthand param.
    939          * @return array {
    940          *      @type string $order SQL-friendly order string.
    941          *      @type string $orderby SQL-friendly orderby column name.
    942          * }
    943          */
    944         protected static function convert_type_to_order_orderby( $type = '' ) {
    945                 $order = $orderby = '';
    946 
    947                 switch ( $type ) {
    948                         case 'newest' :
    949                                 $order   = 'DESC';
    950                                 $orderby = 'date_created';
    951                                 break;
    952 
    953                         case 'active' :
    954                                 $order   = 'DESC';
    955                                 $orderby = 'last_activity';
    956                                 break;
    957 
    958                         case 'popular' :
    959                                 $order   = 'DESC';
    960                                 $orderby = 'total_member_count';
    961                                 break;
    962 
    963                         case 'alphabetical' :
    964                                 $order   = 'ASC';
    965                                 $orderby = 'name';
    966                                 break;
    967 
    968                         case 'random' :
    969                                 $order   = '';
    970                                 $orderby = 'random';
    971                                 break;
    972                 }
    973 
    974                 return array( 'order' => $order, 'orderby' => $orderby );
    975         }
    976 
    977         /**
    978          * Convert the 'orderby' param into a proper SQL term/column.
    979          *
    980          * @since BuddyPress (1.8.0)
    981          * @access protected
    982          *
    983          * @param string $orderby Orderby term as passed to get().
    984          * @return string $order_by_term SQL-friendly orderby term.
    985          */
    986         protected static function convert_orderby_to_order_by_term( $orderby ) {
    987                 $order_by_term = '';
    988 
    989                 switch ( $orderby ) {
    990                         case 'date_created' :
    991                         default :
    992                                 $order_by_term = 'g.date_created';
    993                                 break;
    994 
    995                         case 'last_activity' :
    996                                 $order_by_term = 'last_activity';
    997                                 break;
    998 
    999                         case 'total_member_count' :
    1000                                 $order_by_term = 'CONVERT(gm1.meta_value, SIGNED)';
    1001                                 break;
    1002 
    1003                         case 'name' :
    1004                                 $order_by_term = 'g.name';
    1005                                 break;
    1006 
    1007                         case 'random' :
    1008                                 $order_by_term = 'rand()';
    1009                                 break;
    1010                 }
    1011 
    1012                 return $order_by_term;
    1013         }
    1014 
    1015         /**
    1016          * Get a list of groups, sorted by those that have the most legacy forum topics.
    1017          *
    1018          * @param int $limit Optional. The max number of results to return.
    1019          *        Default: null (no limit).
    1020          * @param int $page Optional. The page offset of results to return.
    1021          *        Default: null (no limit).
    1022          * @param int $user_id Optional. If present, groups will be limited to
    1023          *        those of which the specified user is a member.
    1024          * @param string $search_terms Optional. Limit groups to those whose
    1025          *        name or description field contain the search string.
    1026          * @param bool $populate_extras Optional. Whether to fetch extra
    1027          *        information about the groups. Default: true.
    1028          * @param string|array Optional. Array or comma-separated list of group
    1029          *        IDs to exclude from results.
    1030          * @return array {
    1031          *     @type array $groups Array of group objects returned by the
    1032          *           paginated query.
    1033          *     @type int $total Total count of all groups matching non-
    1034          *           paginated query params.
    1035          * }
    1036          */
    1037         public static function get_by_most_forum_topics( $limit = null, $page = null, $user_id = 0, $search_terms = false, $populate_extras = true, $exclude = false ) {
    1038                 global $wpdb, $bbdb;
    1039 
    1040                 if ( empty( $bbdb ) )
    1041                         do_action( 'bbpress_init' );
    1042 
    1043                 if ( !empty( $limit ) && !empty( $page ) ) {
    1044                         $pag_sql = $wpdb->prepare( " LIMIT %d, %d", intval( ( $page - 1 ) * $limit), intval( $limit ) );
    1045                 }
    1046 
    1047                 if ( !is_user_logged_in() || ( !bp_current_user_can( 'bp_moderate' ) && ( $user_id != bp_loggedin_user_id() ) ) )
    1048                         $hidden_sql = " AND g.status != 'hidden'";
    1049 
    1050                 if ( !empty( $search_terms ) ) {
    1051                         $search_terms_like = '%' . bp_esc_like( $search_terms ) . '%';
    1052                         $search_sql        = $wpdb->prepare( ' AND ( g.name LIKE %s OR g.description LIKE %s ) ', $search_terms_like, $search_terms_like );
    1053                 }
    1054 
    1055                 if ( !empty( $exclude ) ) {
    1056                         $exclude     = implode( ',', wp_parse_id_list( $exclude ) );
    1057                         $exclude_sql = " AND g.id NOT IN ({$exclude})";
    1058                 }
    1059 
    1060                 $bp = buddypress();
    1061 
    1062                 if ( !empty( $user_id ) ) {
    1063                         $user_id      = absint( esc_sql( $user_id ) );
    1064                         $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}" );
    1065                         $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}" );
    1066                 } else {
    1067                         $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}" );
    1068                         $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}" );
    1069                 }
    1070 
    1071                 if ( !empty( $populate_extras ) ) {
    1072                         foreach ( (array) $paged_groups as $group ) {
    1073                                 $group_ids[] = $group->id;
    1074                         }
    1075                         $paged_groups = BP_Groups_Group::get_group_extras( $paged_groups, $group_ids, 'newest' );
    1076                 }
    1077 
    1078                 return array( 'groups' => $paged_groups, 'total' => $total_groups );
    1079         }
    1080 
    1081         /**
    1082          * Get a list of groups, sorted by those that have the most legacy forum posts.
    1083          *
    1084          * @param int $limit Optional. The max number of results to return.
    1085          *        Default: null (no limit).
    1086          * @param int $page Optional. The page offset of results to return.
    1087          *        Default: null (no limit).
    1088          * @param int $user_id Optional. If present, groups will be limited to
    1089          *        those of which the specified user is a member.
    1090          * @param string $search_terms Optional. Limit groups to those whose
    1091          *        name or description field contain the search string.
    1092          * @param bool $populate_extras Optional. Whether to fetch extra
    1093          *        information about the groups. Default: true.
    1094          * @param string|array Optional. Array or comma-separated list of group
    1095          *        IDs to exclude from results.
    1096          * @return array {
    1097          *     @type array $groups Array of group objects returned by the
    1098          *           paginated query.
    1099          *     @type int $total Total count of all groups matching non-
    1100          *           paginated query params.
    1101          * }
    1102          */
    1103         public static function get_by_most_forum_posts( $limit = null, $page = null, $search_terms = false, $populate_extras = true, $exclude = false ) {
    1104                 global $wpdb, $bbdb;
    1105 
    1106                 if ( empty( $bbdb ) )
    1107                         do_action( 'bbpress_init' );
    1108 
    1109                 if ( !empty( $limit ) && !empty( $page ) ) {
    1110                         $pag_sql = $wpdb->prepare( " LIMIT %d, %d", intval( ( $page - 1 ) * $limit), intval( $limit ) );
    1111                 }
    1112 
    1113                 if ( !is_user_logged_in() || ( !bp_current_user_can( 'bp_moderate' ) && ( $user_id != bp_loggedin_user_id() ) ) )
    1114                         $hidden_sql = " AND g.status != 'hidden'";
    1115 
    1116                 if ( !empty( $search_terms ) ) {
    1117                         $search_terms_like = '%' . bp_esc_like( $search_terms ) . '%';
    1118                         $search_sql        = $wpdb->prepare( ' AND ( g.name LIKE %s OR g.description LIKE %s ) ', $search_terms_like, $search_terms_like );
    1119                 }
    1120 
    1121                 if ( !empty( $exclude ) ) {
    1122                         $exclude     = implode( ',', wp_parse_id_list( $exclude ) );
    1123                         $exclude_sql = " AND g.id NOT IN ({$exclude})";
    1124                 }
    1125 
    1126                 $bp = buddypress();
    1127 
    1128                 if ( !empty( $user_id ) ) {
    1129                         $user_id = esc_sql( $user_id );
    1130                         $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}" );
    1131                         $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} " );
    1132                 } else {
    1133                         $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}" );
    1134                         $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}" );
    1135                 }
    1136 
    1137                 if ( !empty( $populate_extras ) ) {
    1138                         foreach ( (array) $paged_groups as $group ) {
    1139                                 $group_ids[] = $group->id;
    1140                         }
    1141                         $paged_groups = BP_Groups_Group::get_group_extras( $paged_groups, $group_ids, 'newest' );
    1142                 }
    1143 
    1144                 return array( 'groups' => $paged_groups, 'total' => $total_groups );
    1145         }
    1146 
    1147         /**
    1148          * Get a list of groups whose names start with a given letter.
    1149          *
    1150          * @param string $letter The letter.
    1151          * @param int $limit Optional. The max number of results to return.
    1152          *        Default: null (no limit).
    1153          * @param int $page Optional. The page offset of results to return.
    1154          *        Default: null (no limit).
    1155          * @param bool $populate_extras Optional. Whether to fetch extra
    1156          *        information about the groups. Default: true.
    1157          * @param string|array Optional. Array or comma-separated list of group
    1158          *        IDs to exclude from results.
    1159          * @return array {
    1160          *     @type array $groups Array of group objects returned by the
    1161          *           paginated query.
    1162          *     @type int $total Total count of all groups matching non-
    1163          *           paginated query params.
    1164          * }
    1165          */
    1166         public static function get_by_letter( $letter, $limit = null, $page = null, $populate_extras = true, $exclude = false ) {
    1167                 global $wpdb;
    1168 
    1169                 $pag_sql = $hidden_sql = $exclude_sql = '';
    1170 
    1171                 // Multibyte compliance
    1172                 if ( function_exists( 'mb_strlen' ) ) {
    1173                         if ( mb_strlen( $letter, 'UTF-8' ) > 1 || is_numeric( $letter ) || !$letter ) {
    1174                                 return false;
    1175                         }
    1176                 } else {
    1177                         if ( strlen( $letter ) > 1 || is_numeric( $letter ) || !$letter ) {
    1178                                 return false;
    1179                         }
    1180                 }
    1181 
    1182                 $bp = buddypress();
    1183 
    1184                 if ( !empty( $exclude ) ) {
    1185                         $exclude     = implode( ',', wp_parse_id_list( $exclude ) );
    1186                         $exclude_sql = " AND g.id NOT IN ({$exclude})";
    1187                 }
    1188 
    1189                 if ( !bp_current_user_can( 'bp_moderate' ) )
    1190                         $hidden_sql = " AND status != 'hidden'";
    1191 
    1192                 $letter_like = bp_esc_like( $letter ) . '%';
    1193 
    1194                 if ( !empty( $limit ) && !empty( $page ) ) {
    1195                         $pag_sql      = $wpdb->prepare( " LIMIT %d, %d", intval( ( $page - 1 ) * $limit), intval( $limit ) );
    1196                 }
    1197 
    1198                 $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 ) );
    1199 
    1200                 $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 ) );
    1201 
    1202                 if ( !empty( $populate_extras ) ) {
    1203                         foreach ( (array) $paged_groups as $group ) {
    1204                                 $group_ids[] = $group->id;
    1205                         }
    1206                         $paged_groups = BP_Groups_Group::get_group_extras( $paged_groups, $group_ids, 'newest' );
    1207                 }
    1208 
    1209                 return array( 'groups' => $paged_groups, 'total' => $total_groups );
    1210         }
    1211 
    1212         /**
    1213          * Get a list of random groups.
    1214          *
    1215          * Use BP_Groups_Group::get() with 'type' = 'random' instead.
    1216          *
    1217          * @param int $limit Optional. The max number of results to return.
    1218          *        Default: null (no limit).
    1219          * @param int $page Optional. The page offset of results to return.
    1220          *        Default: null (no limit).
    1221          * @param int $user_id Optional. If present, groups will be limited to
    1222          *        those of which the specified user is a member.
    1223          * @param string $search_terms Optional. Limit groups to those whose
    1224          *        name or description field contain the search string.
    1225          * @param bool $populate_extras Optional. Whether to fetch extra
    1226          *        information about the groups. Default: true.
    1227          * @param string|array Optional. Array or comma-separated list of group
    1228          *        IDs to exclude from results.
    1229          * @return array {
    1230          *     @type array $groups Array of group objects returned by the
    1231          *           paginated query.
    1232          *     @type int $total Total count of all groups matching non-
    1233          *           paginated query params.
    1234          * }
    1235          */
    1236         public static function get_random( $limit = null, $page = null, $user_id = 0, $search_terms = false, $populate_extras = true, $exclude = false ) {
    1237                 global $wpdb;
    1238 
    1239                 $pag_sql = $hidden_sql = $search_sql = $exclude_sql = '';
    1240 
    1241                 if ( !empty( $limit ) && !empty( $page ) )
    1242                         $pag_sql = $wpdb->prepare( " LIMIT %d, %d", intval( ( $page - 1 ) * $limit), intval( $limit ) );
    1243 
    1244                 if ( !is_user_logged_in() || ( !bp_current_user_can( 'bp_moderate' ) && ( $user_id != bp_loggedin_user_id() ) ) )
    1245                         $hidden_sql = "AND g.status != 'hidden'";
    1246 
    1247                 if ( !empty( $search_terms ) ) {
    1248                         $search_terms_like = '%' . bp_esc_like( $search_terms ) . '%';
    1249                         $search_sql = $wpdb->prepare( " AND ( g.name LIKE %s OR g.description LIKE %s )", $search_terms_like, $search_terms_like );
    1250                 }
    1251 
    1252                 if ( !empty( $exclude ) ) {
    1253                         $exclude     = wp_parse_id_list( $exclude );
    1254                         $exclude     = esc_sql( implode( ',', $exclude ) );
    1255                         $exclude_sql = " AND g.id NOT IN ({$exclude})";
    1256                 }
    1257 
    1258                 $bp = buddypress();
    1259 
    1260                 if ( !empty( $user_id ) ) {
    1261                         $user_id = esc_sql( $user_id );
    1262                         $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}" );
    1263                         $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}" );
    1264                 } else {
    1265                         $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}" );
    1266                         $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}" );
    1267                 }
    1268 
    1269                 if ( !empty( $populate_extras ) ) {
    1270                         foreach ( (array) $paged_groups as $group ) {
    1271                                 $group_ids[] = $group->id;
    1272                         }
    1273                         $paged_groups = BP_Groups_Group::get_group_extras( $paged_groups, $group_ids, 'newest' );
    1274                 }
    1275 
    1276                 return array( 'groups' => $paged_groups, 'total' => $total_groups );
    1277         }
    1278 
    1279         /**
    1280          * Fetch extra data for a list of groups.
    1281          *
    1282          * This method is used throughout the class, by methods that take a
    1283          * $populate_extras parameter.
    1284          *
    1285          * Data fetched:
    1286          *
    1287          *     - Logged-in user's status within each group (is_member,
    1288          *       is_confirmed, is_pending, is_banned)
    1289          *
    1290          * @param array $paged_groups Array of groups.
    1291          * @param string|array Array or comma-separated list of IDs matching
    1292          *        $paged_groups.
    1293          * @param string $type Not used.
    1294          * @return array $paged_groups
    1295          */
    1296         public static function get_group_extras( &$paged_groups, &$group_ids, $type = false ) {
    1297                 global $wpdb;
    1298 
    1299                 if ( empty( $group_ids ) )
    1300                         return $paged_groups;
    1301 
    1302                 $bp = buddypress();
    1303 
    1304                 // Sanitize group IDs
    1305                 $group_ids = implode( ',', wp_parse_id_list( $group_ids ) );
    1306 
    1307                 // Fetch the logged-in user's status within each group
    1308                 if ( is_user_logged_in() ) {
    1309                         $user_status_results = $wpdb->get_results( $wpdb->prepare( "SELECT group_id, is_confirmed, invite_sent FROM {$bp->groups->table_name_members} WHERE user_id = %d AND group_id IN ( {$group_ids} ) AND is_banned = 0", bp_loggedin_user_id() ) );
    1310                 } else {
    1311                         $user_status_results = array();
    1312                 }
    1313 
    1314                 // Reindex
    1315                 $user_status = array();
    1316                 foreach ( $user_status_results as $user_status_result ) {
    1317                         $user_status[ $user_status_result->group_id ] = $user_status_result;
    1318                 }
    1319 
    1320                 for ( $i = 0, $count = count( $paged_groups ); $i < $count; ++$i ) {
    1321                         $is_member = $is_invited = $is_pending = '0';
    1322                         $gid = $paged_groups[ $i ]->id;
    1323 
    1324                         if ( isset( $user_status[ $gid ] ) ) {
    1325 
    1326                                 // is_confirmed means the user is a member
    1327                                 if ( $user_status[ $gid ]->is_confirmed ) {
    1328                                         $is_member = '1';
    1329 
    1330                                 // invite_sent means the user has been invited
    1331                                 } elseif ( $user_status[ $gid ]->invite_sent ) {
    1332                                         $is_invited = '1';
    1333 
    1334                                 // User has sent request, but has not been confirmed
    1335                                 } else {
    1336                                         $is_pending = '1';
    1337                                 }
    1338                         }
    1339 
    1340                         $paged_groups[ $i ]->is_member = $is_member;
    1341                         $paged_groups[ $i ]->is_invited = $is_invited;
    1342                         $paged_groups[ $i ]->is_pending = $is_pending;
    1343                 }
    1344 
    1345                 if ( is_user_logged_in() ) {
    1346                         $user_banned = $wpdb->get_col( $wpdb->prepare( "SELECT group_id FROM {$bp->groups->table_name_members} WHERE is_banned = 1 AND user_id = %d AND group_id IN ( {$group_ids} )", bp_loggedin_user_id() ) );
    1347                 } else {
    1348                         $user_banned = array();
    1349                 }
    1350 
    1351                 for ( $i = 0, $count = count( $paged_groups ); $i < $count; ++$i ) {
    1352                         $paged_groups[$i]->is_banned = false;
    1353 
    1354                         foreach ( (array) $user_banned as $group_id ) {
    1355                                 if ( $group_id == $paged_groups[$i]->id ) {
    1356                                         $paged_groups[$i]->is_banned = true;
    1357                                 }
    1358                         }
    1359                 }
    1360 
    1361                 return $paged_groups;
    1362         }
    1363 
    1364         /**
    1365          * Delete all invitations to a given group.
    1366          *
    1367          * @param int $group_id ID of the group whose invitations are being
    1368          *        deleted.
    1369          * @return int|null Number of rows records deleted on success, null on
    1370          *         failure.
    1371          */
    1372         public static function delete_all_invites( $group_id ) {
    1373                 global $wpdb;
    1374 
    1375                 $bp = buddypress();
    1376 
    1377                 return $wpdb->query( $wpdb->prepare( "DELETE FROM {$bp->groups->table_name_members} WHERE group_id = %d AND invite_sent = 1", $group_id ) );
    1378         }
    1379 
    1380         /**
    1381          * Get a total group count for the site.
    1382          *
    1383          * Will include hidden groups in the count only if
    1384          * current_user_can( 'bp_moderate' ).
    1385          *
    1386          * @return int Group count.
    1387          */
    1388         public static function get_total_group_count() {
    1389                 global $wpdb;
    1390 
    1391                 $hidden_sql = '';
    1392                 if ( !bp_current_user_can( 'bp_moderate' ) )
    1393                         $hidden_sql = "WHERE status != 'hidden'";
    1394 
    1395                 $bp = buddypress();
    1396 
    1397                 return $wpdb->get_var( "SELECT COUNT(id) FROM {$bp->groups->table_name} {$hidden_sql}" );
    1398         }
    1399 
    1400         /**
    1401          * Get global count of forum topics in public groups (legacy forums).
    1402          *
    1403          * @param $type Optional. If 'unreplied', count will be limited to
    1404          *        those topics that have received no replies.
    1405          * @return int Forum topic count.
    1406          */
    1407         public static function get_global_forum_topic_count( $type ) {
    1408                 global $bbdb, $wpdb;
    1409 
    1410                 $bp = buddypress();
    1411 
    1412                 if ( 'unreplied' == $type )
    1413                         $bp->groups->filter_sql = ' AND t.topic_posts = 1';
    1414 
    1415                 // https://buddypress.trac.wordpress.org/ticket/4306
    1416                 $extra_sql = apply_filters( 'get_global_forum_topic_count_extra_sql', $bp->groups->filter_sql, $type );
    1417 
    1418                 // Make sure the $extra_sql begins with an AND
    1419                 if ( 'AND' != substr( trim( strtoupper( $extra_sql ) ), 0, 3 ) )
    1420                         $extra_sql = ' AND ' . $extra_sql;
    1421 
    1422                 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} " );
    1423         }
    1424 
    1425         /**
    1426          * Get the member count for a group.
    1427          *
    1428          * @param int $group_id Group ID.
    1429          * @return int Count of confirmed members for the group.
    1430          */
    1431         public static function get_total_member_count( $group_id ) {
    1432                 global $wpdb;
    1433 
    1434                 $bp = buddypress();
    1435 
    1436                 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 ) );
    1437         }
    1438 
    1439         /**
    1440          * Get a total count of all topics of a given status, across groups/forums
    1441          *
    1442          * @since BuddyPress (1.5.0)
    1443          *
    1444          * @param string $status Which group type to count. 'public', 'private',
    1445          *        'hidden', or 'all'. Default: 'public'.
    1446          * @return int The topic count
    1447          */
    1448         public static function get_global_topic_count( $status = 'public', $search_terms = false ) {
    1449                 global $bbdb, $wpdb;
    1450 
    1451                 switch ( $status ) {
    1452                         case 'all' :
    1453                                 $status_sql = '';
    1454                                 break;
    1455 
    1456                         case 'hidden' :
    1457                                 $status_sql = "AND g.status = 'hidden'";
    1458                                 break;
    1459 
    1460                         case 'private' :
    1461                                 $status_sql = "AND g.status = 'private'";
    1462                                 break;
    1463 
    1464                         case 'public' :
    1465                         default :
    1466                                 $status_sql = "AND g.status = 'public'";
    1467                                 break;
    1468                 }
    1469 
    1470                 $bp = buddypress();
    1471 
    1472                 $sql = array();
    1473 
    1474                 $sql['select'] = "SELECT COUNT(t.topic_id)";
    1475                 $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";
    1476                 $sql['where']  = "WHERE gm.meta_key = 'forum_id' {$status_sql} AND t.topic_status = '0' AND t.topic_sticky != '2'";
    1477 
    1478                 if ( !empty( $search_terms ) ) {
    1479                         $search_terms_like = '%' . bp_esc_like( $search_terms ) . '%';
    1480                         $sql['where'] .= $wpdb->prepare( " AND ( t.topic_title LIKE %s )", $search_terms_like );
    1481                 }
    1482 
    1483                 return $wpdb->get_var( implode( ' ', $sql ) );
    1484         }
    1485 
    1486         /**
    1487          * Get an array containing ids for each group type.
    1488          *
    1489          * A bit of a kludge workaround for some issues
    1490          * with bp_has_groups().
    1491          *
    1492          * @since BuddyPress (1.7.0)
    1493          *
    1494          * @return array
    1495          */
    1496         public static function get_group_type_ids() {
    1497                 global $wpdb;
    1498 
    1499                 $bp  = buddypress();
    1500                 $ids = array();
    1501 
    1502                 $ids['all']     = $wpdb->get_col( "SELECT id FROM {$bp->groups->table_name}" );
    1503                 $ids['public']  = $wpdb->get_col( "SELECT id FROM {$bp->groups->table_name} WHERE status = 'public'" );
    1504                 $ids['private'] = $wpdb->get_col( "SELECT id FROM {$bp->groups->table_name} WHERE status = 'private'" );
    1505                 $ids['hidden']  = $wpdb->get_col( "SELECT id FROM {$bp->groups->table_name} WHERE status = 'hidden'" );
    1506 
    1507                 return $ids;
    1508         }
    1509 }
    1510 
    1511 /**
    1512  * Query for the members of a group.
    1513  *
    1514  * Special notes about the group members data schema:
    1515  * - *Members* are entries with is_confirmed = 1
    1516  * - *Pending requests* are entries with is_confirmed = 0 and inviter_id = 0
    1517  * - *Pending and sent invitations* are entries with is_confirmed = 0 and
    1518  *   inviter_id != 0 and invite_sent = 1
    1519  * - *Pending and unsent invitations* are entries with is_confirmed = 0 and
    1520  *   inviter_id != 0 and invite_sent = 0
    1521  * - *Membership requests* are entries with is_confirmed = 0 and
    1522  *   inviter_id = 0 (and invite_sent = 0)
    1523  *
    1524  * @since BuddyPress (1.8.0)
    1525  *
    1526  * @param array $args {
    1527  *     Array of arguments. Accepts all arguments from
    1528  *     {@link BP_User_Query}, with the following additions:
    1529  *     @type int $group_id ID of the group to limit results to.
    1530  *     @type array $group_role Array of group roles to match ('member',
    1531  *           'mod', 'admin', 'banned'). Default: array( 'member' ).
    1532  *     @type bool $is_confirmed Whether to limit to confirmed members.
    1533  *           Default: true.
    1534  *     @type string $type Sort order. Accepts any value supported by
    1535  *           {@link BP_User_Query}, in addition to 'last_joined' and
    1536  *           'first_joined'. Default: 'last_joined'.
    1537  * }
    1538  */
    1539 class BP_Group_Member_Query extends BP_User_Query {
    1540 
    1541         /**
    1542          * Array of group member ids, cached to prevent redundant lookups.
    1543          *
    1544          * @since BuddyPress (1.8.1)
    1545          * @access protected
    1546          * @var null|array Null if not yet defined, otherwise an array of ints.
    1547          */
    1548         protected $group_member_ids;
    1549 
    1550         /**
    1551          * Set up action hooks.
    1552          *
    1553          * @since BuddyPress (1.8.0)
    1554          */
    1555         public function setup_hooks() {
    1556                 // Take this early opportunity to set the default 'type' param
    1557                 // to 'last_joined', which will ensure that BP_User_Query
    1558                 // trusts our order and does not try to apply its own
    1559                 if ( empty( $this->query_vars_raw['type'] ) ) {
    1560                         $this->query_vars_raw['type'] = 'last_joined';
    1561                 }
    1562 
    1563                 // Set the sort order
    1564                 add_action( 'bp_pre_user_query', array( $this, 'set_orderby' ) );
    1565 
    1566                 // Set up our populate_extras method
    1567                 add_action( 'bp_user_query_populate_extras', array( $this, 'populate_group_member_extras' ), 10, 2 );
    1568         }
    1569 
    1570         /**
    1571          * Get a list of user_ids to include in the IN clause of the main query.
    1572          *
    1573          * Overrides BP_User_Query::get_include_ids(), adding our additional
    1574          * group-member logic.
    1575          *
    1576          * @since BuddyPress (1.8.0)
    1577          *
    1578          * @param array $include Existing group IDs in the $include parameter,
    1579          *        as calculated in BP_User_Query.
    1580          * @return array
    1581          */
    1582         public function get_include_ids( $include = array() ) {
    1583                 // The following args are specific to group member queries, and
    1584                 // are not present in the query_vars of a normal BP_User_Query.
    1585                 // We loop through to make sure that defaults are set (though
    1586                 // values passed to the constructor will, as usual, override
    1587                 // these defaults).
    1588                 $this->query_vars = wp_parse_args( $this->query_vars, array(
    1589                         'group_id'     => 0,
    1590                         'group_role'   => array( 'member' ),
    1591                         'is_confirmed' => true,
    1592                         'invite_sent'  => null,
    1593                         'inviter_id'   => null,
    1594                         'type'         => 'last_joined',
    1595                 ) );
    1596 
    1597                 $group_member_ids = $this->get_group_member_ids();
    1598 
    1599                 // If the group member query returned no users, bail with an
    1600                 // array that will guarantee no matches for BP_User_Query
    1601                 if ( empty( $group_member_ids ) ) {
    1602                         return array( 0 );
    1603                 }
    1604 
    1605                 if ( ! empty( $include ) ) {
    1606                         $group_member_ids = array_intersect( $include, $group_member_ids );
    1607                 }
    1608 
    1609                 return $group_member_ids;
    1610         }
    1611 
    1612         /**
    1613          * Get the members of the queried group.
    1614          *
    1615          * @since BuddyPress (1.8.0)
    1616          *
    1617          * @return array $ids User IDs of relevant group member ids.
    1618          */
    1619         protected function get_group_member_ids() {
    1620                 global $wpdb;
    1621 
    1622                 if ( is_array( $this->group_member_ids ) ) {
    1623                         return $this->group_member_ids;
    1624                 }
    1625 
    1626                 $bp  = buddypress();
    1627                 $sql = array(
    1628                         'select'  => "SELECT user_id FROM {$bp->groups->table_name_members}",
    1629                         'where'   => array(),
    1630                         'orderby' => '',
    1631                         'order'   => '',
    1632                 );
    1633 
    1634                 /** WHERE clauses *****************************************************/
    1635 
    1636                 // Group id
    1637                 $sql['where'][] = $wpdb->prepare( "group_id = %d", $this->query_vars['group_id'] );
    1638 
    1639                 // is_confirmed
    1640                 $is_confirmed = ! empty( $this->query_vars['is_confirmed'] ) ? 1 : 0;
    1641                 $sql['where'][] = $wpdb->prepare( "is_confirmed = %d", $is_confirmed );
    1642 
    1643                 // invite_sent
    1644                 if ( ! is_null( $this->query_vars['invite_sent'] ) ) {
    1645                         $invite_sent = ! empty( $this->query_vars['invite_sent'] ) ? 1 : 0;
    1646                         $sql['where'][] = $wpdb->prepare( "invite_sent = %d", $invite_sent );
    1647                 }
    1648 
    1649                 // inviter_id
    1650                 if ( ! is_null( $this->query_vars['inviter_id'] ) ) {
    1651                         $inviter_id = $this->query_vars['inviter_id'];
    1652 
    1653                         // Empty: inviter_id = 0. (pass false, 0, or empty array)
    1654                         if ( empty( $inviter_id ) ) {
    1655                                 $sql['where'][] = "inviter_id = 0";
    1656 
    1657                         // The string 'any' matches any non-zero value (inviter_id != 0)
    1658                         } elseif ( 'any' === $inviter_id ) {
    1659                                 $sql['where'][] = "inviter_id != 0";
    1660 
    1661                         // Assume that a list of inviter IDs has been passed
    1662                         } else {
    1663                                 // Parse and sanitize
    1664                                 $inviter_ids = wp_parse_id_list( $inviter_id );
    1665                                 if ( ! empty( $inviter_ids ) ) {
    1666                                         $inviter_ids_sql = implode( ',', $inviter_ids );
    1667                                         $sql['where'][] = "inviter_id IN ({$inviter_ids_sql})";
    1668                                 }
    1669                         }
    1670                 }
    1671 
    1672                 // Role information is stored as follows: admins have
    1673                 // is_admin = 1, mods have is_mod = 1, banned have is_banned =
    1674                 // 1, and members have all three set to 0.
    1675                 $roles = !empty( $this->query_vars['group_role'] ) ? $this->query_vars['group_role'] : array();
    1676                 if ( is_string( $roles ) ) {
    1677                         $roles = explode( ',', $roles );
    1678                 }
    1679 
    1680                 // Sanitize: Only 'admin', 'mod', 'member', and 'banned' are valid
    1681                 $allowed_roles = array( 'admin', 'mod', 'member', 'banned' );
    1682                 foreach ( $roles as $role_key => $role_value ) {
    1683                         if ( ! in_array( $role_value, $allowed_roles ) ) {
    1684                                 unset( $roles[ $role_key ] );
    1685                         }
    1686                 }
    1687 
    1688                 $roles = array_unique( $roles );
    1689 
    1690                 // When querying for a set of roles containing 'member' (for
    1691                 // which there is no dedicated is_ column), figure out a list
    1692                 // of columns *not* to match
    1693                 $roles_sql = '';
    1694                 if ( in_array( 'member', $roles ) ) {
    1695                         $role_columns = array();
    1696                         foreach ( array_diff( $allowed_roles, $roles ) as $excluded_role ) {
    1697                                 $role_columns[] = 'is_' . $excluded_role . ' = 0';
    1698                         }
    1699 
    1700                         if ( ! empty( $role_columns ) ) {
    1701                                 $roles_sql = '(' . implode( ' AND ', $role_columns ) . ')';
    1702                         }
    1703 
    1704                 // When querying for a set of roles *not* containing 'member',
    1705                 // simply construct a list of is_* = 1 clauses
    1706                 } else {
    1707                         $role_columns = array();
    1708                         foreach ( $roles as $role ) {
    1709                                 $role_columns[] = 'is_' . $role . ' = 1';
    1710                         }
    1711 
    1712                         if ( ! empty( $role_columns ) ) {
    1713                                 $roles_sql = '(' . implode( ' OR ', $role_columns ) . ')';
    1714                         }
    1715                 }
    1716 
    1717                 if ( ! empty( $roles_sql ) ) {
    1718                         $sql['where'][] = $roles_sql;
    1719                 }
    1720 
    1721                 $sql['where'] = ! empty( $sql['where'] ) ? 'WHERE ' . implode( ' AND ', $sql['where'] ) : '';
    1722 
    1723                 // We fetch group members in order of last_joined, regardless
    1724                 // of 'type'. If the 'type' value is not 'last_joined' or
    1725                 // 'first_joined', the order will be overridden in
    1726                 // BP_Group_Member_Query::set_orderby()
    1727                 $sql['orderby'] = "ORDER BY date_modified";
    1728                 $sql['order']   = 'first_joined' === $this->query_vars['type'] ? 'ASC' : 'DESC';
    1729 
    1730                 $this->group_member_ids = $wpdb->get_col( "{$sql['select']} {$sql['where']} {$sql['orderby']} {$sql['order']}" );
    1731 
    1732                 /**
    1733                  * Use this filter to build a custom query (such as when you've
    1734                  * defined a custom 'type').
    1735                  */
    1736                 $this->group_member_ids = apply_filters( 'bp_group_member_query_group_member_ids', $this->group_member_ids, $this );
    1737 
    1738                 return $this->group_member_ids;
    1739         }
    1740 
    1741         /**
    1742          * Tell BP_User_Query to order by the order of our query results.
    1743          *
    1744          * We only override BP_User_Query's native ordering in case of the
    1745          * 'last_joined' and 'first_joined' $type parameters.
    1746          *
    1747          * @param BP_User_Query $query BP_User_Query object.
    1748          */
    1749         public function set_orderby( $query ) {
    1750                 $gm_ids = $this->get_group_member_ids();
    1751                 if ( empty( $gm_ids ) ) {
    1752                         $gm_ids = array( 0 );
    1753                 }
    1754 
    1755                 // For 'last_joined', 'first_joined', and 'group_activity'
    1756                 // types, we override the default orderby clause of
    1757                 // BP_User_Query. In the case of 'group_activity', we perform
    1758                 // a separate query to get the necessary order. In the case of
    1759                 // 'last_joined' and 'first_joined', we can trust the order of
    1760                 // results from  BP_Group_Member_Query::get_group_members().
    1761                 // In all other cases, we fall through and let BP_User_Query
    1762                 // do its own (non-group-specific) ordering.
    1763                 if ( in_array( $query->query_vars['type'], array( 'last_joined', 'first_joined', 'group_activity' ) ) ) {
    1764 
    1765                         // Group Activity DESC
    1766                         if ( 'group_activity' == $query->query_vars['type'] ) {
    1767                                 $gm_ids = $this->get_gm_ids_ordered_by_activity( $query, $gm_ids );
    1768                         }
    1769 
    1770                         // The first param in the FIELD() clause is the sort column id
    1771                         $gm_ids = array_merge( array( 'u.id' ), wp_parse_id_list( $gm_ids ) );
    1772                         $gm_ids_sql = implode( ',', $gm_ids );
    1773 
    1774                         $query->uid_clauses['orderby'] = "ORDER BY FIELD(" . $gm_ids_sql . ")";
    1775                 }
    1776 
    1777                 // Prevent this filter from running on future BP_User_Query
    1778                 // instances on the same page
    1779                 remove_action( 'bp_pre_user_query', array( $this, 'set_orderby' ) );
    1780         }
    1781 
    1782         /**
    1783          * Fetch additional data required in bp_group_has_members() loops.
    1784          *
    1785          * Additional data fetched:
    1786          *
    1787          *      - is_banned
    1788          *      - date_modified
    1789          *
    1790          * @since BuddyPress (1.8.0)
    1791          *
    1792          * @param BP_User_Query $query BP_User_Query object. Because we're
    1793          *        filtering the current object, we use $this inside of the
    1794          *        method instead.
    1795          * @param string $user_ids_sql Sanitized, comma-separated string of
    1796          *        the user ids returned by the main query.
    1797          */
    1798         public function populate_group_member_extras( $query, $user_ids_sql ) {
    1799                 global $wpdb;
    1800 
    1801                 $bp     = buddypress();
    1802                 $extras = $wpdb->get_results( $wpdb->prepare( "SELECT id, user_id, date_modified, is_admin, is_mod, comments, user_title, invite_sent, is_confirmed, inviter_id, is_banned FROM {$bp->groups->table_name_members} WHERE user_id IN ({$user_ids_sql}) AND group_id = %d", $this->query_vars['group_id'] ) );
    1803 
    1804                 foreach ( (array) $extras as $extra ) {
    1805                         if ( isset( $this->results[ $extra->user_id ] ) ) {
    1806                                 // user_id is provided for backward compatibility
    1807                                 $this->results[ $extra->user_id ]->user_id       = (int) $extra->user_id;
    1808                                 $this->results[ $extra->user_id ]->is_admin      = (int) $extra->is_admin;
    1809                                 $this->results[ $extra->user_id ]->is_mod        = (int) $extra->is_mod;
    1810                                 $this->results[ $extra->user_id ]->is_banned     = (int) $extra->is_banned;
    1811                                 $this->results[ $extra->user_id ]->date_modified = $extra->date_modified;
    1812                                 $this->results[ $extra->user_id ]->user_title    = $extra->user_title;
    1813                                 $this->results[ $extra->user_id ]->comments      = $extra->comments;
    1814                                 $this->results[ $extra->user_id ]->invite_sent   = (int) $extra->invite_sent;
    1815                                 $this->results[ $extra->user_id ]->inviter_id    = (int) $extra->inviter_id;
    1816                                 $this->results[ $extra->user_id ]->is_confirmed  = (int) $extra->is_confirmed;
    1817                                 $this->results[ $extra->user_id ]->membership_id = (int) $extra->id;
    1818                         }
    1819                 }
    1820 
    1821                 // Don't filter other BP_User_Query objects on the same page
    1822                 remove_action( 'bp_user_query_populate_extras', array( $this, 'populate_group_member_extras' ), 10, 2 );
    1823         }
    1824 
    1825         /**
    1826          * Sort user IDs by how recently they have generated activity within a given group.
    1827          *
    1828          * @since BuddyPress (2.1.0)
    1829          *
    1830          * @param BP_User_Query $query BP_User_Query object.
    1831          * @param array $gm_ids array of group member ids.
    1832          * @return array
    1833          */
    1834         public function get_gm_ids_ordered_by_activity( $query, $gm_ids = array() ) {
    1835                 global $wpdb;
    1836 
    1837                 if ( empty( $gm_ids ) ) {
    1838                         return $gm_ids;
    1839                 }
    1840 
    1841                 if ( ! bp_is_active( 'activity' ) ) {
    1842                         return $gm_ids;
    1843                 }
    1844 
    1845                 $activity_table = buddypress()->activity->table_name;
    1846 
    1847                 $sql = array(
    1848                         'select'  => "SELECT user_id, max( date_recorded ) as date_recorded FROM {$activity_table}",
    1849                         'where'   => array(),
    1850                         'groupby' => 'GROUP BY user_id',
    1851                         'orderby' => 'ORDER BY date_recorded',
    1852                         'order'   => 'DESC',
    1853                 );
    1854 
    1855                 $sql['where'] = array(
    1856                         'user_id IN (' . implode( ',', wp_parse_id_list( $gm_ids ) ) . ')',
    1857                         'item_id = ' . absint( $query->query_vars['group_id'] ),
    1858                         $wpdb->prepare( "component = %s", buddypress()->groups->id ),
    1859                 );
    1860 
    1861                 $sql['where'] = 'WHERE ' . implode( ' AND ', $sql['where'] );
    1862 
    1863                 $group_user_ids = $wpdb->get_results( "{$sql['select']} {$sql['where']} {$sql['groupby']} {$sql['orderby']} {$sql['order']}" );
    1864 
    1865                 return wp_list_pluck( $group_user_ids, 'user_id' );
    1866         }
    1867 }
    1868 
    1869 /**
    1870  * BuddyPress Group Membership object.
    1871  */
    1872 class BP_Groups_Member {
    1873 
    1874         /**
    1875          * ID of the membership.
    1876          *
    1877          * @access public
    1878          * @var int
    1879          */
    1880         var $id;
    1881 
    1882         /**
    1883          * ID of the group associated with the membership.
    1884          *
    1885          * @access public
    1886          * @var int
    1887          */
    1888         var $group_id;
    1889 
    1890         /**
    1891          * ID of the user associated with the membership.
    1892          *
    1893          * @access public
    1894          * @var int
    1895          */
    1896         var $user_id;
    1897 
    1898         /**
    1899          * ID of the user whose invitation initiated the membership.
    1900          *
    1901          * @access public
    1902          * @var int
    1903          */
    1904         var $inviter_id;
    1905 
    1906         /**
    1907          * Whether the member is an admin of the group.
    1908          *
    1909          * @access public
    1910          * @var int
    1911          */
    1912         var $is_admin;
    1913 
    1914         /**
    1915          * Whether the member is a mod of the group.
    1916          *
    1917          * @access public
    1918          * @var int
    1919          */
    1920         var $is_mod;
    1921 
    1922         /**
    1923          * Whether the member is banned from the group.
    1924          *
    1925          * @access public
    1926          * @var int
    1927          */
    1928         var $is_banned;
    1929 
    1930         /**
    1931          * Title used to describe the group member's role in the group.
    1932          *
    1933          * Eg, 'Group Admin'.
    1934          *
    1935          * @access public
    1936          * @var int
    1937          */
    1938         var $user_title;
    1939 
    1940         /**
    1941          * Last modified date of the membership.
    1942          *
    1943          * This value is updated when, eg, invitations are accepted.
    1944          *
    1945          * @access public
    1946          * @var string
    1947          */
    1948         var $date_modified;
    1949 
    1950         /**
    1951          * Whether the membership has been confirmed.
    1952          *
    1953          * @access public
    1954          * @var int
    1955          */
    1956         var $is_confirmed;
    1957 
    1958         /**
    1959          * Comments associated with the membership.
    1960          *
    1961          * In BP core, these are limited to the optional message users can
    1962          * include when requesting membership to a private group.
    1963          *
    1964          * @access public
    1965          * @var string
    1966          */
    1967         var $comments;
    1968 
    1969         /**
    1970          * Whether an invitation has been sent for this membership.
    1971          *
    1972          * The purpose of this flag is to mark when an invitation has been
    1973          * "drafted" (the user has been added via the interface at Send
    1974          * Invites), but the Send button has not been pressed, so the
    1975          * invitee has not yet been notified.
    1976          *
    1977          * @access public
    1978          * @var int
    1979          */
    1980         var $invite_sent;
    1981 
    1982         /**
    1983          * WP_User object representing the membership's user.
    1984          *
    1985          * @access public
    1986          * @var WP_User
    1987          */
    1988         var $user;
    1989 
    1990         /**
    1991          * Constructor method.
    1992          *
    1993          * @param int $user_id Optional. Along with $group_id, can be used to
    1994          *        look up a membership.
    1995          * @param int $group_id Optional. Along with $user_id, can be used to
    1996          *        look up a membership.
    1997          * @param int $id Optional. The unique ID of the membership object.
    1998          * @param bool $populate Whether to populate the properties of the
    1999          *        located membership. Default: true.
    2000          */
    2001         public function __construct( $user_id = 0, $group_id = 0, $id = false, $populate = true ) {
    2002 
    2003                 // User and group are not empty, and ID is
    2004                 if ( !empty( $user_id ) && !empty( $group_id ) && empty( $id ) ) {
    2005                         $this->user_id  = $user_id;
    2006                         $this->group_id = $group_id;
    2007 
    2008                         if ( !empty( $populate ) ) {
    2009                                 $this->populate();
    2010                         }
    2011                 }
    2012 
    2013                 // ID is not empty
    2014                 if ( !empty( $id ) ) {
    2015                         $this->id = $id;
    2016 
    2017                         if ( !empty( $populate ) ) {
    2018                                 $this->populate();
    2019                         }
    2020                 }
    2021         }
    2022 
    2023         /**
    2024          * Populate the object's properties.
    2025          */
    2026         public function populate() {
    2027                 global $wpdb;
    2028 
    2029                 $bp = buddypress();
    2030 
    2031                 if ( $this->user_id && $this->group_id && !$this->id )
    2032                         $sql = $wpdb->prepare( "SELECT * FROM {$bp->groups->table_name_members} WHERE user_id = %d AND group_id = %d", $this->user_id, $this->group_id );
    2033 
    2034                 if ( !empty( $this->id ) )
    2035                         $sql = $wpdb->prepare( "SELECT * FROM {$bp->groups->table_name_members} WHERE id = %d", $this->id );
    2036 
    2037                 $member = $wpdb->get_row($sql);
    2038 
    2039                 if ( !empty( $member ) ) {
    2040                         $this->id            = $member->id;
    2041                         $this->group_id      = $member->group_id;
    2042                         $this->user_id       = $member->user_id;
    2043                         $this->inviter_id    = $member->inviter_id;
    2044                         $this->is_admin      = $member->is_admin;
    2045                         $this->is_mod        = $member->is_mod;
    2046                         $this->is_banned     = $member->is_banned;
    2047                         $this->user_title    = $member->user_title;
    2048                         $this->date_modified = $member->date_modified;
    2049                         $this->is_confirmed  = $member->is_confirmed;
    2050                         $this->comments      = $member->comments;
    2051                         $this->invite_sent   = $member->invite_sent;
    2052 
    2053                         $this->user = new BP_Core_User( $this->user_id );
    2054                 }
    2055         }
    2056 
    2057         /**
    2058          * Save the membership data to the database.
    2059          *
    2060          * @return bool True on success, false on failure.
    2061          */
    2062         public function save() {
    2063                 global $wpdb;
    2064 
    2065                 $bp = buddypress();
    2066 
    2067                 $this->user_id       = apply_filters( 'groups_member_user_id_before_save',       $this->user_id,       $this->id );
    2068                 $this->group_id      = apply_filters( 'groups_member_group_id_before_save',      $this->group_id,      $this->id );
    2069                 $this->inviter_id    = apply_filters( 'groups_member_inviter_id_before_save',    $this->inviter_id,    $this->id );
    2070                 $this->is_admin      = apply_filters( 'groups_member_is_admin_before_save',      $this->is_admin,      $this->id );
    2071                 $this->is_mod        = apply_filters( 'groups_member_is_mod_before_save',        $this->is_mod,        $this->id );
    2072                 $this->is_banned     = apply_filters( 'groups_member_is_banned_before_save',     $this->is_banned,     $this->id );
    2073                 $this->user_title    = apply_filters( 'groups_member_user_title_before_save',    $this->user_title,    $this->id );
    2074                 $this->date_modified = apply_filters( 'groups_member_date_modified_before_save', $this->date_modified, $this->id );
    2075                 $this->is_confirmed  = apply_filters( 'groups_member_is_confirmed_before_save',  $this->is_confirmed,  $this->id );
    2076                 $this->comments      = apply_filters( 'groups_member_comments_before_save',      $this->comments,      $this->id );
    2077                 $this->invite_sent   = apply_filters( 'groups_member_invite_sent_before_save',   $this->invite_sent,   $this->id );
    2078 
    2079                 do_action_ref_array( 'groups_member_before_save', array( &$this ) );
    2080 
    2081                 if ( !empty( $this->id ) ) {
    2082                         $sql = $wpdb->prepare( "UPDATE {$bp->groups->table_name_members} SET inviter_id = %d, is_admin = %d, is_mod = %d, is_banned = %d, user_title = %s, date_modified = %s, is_confirmed = %d, comments = %s, invite_sent = %d WHERE id = %d", $this->inviter_id, $this->is_admin, $this->is_mod, $this->is_banned, $this->user_title, $this->date_modified, $this->is_confirmed, $this->comments, $this->invite_sent, $this->id );
    2083                 } else {
    2084                         // Ensure that user is not already a member of the group before inserting
    2085                         if ( $wpdb->get_var( $wpdb->prepare( "SELECT id FROM {$bp->groups->table_name_members} WHERE user_id = %d AND group_id = %d AND is_confirmed = 1 LIMIT 1", $this->user_id, $this->group_id ) ) ) {
    2086                                 return false;
    2087                         }
    2088 
    2089                         $sql = $wpdb->prepare( "INSERT INTO {$bp->groups->table_name_members} ( user_id, group_id, inviter_id, is_admin, is_mod, is_banned, user_title, date_modified, is_confirmed, comments, invite_sent ) VALUES ( %d, %d, %d, %d, %d, %d, %s, %s, %d, %s, %d )", $this->user_id, $this->group_id, $this->inviter_id, $this->is_admin, $this->is_mod, $this->is_banned, $this->user_title, $this->date_modified, $this->is_confirmed, $this->comments, $this->invite_sent );
    2090                 }
    2091 
    2092                 if ( !$wpdb->query( $sql ) )
    2093                         return false;
    2094 
    2095                 $this->id = $wpdb->insert_id;
    2096 
    2097                 // Update the user's group count
    2098                 self::refresh_total_group_count_for_user( $this->user_id );
    2099 
    2100                 // Update the group's member count
    2101                 self::refresh_total_member_count_for_group( $this->group_id );
    2102 
    2103                 do_action_ref_array( 'groups_member_after_save', array( &$this ) );
    2104 
    2105                 return true;
    2106         }
    2107 
    2108         /**
    2109          * Promote a member to a new status.
    2110          *
    2111          * @param string $status The new status. 'mod' or 'admin'.
    2112          * @return bool True on success, false on failure.
    2113          */
    2114         public function promote( $status = 'mod' ) {
    2115                 if ( 'mod' == $status ) {
    2116                         $this->is_admin   = 0;
    2117                         $this->is_mod     = 1;
    2118                         $this->user_title = __( 'Group Mod', 'buddypress' );
    2119                 }
    2120 
    2121                 if ( 'admin' == $status ) {
    2122                         $this->is_admin   = 1;
    2123                         $this->is_mod     = 0;
    2124                         $this->user_title = __( 'Group Admin', 'buddypress' );
    2125                 }
    2126 
    2127                 return $this->save();
    2128         }
    2129 
    2130         /**
    2131          * Demote membership to Member status (non-admin, non-mod).
    2132          *
    2133          * @return bool True on success, false on failure.
    2134          */
    2135         public function demote() {
    2136                 $this->is_mod     = 0;
    2137                 $this->is_admin   = 0;
    2138                 $this->user_title = false;
    2139 
    2140                 return $this->save();
    2141         }
    2142 
    2143         /**
    2144          * Ban the user from the group.
    2145          *
    2146          * @return bool True on success, false on failure.
    2147          */
    2148         public function ban() {
    2149                 if ( !empty( $this->is_admin ) )
    2150                         return false;
    2151 
    2152                 $this->is_mod = 0;
    2153                 $this->is_banned = 1;
    2154 
    2155                 return $this->save();
    2156         }
    2157 
    2158         /**
    2159          * Unban the user from the group.
    2160          *
    2161          * @return bool True on success, false on failure.
    2162          */
    2163         public function unban() {
    2164                 if ( !empty( $this->is_admin ) )
    2165                         return false;
    2166 
    2167                 $this->is_banned = 0;
    2168 
    2169                 return $this->save();
    2170         }
    2171 
    2172         /**
    2173          * Mark a pending invitation as accepted.
    2174          */
    2175         public function accept_invite() {
    2176                 $this->inviter_id    = 0;
    2177                 $this->is_confirmed  = 1;
    2178                 $this->date_modified = bp_core_current_time();
    2179         }
    2180 
    2181         /**
    2182          * Confirm a membership request.
    2183          */
    2184         public function accept_request() {
    2185                 $this->is_confirmed = 1;
    2186                 $this->date_modified = bp_core_current_time();
    2187         }
    2188 
    2189         /**
    2190          * Remove the current membership.
    2191          *
    2192          * @return bool True on success, false on failure.
    2193          */
    2194         public function remove() {
    2195                 global $wpdb;
    2196 
    2197                 $bp  = buddypress();
    2198                 $sql = $wpdb->prepare( "DELETE FROM {$bp->groups->table_name_members} WHERE user_id = %d AND group_id = %d", $this->user_id, $this->group_id );
    2199 
    2200                 if ( !$result = $wpdb->query( $sql ) )
    2201                         return false;
    2202 
    2203                 // Update the user's group count
    2204                 self::refresh_total_group_count_for_user( $this->user_id );
    2205 
    2206                 // Update the group's member count
    2207                 self::refresh_total_member_count_for_group( $this->group_id );
    2208 
    2209                 return $result;
    2210         }
    2211 
    2212         /** Static Methods ****************************************************/
    2213 
    2214         /**
    2215          * Refresh the total_group_count for a user.
    2216          *
    2217          * @since BuddyPress (1.8.0)
    2218          *
    2219          * @param int $user_id ID of the user.
    2220          * @return bool True on success, false on failure.
    2221          */
    2222         public static function refresh_total_group_count_for_user( $user_id ) {
    2223                 return bp_update_user_meta( $user_id, 'total_group_count', (int) self::total_group_count( $user_id ) );
    2224         }
    2225 
    2226         /**
    2227          * Refresh the total_member_count for a group.
    2228          *
    2229          * @since BuddyPress (1.8.0)
    2230          *
    2231          * @param int $group_id ID of the group.
    2232          * @return bool True on success, false on failure.
    2233          */
    2234         public static function refresh_total_member_count_for_group( $group_id ) {
    2235                 return groups_update_groupmeta( $group_id, 'total_member_count', (int) BP_Groups_Group::get_total_member_count( $group_id ) );
    2236         }
    2237 
    2238         /**
    2239          * Delete a membership, based on user + group IDs.
    2240          *
    2241          * @param int $user_id ID of the user.
    2242          * @param int $group_id ID of the group.
    2243          * @return True on success, false on failure.
    2244          */
    2245         public static function delete( $user_id, $group_id ) {
    2246                 global $wpdb;
    2247 
    2248                 $bp = buddypress();
    2249                 $remove = $wpdb->query( $wpdb->prepare( "DELETE FROM {$bp->groups->table_name_members} WHERE user_id = %d AND group_id = %d", $user_id, $group_id ) );
    2250 
    2251                 // Update the user's group count
    2252                 self::refresh_total_group_count_for_user( $user_id );
    2253 
    2254                 // Update the group's member count
    2255                 self::refresh_total_member_count_for_group( $group_id );
    2256 
    2257                 return $remove;
    2258         }
    2259 
    2260         /**
    2261          * Get the IDs of the groups of which a specified user is a member.
    2262          *
    2263          * @param int $user_id ID of the user.
    2264          * @param int $limit Optional. Max number of results to return.
    2265          *        Default: false (no limit).
    2266          * @param int $page Optional. Page offset of results to return.
    2267          *        Default: false (no limit).
    2268          * @return array {
    2269          *     @type array $groups Array of groups returned by paginated query.
    2270          *     @type int $total Count of groups matching query.
    2271          * }
    2272          */
    2273         public static function get_group_ids( $user_id, $limit = false, $page = false ) {
    2274                 global $wpdb;
    2275 
    2276                 $pag_sql = '';
    2277                 if ( !empty( $limit ) && !empty( $page ) )
    2278                         $pag_sql = $wpdb->prepare( " LIMIT %d, %d", intval( ( $page - 1 ) * $limit), intval( $limit ) );
    2279 
    2280                 $bp = buddypress();
    2281 
    2282                 // If the user is logged in and viewing their own groups, we can show hidden and private groups
    2283                 if ( $user_id != bp_loggedin_user_id() ) {
    2284                         $group_sql = $wpdb->prepare( "SELECT DISTINCT m.group_id FROM {$bp->groups->table_name_members} m, {$bp->groups->table_name} g WHERE g.status != 'hidden' AND m.user_id = %d AND m.is_confirmed = 1 AND m.is_banned = 0{$pag_sql}", $user_id );
    2285                         $total_groups = $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(DISTINCT m.group_id) FROM {$bp->groups->table_name_members} m, {$bp->groups->table_name} g WHERE g.status != 'hidden' AND m.user_id = %d AND m.is_confirmed = 1 AND m.is_banned = 0", $user_id ) );
    2286                 } else {
    2287                         $group_sql = $wpdb->prepare( "SELECT DISTINCT group_id FROM {$bp->groups->table_name_members} WHERE user_id = %d AND is_confirmed = 1 AND is_banned = 0{$pag_sql}", $user_id );
    2288                         $total_groups = $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(DISTINCT group_id) FROM {$bp->groups->table_name_members} WHERE user_id = %d AND is_confirmed = 1 AND is_banned = 0", $user_id ) );
    2289                 }
    2290 
    2291                 $groups = $wpdb->get_col( $group_sql );
    2292 
    2293                 return array( 'groups' => $groups, 'total' => (int) $total_groups );
    2294         }
    2295 
    2296         /**
    2297          * Get the IDs of the groups of which a specified user is a member, sorted by the date joined.
    2298          *
    2299          * @param int $user_id ID of the user.
    2300          * @param int $limit Optional. Max number of results to return.
    2301          *        Default: false (no limit).
    2302          * @param int $page Optional. Page offset of results to return.
    2303          *        Default: false (no limit).
    2304          * @param string $filter Optional. Limit results to groups whose name or
    2305          *        description field matches search terms.
    2306          * @return array {
    2307          *     @type array $groups Array of groups returned by paginated query.
    2308          *     @type int $total Count of groups matching query.
    2309          * }
    2310          */
    2311         public static function get_recently_joined( $user_id, $limit = false, $page = false, $filter = false ) {
    2312                 global $wpdb;
    2313 
    2314                 $user_id_sql = $pag_sql = $hidden_sql = $filter_sql = '';
    2315 
    2316                 $user_id_sql = $wpdb->prepare( 'm.user_id = %d', $user_id );
    2317 
    2318                 if ( !empty( $limit ) && !empty( $page ) )
    2319                         $pag_sql = $wpdb->prepare( " LIMIT %d, %d", intval( ( $page - 1 ) * $limit), intval( $limit ) );
    2320 
    2321                 if ( !empty( $filter ) ) {
    2322                         $search_terms_like = '%' . bp_esc_like( $filter ) . '%';
    2323                         $filter_sql = $wpdb->prepare( " AND ( g.name LIKE %s OR g.description LIKE %s )", $search_terms_like, $search_terms_like );
    2324                 }
    2325 
    2326                 if ( $user_id != bp_loggedin_user_id() )
    2327                         $hidden_sql = " AND g.status != 'hidden'";
    2328 
    2329                 $bp = buddypress();
    2330 
    2331                 $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}{$filter_sql} AND {$user_id_sql} AND m.is_confirmed = 1 AND m.is_banned = 0 ORDER BY m.date_modified DESC {$pag_sql}" );
    2332                 $total_groups = $wpdb->get_var( "SELECT COUNT(DISTINCT m.group_id) FROM {$bp->groups->table_name_members} m, {$bp->groups->table_name} g WHERE m.group_id = g.id{$hidden_sql}{$filter_sql} AND {$user_id_sql} AND m.is_banned = 0 AND m.is_confirmed = 1 ORDER BY m.date_modified DESC" );
    2333 
    2334                 return array( 'groups' => $paged_groups, 'total' => $total_groups );
    2335         }
    2336 
    2337         /**
    2338          * Get the IDs of the groups of which a specified user is an admin.
    2339          *
    2340          * @param int $user_id ID of the user.
    2341          * @param int $limit Optional. Max number of results to return.
    2342          *        Default: false (no limit).
    2343          * @param int $page Optional. Page offset of results to return.
    2344          *        Default: false (no limit).
    2345          * @param string $filter Optional. Limit results to groups whose name or
    2346          *        description field matches search terms.
    2347          * @return array {
    2348          *     @type array $groups Array of groups returned by paginated query.
    2349          *     @type int $total Count of groups matching query.
    2350          * }
    2351          */
    2352         public static function get_is_admin_of( $user_id, $limit = false, $page = false, $filter = false ) {
    2353                 global $wpdb;
    2354 
    2355                 $user_id_sql = $pag_sql = $hidden_sql = $filter_sql = '';
    2356 
    2357                 $user_id_sql = $wpdb->prepare( 'm.user_id = %d', $user_id );
    2358 
    2359                 if ( !empty( $limit ) && !empty( $page ) )
    2360                         $pag_sql = $wpdb->prepare( " LIMIT %d, %d", intval( ( $page - 1 ) * $limit), intval( $limit ) );
    2361 
    2362                 if ( !empty( $filter ) ) {
    2363                         $search_terms_like = '%' . bp_esc_like( $filter ) . '%';
    2364                         $filter_sql = $wpdb->prepare( " AND ( g.name LIKE %s OR g.description LIKE %s )", $search_terms_like, $search_terms_like );
    2365                 }
    2366 
    2367                 if ( $user_id != bp_loggedin_user_id() )
    2368                         $hidden_sql = " AND g.status != 'hidden'";
    2369 
    2370                 $bp = buddypress();
    2371 
    2372                 $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}{$filter_sql} AND {$user_id_sql} AND m.is_confirmed = 1 AND m.is_banned = 0 AND m.is_admin = 1 ORDER BY m.date_modified ASC {$pag_sql}" );
    2373                 $total_groups = $wpdb->get_var( "SELECT COUNT(DISTINCT m.group_id) FROM {$bp->groups->table_name_members} m, {$bp->groups->table_name} g WHERE m.group_id = g.id{$hidden_sql}{$filter_sql} AND {$user_id_sql} AND m.is_confirmed = 1 AND m.is_banned = 0 AND m.is_admin = 1 ORDER BY date_modified ASC" );
    2374 
    2375                 return array( 'groups' => $paged_groups, 'total' => $total_groups );
    2376         }
    2377 
    2378         /**
    2379          * Get the IDs of the groups of which a specified user is a moderator.
    2380          *
    2381          * @param int $user_id ID of the user.
    2382          * @param int $limit Optional. Max number of results to return.
    2383          *        Default: false (no limit).
    2384          * @param int $page Optional. Page offset of results to return.
    2385          *        Default: false (no limit).
    2386          * @param string $filter Optional. Limit results to groups whose name or
    2387          *        description field matches search terms.
    2388          * @return array {
    2389          *     @type array $groups Array of groups returned by paginated query.
    2390          *     @type int $total Count of groups matching query.
    2391          * }
    2392          */
    2393         public static function get_is_mod_of( $user_id, $limit = false, $page = false, $filter = false ) {
    2394                 global $wpdb;
    2395 
    2396                 $user_id_sql = $pag_sql = $hidden_sql = $filter_sql = '';
    2397 
    2398                 $user_id_sql = $wpdb->prepare( 'm.user_id = %d', $user_id );
    2399 
    2400                 if ( !empty( $limit ) && !empty( $page ) )
    2401                         $pag_sql = $wpdb->prepare( " LIMIT %d, %d", intval( ( $page - 1 ) * $limit), intval( $limit ) );
    2402 
    2403                 if ( !empty( $filter ) ) {
    2404                         $search_terms_like = '%' . bp_esc_like( $filter ) . '%';
    2405                         $filter_sql = $wpdb->prepare( " AND ( g.name LIKE %s OR g.description LIKE %s )", $search_terms_like, $search_terms_like );
    2406                 }
    2407 
    2408                 if ( $user_id != bp_loggedin_user_id() )
    2409                         $hidden_sql = " AND g.status != 'hidden'";
    2410 
    2411                 $bp = buddypress();
    2412 
    2413                 $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}{$filter_sql} AND {$user_id_sql} AND m.is_confirmed = 1 AND m.is_banned = 0 AND m.is_mod = 1 ORDER BY m.date_modified ASC {$pag_sql}" );
    2414                 $total_groups = $wpdb->get_var( "SELECT COUNT(DISTINCT m.group_id) FROM {$bp->groups->table_name_members} m, {$bp->groups->table_name} g WHERE m.group_id = g.id{$hidden_sql}{$filter_sql} AND {$user_id_sql} AND m.is_confirmed = 1 AND m.is_banned = 0 AND m.is_mod = 1 ORDER BY date_modified ASC" );
    2415 
    2416                 return array( 'groups' => $paged_groups, 'total' => $total_groups );
    2417         }
    2418 
    2419         /**
    2420          * Get the count of groups of which the specified user is a member.
    2421          *
    2422          * @param int $user_id Optional. Default: ID of the displayed user.
    2423          * @return int Group count.
    2424          */
    2425         public static function total_group_count( $user_id = 0 ) {
    2426                 global $wpdb;
    2427 
    2428                 if ( empty( $user_id ) )
    2429                         $user_id = bp_displayed_user_id();
    2430 
    2431                 $bp = buddypress();
    2432 
    2433                 if ( $user_id != bp_loggedin_user_id() && !bp_current_user_can( 'bp_moderate' ) ) {
    2434                         return $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(DISTINCT m.group_id) FROM {$bp->groups->table_name_members} m, {$bp->groups->table_name} g WHERE m.group_id = g.id AND g.status != 'hidden' AND m.user_id = %d AND m.is_confirmed = 1 AND m.is_banned = 0", $user_id ) );
    2435                 } else {
    2436                         return $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(DISTINCT m.group_id) FROM {$bp->groups->table_name_members} m, {$bp->groups->table_name} g WHERE m.group_id = g.id AND m.user_id = %d AND m.is_confirmed = 1 AND m.is_banned = 0", $user_id ) );
    2437                 }
    2438         }
    2439 
    2440         /**
    2441          * Get a user's outstanding group invitations.
    2442          *
    2443          * @param int $user_id ID of the invitee.
    2444          * @param int $limit Optional. Max number of results to return.
    2445          *        Default: false (no limit).
    2446          * @param int $page Optional. Page offset of results to return.
    2447          *        Default: false (no limit).
    2448          * @param string|array $exclude Optional. Array or comma-separated list
    2449          *        of group IDs to exclude from results.
    2450          * @return array {
    2451          *     @type array $groups Array of groups returned by paginated query.
    2452          *     @type int $total Count of groups matching query.
    2453          * }
    2454          */
    2455         public static function get_invites( $user_id, $limit = false, $page = false, $exclude = false ) {
    2456                 global $wpdb;
    2457 
    2458                 $pag_sql = ( !empty( $limit ) && !empty( $page ) ) ? $wpdb->prepare( " LIMIT %d, %d", intval( ( $page - 1 ) * $limit), intval( $limit ) ) : '';
    2459 
    2460                 if ( !empty( $exclude ) ) {
    2461                         $exclude     = implode( ',', wp_parse_id_list( $exclude ) );
    2462                         $exclude_sql = " AND g.id NOT IN ({$exclude})";
    2463                 } else {
    2464                         $exclude_sql = '';
    2465                 }
    2466 
    2467                 $bp = buddypress();
    2468 
    2469                 $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_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' AND m.is_confirmed = 0 AND m.inviter_id != 0 AND m.invite_sent = 1 AND m.user_id = %d {$exclude_sql} ORDER BY m.date_modified ASC {$pag_sql}", $user_id ) );
    2470 
    2471                 return array( 'groups' => $paged_groups, 'total' => self::get_invite_count_for_user( $user_id ) );
    2472         }
    2473 
    2474         /**
    2475          * Gets the total group invite count for a user.
    2476          *
    2477          * @since BuddyPress (2.0.0)
    2478          *
    2479          * @param int $user_id The user ID
    2480          * @return int
    2481          */
    2482         public static function get_invite_count_for_user( $user_id = 0 ) {
    2483                 global $wpdb;
    2484 
    2485                 $bp = buddypress();
    2486 
    2487                 $count = wp_cache_get( $user_id, 'bp_group_invite_count' );
    2488 
    2489                 if ( false === $count ) {
    2490                         $count = (int) $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(DISTINCT m.group_id) FROM {$bp->groups->table_name_members} m, {$bp->groups->table_name} g WHERE m.group_id = g.id AND m.is_confirmed = 0 AND m.inviter_id != 0 AND m.invite_sent = 1 AND m.user_id = %d", $user_id ) );
    2491                         wp_cache_set( $user_id, $count, 'bp_group_invite_count' );
    2492                 }
    2493 
    2494                 return $count;
    2495         }
    2496 
    2497         /**
    2498          * Check whether a user has an outstanding invitation to a given group.
    2499          *
    2500          * @param int $user_id ID of the potential invitee.
    2501          * @param int $group_id ID of the group.
    2502          * @param string $type If 'sent', results are limited to those
    2503          *        invitations that have actually been sent (non-draft).
    2504          *        Default: 'sent'.
    2505          * @return int|null The ID of the invitation if found, otherwise null.
    2506          */
    2507         public static function check_has_invite( $user_id, $group_id, $type = 'sent' ) {
    2508                 global $wpdb;
    2509 
    2510                 if ( empty( $user_id ) )
    2511                         return false;
    2512 
    2513                 $bp  = buddypress();
    2514                 $sql = "SELECT id FROM {$bp->groups->table_name_members} WHERE user_id = %d AND group_id = %d AND is_confirmed = 0 AND inviter_id != 0";
    2515 
    2516                 if ( 'sent' == $type )
    2517                         $sql .= " AND invite_sent = 1";
    2518 
    2519                 return $wpdb->get_var( $wpdb->prepare( $sql, $user_id, $group_id ) );
    2520         }
    2521 
    2522         /**
    2523          * Delete an invitation, by specifying user ID and group ID.
    2524          *
    2525          * @param int $user_id ID of the user.
    2526          * @param int $group_id ID of the group.
    2527          * @return int Number of records deleted.
    2528          */
    2529         public static function delete_invite( $user_id, $group_id ) {
    2530                 global $wpdb;
    2531 
    2532                 if ( empty( $user_id ) )
    2533                         return false;
    2534 
    2535                 $bp = buddypress();
    2536 
    2537                 return $wpdb->query( $wpdb->prepare( "DELETE FROM {$bp->groups->table_name_members} WHERE user_id = %d AND group_id = %d AND is_confirmed = 0 AND inviter_id != 0 AND invite_sent = 1", $user_id, $group_id ) );
    2538         }
    2539 
    2540         /**
    2541          * Delete an unconfirmed membership request, by user ID and group ID.
    2542          *
    2543          * @param int $user_id ID of the user.
    2544          * @param int $group_id ID of the group.
    2545          * @return int Number of records deleted.
    2546          */
    2547         public static function delete_request( $user_id, $group_id ) {
    2548                 global $wpdb;
    2549 
    2550                 if ( empty( $user_id ) )
    2551                         return false;
    2552 
    2553                 $bp = buddypress();
    2554 
    2555                 return $wpdb->query( $wpdb->prepare( "DELETE FROM {$bp->groups->table_name_members} WHERE user_id = %d AND group_id = %d AND is_confirmed = 0 AND inviter_id = 0 AND invite_sent = 0", $user_id, $group_id ) );
    2556         }
    2557 
    2558         /**
    2559          * Check whether a user is an admin of a given group.
    2560          *
    2561          * @param int $user_id ID of the user.
    2562          * @param int $group_id ID of the group.
    2563          * @param int|null ID of the membership if the user is an admin,
    2564          *        otherwise null.
    2565          */
    2566         public static function check_is_admin( $user_id, $group_id ) {
    2567                 global $wpdb;
    2568 
    2569                 if ( empty( $user_id ) )
    2570                         return false;
    2571 
    2572                 $bp = buddypress();
    2573 
    2574                 return $wpdb->query( $wpdb->prepare( "SELECT id FROM {$bp->groups->table_name_members} WHERE user_id = %d AND group_id = %d AND is_admin = 1 AND is_banned = 0", $user_id, $group_id ) );
    2575         }
    2576 
    2577         /**
    2578          * Check whether a user is a mod of a given group.
    2579          *
    2580          * @param int $user_id ID of the user.
    2581          * @param int $group_id ID of the group.
    2582          * @param int|null ID of the membership if the user is a mod,
    2583          *        otherwise null.
    2584          */
    2585         public static function check_is_mod( $user_id, $group_id ) {
    2586                 global $wpdb;
    2587 
    2588                 if ( empty( $user_id ) )
    2589                         return false;
    2590 
    2591                 $bp = buddypress();
    2592 
    2593                 return $wpdb->query( $wpdb->prepare( "SELECT id FROM {$bp->groups->table_name_members} WHERE user_id = %d AND group_id = %d AND is_mod = 1 AND is_banned = 0", $user_id, $group_id ) );
    2594         }
    2595 
    2596         /**
    2597          * Check whether a user is a member of a given group.
    2598          *
    2599          * @param int $user_id ID of the user.
    2600          * @param int $group_id ID of the group.
    2601          * @param int|null ID of the membership if the user is a member,
    2602          *        otherwise null.
    2603          */
    2604         public static function check_is_member( $user_id, $group_id ) {
    2605                 global $wpdb;
    2606 
    2607                 if ( empty( $user_id ) )
    2608                         return false;
    2609 
    2610                 $bp = buddypress();
    2611 
    2612                 return $wpdb->query( $wpdb->prepare( "SELECT id FROM {$bp->groups->table_name_members} WHERE user_id = %d AND group_id = %d AND is_confirmed = 1 AND is_banned = 0", $user_id, $group_id ) );
    2613         }
    2614 
    2615         /**
    2616          * Check whether a user is banned from a given group.
    2617          *
    2618          * @param int $user_id ID of the user.
    2619          * @param int $group_id ID of the group.
    2620          * @param int|null ID of the membership if the user is banned,
    2621          *        otherwise null.
    2622          */
    2623         public static function check_is_banned( $user_id, $group_id ) {
    2624                 global $wpdb;
    2625 
    2626                 if ( empty( $user_id ) )
    2627                         return false;
    2628 
    2629                 $bp = buddypress();
    2630 
    2631                 return $wpdb->get_var( $wpdb->prepare( "SELECT is_banned FROM {$bp->groups->table_name_members} WHERE user_id = %d AND group_id = %d", $user_id, $group_id ) );
    2632         }
    2633 
    2634         /**
    2635          * Is the specified user the creator of the group?
    2636          *
    2637          * @since BuddyPress (1.2.6)
    2638          *
    2639          * @param int $user_id ID of the user.
    2640          * @param int $group_id ID of the group.
    2641          * @return int|null ID of the group if the user is the creator,
    2642          *         otherwise false.
    2643          */
    2644         public static function check_is_creator( $user_id, $group_id ) {
    2645                 global $wpdb;
    2646 
    2647                 if ( empty( $user_id ) )
    2648                         return false;
    2649 
    2650                 $bp = buddypress();
    2651 
    2652                 return $wpdb->get_var( $wpdb->prepare( "SELECT id FROM {$bp->groups->table_name} WHERE creator_id = %d AND id = %d", $user_id, $group_id ) );
    2653         }
    2654 
    2655         /**
    2656          * Check whether a user has an outstanding membership request for a given group.
    2657          *
    2658          * @param int $user_id ID of the user.
    2659          * @param int $group_id ID of the group.
    2660          * @return int|null ID of the membership if found, otherwise false.
    2661          */
    2662         public static function check_for_membership_request( $user_id, $group_id ) {
    2663                 global $wpdb;
    2664 
    2665                 if ( empty( $user_id ) )
    2666                         return false;
    2667 
    2668                 $bp = buddypress();
    2669 
    2670                 return $wpdb->query( $wpdb->prepare( "SELECT id FROM {$bp->groups->table_name_members} WHERE user_id = %d AND group_id = %d AND is_confirmed = 0 AND is_banned = 0 AND inviter_id = 0", $user_id, $group_id ) );
    2671         }
    2672 
    2673         /**
    2674          * Get a list of randomly selected IDs of groups that the member belongs to.
    2675          *
    2676          * @param int $user_id ID of the user.
    2677          * @param int $total_groups Max number of group IDs to return. Default: 5.
    2678          * @return array Group IDs.
    2679          */
    2680         public static function get_random_groups( $user_id = 0, $total_groups = 5 ) {
    2681                 global $wpdb;
    2682 
    2683                 $bp = buddypress();
    2684 
    2685                 // If the user is logged in and viewing their random groups, we can show hidden and private groups
    2686                 if ( bp_is_my_profile() ) {
    2687                         return $wpdb->get_col( $wpdb->prepare( "SELECT DISTINCT group_id FROM {$bp->groups->table_name_members} WHERE user_id = %d AND is_confirmed = 1 AND is_banned = 0 ORDER BY rand() LIMIT %d", $user_id, $total_groups ) );
    2688                 } else {
    2689                         return $wpdb->get_col( $wpdb->prepare( "SELECT DISTINCT m.group_id FROM {$bp->groups->table_name_members} m, {$bp->groups->table_name} g WHERE m.group_id = g.id AND g.status != 'hidden' AND m.user_id = %d AND m.is_confirmed = 1 AND m.is_banned = 0 ORDER BY rand() LIMIT %d", $user_id, $total_groups ) );
    2690                 }
    2691         }
    2692 
    2693         /**
    2694          * Get the IDs of all a given group's members.
    2695          *
    2696          * @param int $group_id ID of the group.
    2697          * @return array IDs of all group members.
    2698          */
    2699         public static function get_group_member_ids( $group_id ) {
    2700                 global $wpdb;
    2701 
    2702                 $bp = buddypress();
    2703 
    2704                 return $wpdb->get_col( $wpdb->prepare( "SELECT user_id FROM {$bp->groups->table_name_members} WHERE group_id = %d AND is_confirmed = 1 AND is_banned = 0", $group_id ) );
    2705         }
    2706 
    2707         /**
    2708          * Get a list of all a given group's admins.
    2709          *
    2710          * @param int $group_id ID of the group.
    2711          * @return array Info about group admins (user_id + date_modified).
    2712          */
    2713         public static function get_group_administrator_ids( $group_id ) {
    2714                 global $wpdb;
    2715 
    2716                 $group_admins = wp_cache_get( $group_id, 'bp_group_admins' );
    2717 
    2718                 if ( false === $group_admins ) {
    2719                         $bp = buddypress();
    2720                         $group_admins = $wpdb->get_results( $wpdb->prepare( "SELECT user_id, date_modified FROM {$bp->groups->table_name_members} WHERE group_id = %d AND is_admin = 1 AND is_banned = 0", $group_id ) );
    2721 
    2722                         wp_cache_set( $group_id, $group_admins, 'bp_group_admins' );
    2723                 }
    2724 
    2725                 return $group_admins;
    2726         }
    2727 
    2728         /**
    2729          * Get a list of all a given group's moderators.
    2730          *
    2731          * @param int $group_id ID of the group.
    2732          * @return array Info about group mods (user_id + date_modified).
    2733          */
    2734         public static function get_group_moderator_ids( $group_id ) {
    2735                 global $wpdb;
    2736 
    2737                 $bp = buddypress();
    2738 
    2739                 return $wpdb->get_results( $wpdb->prepare( "SELECT user_id, date_modified FROM {$bp->groups->table_name_members} WHERE group_id = %d AND is_mod = 1 AND is_banned = 0", $group_id ) );
    2740         }
    2741 
    2742         /**
    2743          * Get the IDs users with outstanding membership requests to the group.
    2744          *
    2745          * @param int $group_id ID of the group.
    2746          * @return array IDs of users with outstanding membership requests.
    2747          */
    2748         public static function get_all_membership_request_user_ids( $group_id ) {
    2749                 global $wpdb;
    2750 
    2751                 $bp = buddypress();
    2752 
    2753                 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 = 0", $group_id ) );
    2754         }
    2755 
    2756         /**
    2757          * Get members of a group.
    2758          *
    2759          * @deprecated BuddyPress (1.8.0)
    2760          */
    2761         public static function get_all_for_group( $group_id, $limit = false, $page = false, $exclude_admins_mods = true, $exclude_banned = true, $exclude = false ) {
    2762                 global $wpdb;
    2763 
    2764                 _deprecated_function( __METHOD__, '1.8', 'BP_Group_Member_Query' );
    2765 
    2766                 $pag_sql = '';
    2767                 if ( !empty( $limit ) && !empty( $page ) )
    2768                         $pag_sql = $wpdb->prepare( "LIMIT %d, %d", intval( ( $page - 1 ) * $limit), intval( $limit ) );
    2769 
    2770                 $exclude_admins_sql = '';
    2771                 if ( !empty( $exclude_admins_mods ) )
    2772                         $exclude_admins_sql = "AND is_admin = 0 AND is_mod = 0";
    2773 
    2774                 $banned_sql = '';
    2775                 if ( !empty( $exclude_banned ) )
    2776                         $banned_sql = " AND is_banned = 0";
    2777 
    2778                 $exclude_sql = '';
    2779                 if ( !empty( $exclude ) ) {
    2780                         $exclude     = implode( ',', wp_parse_id_list( $exclude ) );
    2781                         $exclude_sql = " AND m.user_id NOT IN ({$exclude})";
    2782                 }
    2783 
    2784                 $bp = buddypress();
    2785 
    2786                 if ( bp_is_active( 'xprofile' ) ) {
    2787                         $members = $wpdb->get_results( apply_filters( 'bp_group_members_user_join_filter', $wpdb->prepare( "SELECT m.user_id, m.date_modified, m.is_banned, u.user_login, u.user_nicename, u.user_email, pd.value as display_name FROM {$bp->groups->table_name_members} m, {$wpdb->users} u, {$bp->profile->table_name_data} pd WHERE u.ID = m.user_id AND u.ID = pd.user_id AND pd.field_id = 1 AND group_id = %d AND is_confirmed = 1 {$banned_sql} {$exclude_admins_sql} {$exclude_sql} ORDER BY m.date_modified DESC {$pag_sql}", $group_id ) ) );
    2788                 } else {
    2789                         $members = $wpdb->get_results( apply_filters( 'bp_group_members_user_join_filter', $wpdb->prepare( "SELECT m.user_id, m.date_modified, m.is_banned, u.user_login, u.user_nicename, u.user_email, u.display_name FROM {$bp->groups->table_name_members} m, {$wpdb->users} u WHERE u.ID = m.user_id AND group_id = %d AND is_confirmed = 1 {$banned_sql} {$exclude_admins_sql} {$exclude_sql} ORDER BY m.date_modified DESC {$pag_sql}", $group_id ) ) );
    2790                 }
    2791 
    2792                 if ( empty( $members ) ) {
    2793                         return false;
    2794                 }
    2795 
    2796                 if ( empty( $pag_sql ) ) {
    2797                         $total_member_count = count( $members );
    2798                 } else {
    2799                         $total_member_count = $wpdb->get_var( apply_filters( 'bp_group_members_count_user_join_filter', $wpdb->prepare( "SELECT COUNT(user_id) FROM {$bp->groups->table_name_members} m WHERE group_id = %d AND is_confirmed = 1 {$banned_sql} {$exclude_admins_sql} {$exclude_sql}", $group_id ) ) );
    2800                 }
    2801 
    2802                 // Fetch whether or not the user is a friend
    2803                 foreach ( (array) $members as $user )
    2804                         $user_ids[] = $user->user_id;
    2805 
    2806                 $user_ids = implode( ',', wp_parse_id_list( $user_ids ) );
    2807 
    2808                 if ( bp_is_active( 'friends' ) ) {
    2809                         $friend_status = $wpdb->get_results( $wpdb->prepare( "SELECT initiator_user_id, friend_user_id, is_confirmed FROM {$bp->friends->table_name} WHERE (initiator_user_id = %d AND friend_user_id IN ( {$user_ids} ) ) OR (initiator_user_id IN ( {$user_ids} ) AND friend_user_id = %d )", bp_loggedin_user_id(), bp_loggedin_user_id() ) );
    2810                         for ( $i = 0, $count = count( $members ); $i < $count; ++$i ) {
    2811                                 foreach ( (array) $friend_status as $status ) {
    2812                                         if ( $status->initiator_user_id == $members[$i]->user_id || $status->friend_user_id == $members[$i]->user_id ) {
    2813                                                 $members[$i]->is_friend = $status->is_confirmed;
    2814                                         }
    2815                                 }
    2816                         }
    2817                 }
    2818 
    2819                 return array( 'members' => $members, 'count' => $total_member_count );
    2820         }
    2821 
    2822         /**
    2823          * Delete all memberships for a given group.
    2824          *
    2825          * @param int $group_id ID of the group.
    2826          * @return int Number of records deleted.
    2827          */
    2828         public static function delete_all( $group_id ) {
    2829                 global $wpdb;
    2830 
    2831                 $bp = buddypress();
    2832 
    2833                 return $wpdb->query( $wpdb->prepare( "DELETE FROM {$bp->groups->table_name_members} WHERE group_id = %d", $group_id ) );
    2834         }
    2835 
    2836         /**
    2837          * Delete all group membership information for the specified user.
    2838          *
    2839          * @since BuddyPress (1.0.0)
    2840          *
    2841          * @param int $user_id ID of the user.
    2842          */
    2843         public static function delete_all_for_user( $user_id ) {
    2844                 global $wpdb;
    2845 
    2846                 $bp = buddypress();
    2847 
    2848                 // Get all the group ids for the current user's groups and update counts
    2849                 $group_ids = BP_Groups_Member::get_group_ids( $user_id );
    2850                 foreach ( $group_ids['groups'] as $group_id ) {
    2851                         groups_update_groupmeta( $group_id, 'total_member_count', groups_get_total_member_count( $group_id ) - 1 );
    2852 
    2853                         // If current user is the creator of a group and is the sole admin, delete that group to avoid counts going out-of-sync
    2854                         if ( groups_is_user_admin( $user_id, $group_id ) && count( groups_get_group_admins( $group_id ) ) < 2 && groups_is_user_creator( $user_id, $group_id ) )
    2855                                 groups_delete_group( $group_id );
    2856                 }
    2857 
    2858                 return $wpdb->query( $wpdb->prepare( "DELETE FROM {$bp->groups->table_name_members} WHERE user_id = %d", $user_id ) );
    2859         }
    2860 }
    2861 
    2862 /**
    2863  * API for creating group extensions without having to hardcode the content into
    2864  * the theme.
    2865  *
    2866  * To implement, extend this class. In your constructor, pass an optional array
    2867  * of arguments to parent::init() to configure your widget. The config array
    2868  * supports the following values:
    2869  *   - 'slug' A unique identifier for your extension. This value will be used
    2870  *     to build URLs, so make it URL-safe.
    2871  *   - 'name' A translatable name for your extension. This value is used to
    2872  *     populate the navigation tab, as well as the default titles for admin/
    2873  *     edit/create tabs.
    2874  *   - 'visibility' Set to 'public' (default) for your extension (the main tab
    2875  *     as well as the widget) to be available to anyone who can access the
    2876  *     group, 'private' otherwise.
    2877  *   - 'nav_item_position' An integer explaining where the nav item should
    2878  *     appear in the tab list.
    2879  *   - 'enable_nav_item' Set to true for your extension's main tab to be
    2880  *     available to anyone who can access the group.
    2881  *   - 'nav_item_name' The translatable text you want to appear in the nav tab.
    2882  *     Defaults to the value of 'name'.
    2883  *   - 'display_hook' The WordPress action that the widget_display() method is
    2884  *     hooked to.
    2885  *   - 'template_file' The template file that will be used to load the content
    2886  *     of your main extension tab. Defaults to 'groups/single/plugins.php'.
    2887  *   - 'screens' A multi-dimensional array, described below.
    2888  *       - 'access' Which users can visit the plugin's tab.
    2889  *       - 'show_tab' Which users can see the plugin's navigation tab.
    2890  *
    2891  * BP_Group_Extension uses the concept of "settings screens". There are three
    2892  * contexts for settings screens:
    2893  *   - 'create', which inserts a new step into the group creation process
    2894  *   - 'edit', which adds a tab for your extension into the Admin section of
    2895  *     a group
    2896  *   - 'admin', which adds a metabox to the Groups administration panel in the
    2897  *     WordPress Dashboard
    2898  * Each of these settings screens is populated by a pair of methods: one that
    2899  * creates the markup for the screen, and one that processes form data
    2900  * submitted from the screen. If your plugin needs screens in all three
    2901  * contexts, and if the markup and form processing logic will be the same in
    2902  * each case, you can define two methods to handle all of the screens:
    2903  *   function settings_screen() {}
    2904  *   function settings_screen_save() {}
    2905  * If one or more of your settings screen needs separate logic, you may define
    2906  * context-specific methods, for example:
    2907  *   function edit_screen() {}
    2908  *   function edit_screen_save() {}
    2909  * BP_Group_Extension will use the more specific methods if they are available.
    2910  *
    2911  * You can further customize the settings screens (tab names, etc) by passing
    2912  * an optional 'screens' parameter to the init array. The format is as follows:
    2913  *   'screens' => array(
    2914  *       'create' => array(
    2915  *           'slug' => 'foo',
    2916  *           'name' => 'Foo',
    2917  *           'position' => 55,
    2918  *           'screen_callback' => 'my_create_screen_callback',
    2919  *           'screen_save_callback' => 'my_create_screen_save_callback',
    2920  *       ),
    2921  *       'edit' => array( // ...
    2922  *   ),
    2923  * Only provide those arguments that you actually want to change from the
    2924  * default configuration. BP_Group_Extension will do the rest.
    2925  *
    2926  * Note that the 'edit' screen accepts an additional parameter: 'submit_text',
    2927  * which defines the text of the Submit button automatically added to the Edit
    2928  * screen of the extension (defaults to 'Save Changes'). Also, the 'admin'
    2929  * screen accepts two additional parameters: 'metabox_priority' and
    2930  * 'metabox_context'. See the docs for add_meta_box() for more details on these
    2931  * arguments.
    2932  *
    2933  * Prior to BuddyPress 1.7, group extension configurations were set slightly
    2934  * differently. The legacy method is still supported, though deprecated.
    2935  *
    2936  * @package BuddyPress
    2937  * @subpackage Groups
    2938  * @since BuddyPress (1.1.0)
    2939  */
    2940 class BP_Group_Extension {
    2941 
    2942         /** Public ************************************************************/
    2943 
    2944         /**
    2945          * Information about this extension's screens.
    2946          *
    2947          * @since BuddyPress (1.8.0)
    2948          * @access public
    2949          * @var array
    2950          */
    2951         public $screens = array();
    2952 
    2953         /**
    2954          * The name of the extending class.
    2955          *
    2956          * @since BuddyPress (1.8.0)
    2957          * @access public
    2958          * @var string
    2959          */
    2960         public $class_name = '';
    2961 
    2962         /**
    2963          * A ReflectionClass object of the current extension.
    2964          *
    2965          * @since BuddyPress (1.8.0)
    2966          * @access public
    2967          * @var ReflectionClass
    2968          */
    2969         public $class_reflection = null;
    2970 
    2971         /**
    2972          * Parsed configuration parameters for the extension.
    2973          *
    2974          * @since BuddyPress (1.8.0)
    2975          * @access public
    2976          * @var array
    2977          */
    2978         public $params = array();
    2979 
    2980         /**
    2981          * Raw config params, as passed by the extending class.
    2982          *
    2983          * @since BuddyPress (2.1.0)
    2984          * @access public
    2985          * @var array
    2986          */
    2987         public $params_raw = array();
    2988 
    2989         /**
    2990          * The ID of the current group.
    2991          *
    2992          * @since BuddyPress (1.8.0)
    2993          * @access public
    2994          * @var int
    2995          */
    2996         public $group_id = 0;
    2997 
    2998         /**
    2999          * The slug of the current extension.
    3000          *
    3001          * @access public
    3002          * @var string
    3003          */
    3004         public $slug = '';
    3005 
    3006         /**
    3007          * The translatable name of the current extension.
    3008          *
    3009          * @access public
    3010          * @var string
    3011          */
    3012         public $name = '';
    3013 
    3014         /**
    3015          * The visibility of the extension tab. 'public' or 'private'.
    3016          *
    3017          * @access public
    3018          * @var string
    3019          */
    3020         public $visibility = 'public';
    3021 
    3022         /**
    3023          * The numeric position of the main nav item.
    3024          *
    3025          * @access public
    3026          * @var int
    3027          */
    3028         public $nav_item_position = 81;
    3029 
    3030         /**
    3031          * Whether to show the nav item.
    3032          *
    3033          * @access public
    3034          * @var bool
    3035          */
    3036         public $enable_nav_item = true;
    3037 
    3038         /**
    3039          * Whether the current user should see the navigation item.
    3040          *
    3041          * @since BuddyPress (2.1.0)
    3042          * @access public
    3043          * @var bool
    3044          */
    3045         public $user_can_see_nav_item;
    3046 
    3047         /**
    3048          * Whether the current user can visit the tab.
    3049          *
    3050          * @since BuddyPress (2.1.0)
    3051          * @access public
    3052          * @var bool
    3053          */
    3054         public $user_can_visit;
    3055 
    3056         /**
    3057          * The text of the nav item. Defaults to self::name.
    3058          *
    3059          * @access public
    3060          * @var string
    3061          */
    3062         public $nav_item_name = '';
    3063 
    3064         /**
    3065          * The WP action that self::widget_display() is attached to.
    3066          *
    3067          * Default: 'groups_custom_group_boxes'.
    3068          *
    3069          * @access public
    3070          * @var string
    3071          */
    3072         public $display_hook = 'groups_custom_group_boxes';
    3073 
    3074         /**
    3075          * The template file used to load the plugin content.
    3076          *
    3077          * Default: 'groups/single/plugins'.
    3078          *
    3079          * @access public
    3080          * @var string
    3081          */
    3082         public $template_file = 'groups/single/plugins';
    3083 
    3084         /** Protected *********************************************************/
    3085 
    3086         /**
    3087          * Has the extension been initialized?
    3088          *
    3089          * @since BuddyPress (1.8.0)
    3090          * @access protected
    3091          * @var bool
    3092          */
    3093         protected $initialized = false;
    3094 
    3095         /**
    3096          * Extension properties as set by legacy extensions.
    3097          *
    3098          * @since BuddyPress (1.8.0)
    3099          * @access protected
    3100          * @var array
    3101          */
    3102         protected $legacy_properties = array();
    3103 
    3104         /**
    3105          * Converted legacy parameters.
    3106          *
    3107          * These are the extension properties as set by legacy extensions, but
    3108          * then converted to match the new format for params.
    3109          *
    3110          * @since BuddyPress (1.8.0)
    3111          * @access protected
    3112          * @var array
    3113          */
    3114         protected $legacy_properties_converted = array();
    3115 
    3116         /**
    3117          * Redirect location as defined by post-edit save callback.
    3118          *
    3119          * @since BuddyPress (2.1.0)
    3120          * @access protected
    3121          * @var string
    3122          */
    3123         protected $post_save_redirect;
    3124 
    3125         /**
    3126          * Miscellaneous data as set by the __set() magic method.
    3127          *
    3128          * @since BuddyPress (1.8.0)
    3129          * @access protected
    3130          * @var array
    3131          */
    3132         protected $data = array();
    3133 
    3134         /** Screen Overrides **************************************************/
    3135 
    3136         /*
    3137          * Screen override methods are how your extension will display content
    3138          * and handle form submits. Your extension should only override those
    3139          * methods that it needs for its purposes.
    3140          */
    3141 
    3142         // The content of the group tab
    3143         public function display( $group_id = null ) {}
    3144 
    3145         // Content displayed in a widget sidebar, if applicable
    3146         public function widget_display() {}
    3147 
    3148         // *_screen() displays the settings form for the given context
    3149         // *_screen_save() processes data submitted via the settings form
    3150         // The settings_* methods are generic fallbacks, which can optionally
    3151         // be overridden by the more specific edit_*, create_*, and admin_*
    3152         // versions.
    3153         public function settings_screen( $group_id = null ) {}
    3154         public function settings_screen_save( $group_id = null ) {}
    3155         public function edit_screen( $group_id = null ) {}
    3156         public function edit_screen_save( $group_id = null ) {}
    3157         public function create_screen( $group_id = null ) {}
    3158         public function create_screen_save( $group_id = null ) {}
    3159         public function admin_screen( $group_id = null ) {}
    3160         public function admin_screen_save( $group_id = null ) {}
    3161 
    3162         /** Setup *************************************************************/
    3163 
    3164         /**
    3165          * Initialize the extension, using your config settings
    3166          *
    3167          * Your plugin should call this method at the very end of its
    3168          * constructor, like so:
    3169          *
    3170          *   public function __construct() {
    3171          *       $args = array(
    3172          *           'slug' => 'my-group-extension',
    3173          *           'name' => 'My Group Extension',
    3174          *           // ...
    3175          *       );
    3176          *
    3177          *       parent::init( $args );
    3178          *   }
    3179          *
    3180          * @since BuddyPress (1.8.0)
    3181          * @since BuddyPress (2.1.0) Added 'access' and 'show_tab' arguments
    3182          *                to $args.
    3183          * @param array $args {
    3184          *     Array of initialization arguments.
    3185          *     @type string $slug Unique, URL-safe identifier for your
    3186          *           extension.
    3187          *     @type string $name Translatable name for your extension. Used to
    3188          *           populate navigation items.
    3189          *     @type string $visibility Optional. Set to 'public' for your
    3190          *           extension (the main tab as well as the widget) to be
    3191          *           available to anyone who can access the group; set to
    3192          *           'private' otherwise. Default: 'public'.
    3193          *     @type int $nav_item_position Optional. Location of the nav item
    3194          *           in the tab list. Default: 81.
    3195          *     @type bool $enable_nav_item Optional. Whether the extension's
    3196          *           tab should be accessible to anyone who can view the group.
    3197          *           Default: true.
    3198          *     @type string $nav_item_name Optional. The translatable text you
    3199          *           want to appear in the nav tab. Default: the value of $name.
    3200          *     @type string $display_hook Optional. The WordPress action that
    3201          *           the widget_display() method is hooked to.
    3202          *           Default: 'groups_custom_group_boxes'.
    3203          *     @type string $template_file Optional. Theme-relative path to the
    3204          *           template file BP should use to load the content of your
    3205          *           main extension tab. Default: 'groups/single/plugins.php'.
    3206          *     @type array $screens A multi-dimensional array of configuration
    3207          *           information for the extension screens. See docblock of
    3208          *           {@link BP_Group_Extension} for more details.
    3209          *     @type string $access Which users can visit the plugin's tab.
    3210          *                       Possible values: 'anyone', 'loggedin', 'member',
    3211          *               'mod', 'admin' or 'noone'
    3212          *                       ('member', 'mod', 'admin' refer to user's role in group.)
    3213          *                       Defaults to 'anyone' for public groups and 'member' for
    3214          *                       private groups.
    3215          *     @type string $show_tab Which users can see the plugin's navigation
    3216          *                       tab.
    3217          *                       Possible values: 'anyone', 'loggedin', 'member',
    3218          *               'mod', 'admin' or 'noone'
    3219          *                       ('member', 'mod', 'admin' refer to user's role in group.)
    3220          *                       Defaults to 'anyone' for public groups and 'member' for
    3221          *                       private groups.
    3222          * }
    3223          */
    3224         public function init( $args = array() ) {
    3225                 // Store the raw arguments
    3226                 $this->params_raw = $args;
    3227 
    3228                 // Before this init() method was introduced, plugins were
    3229                 // encouraged to set their config directly. For backward
    3230                 // compatibility with these plugins, we detect whether this is
    3231                 // one of those legacy plugins, and parse any legacy arguments
    3232                 // with those passed to init()
    3233                 $this->parse_legacy_properties();
    3234                 $args = $this->parse_args_r( $args, $this->legacy_properties_converted );
    3235 
    3236                 // Parse with defaults
    3237                 $this->params = $this->parse_args_r( $args, array(
    3238                         'slug'              => $this->slug,
    3239                         'name'              => $this->name,
    3240                         'visibility'        => $this->visibility,
    3241                         'nav_item_position' => $this->nav_item_position,
    3242                         'enable_nav_item'   => (bool) $this->enable_nav_item,
    3243                         'nav_item_name'     => $this->nav_item_name,
    3244                         'display_hook'      => $this->display_hook,
    3245                         'template_file'     => $this->template_file,
    3246                         'screens'           => $this->get_default_screens(),
    3247                         'access'            => null,
    3248                         'show_tab'          => null,
    3249                 ) );
    3250 
    3251                 $this->initialized = true;
    3252         }
    3253 
    3254         /**
    3255          * The main setup routine for the extension.
    3256          *
    3257          * This method contains the primary logic for setting up an extension's
    3258          * configuration, setting up backward compatibility for legacy plugins,
    3259          * and hooking the extension's screen functions into WP and BP.
    3260          *
    3261          * Marked 'public' because it must be accessible to add_action().
    3262          * However, you should never need to invoke this method yourself - it
    3263          * is called automatically at the right point in the load order by
    3264          * bp_register_group_extension().
    3265          *
    3266          * @since BuddyPress (1.1.0)
    3267          */
    3268         public function _register() {
    3269 
    3270                 // Detect and parse properties set by legacy extensions
    3271                 $this->parse_legacy_properties();
    3272 
    3273                 // Initialize, if necessary. This should only happen for
    3274                 // legacy extensions that don't call parent::init() themselves
    3275                 if ( true !== $this->initialized ) {
    3276                         $this->init();
    3277                 }
    3278 
    3279                 // Set some config values, based on the parsed params
    3280                 $this->group_id          = $this->get_group_id();
    3281                 $this->slug              = $this->params['slug'];
    3282                 $this->name              = $this->params['name'];
    3283                 $this->visibility        = $this->params['visibility'];
    3284                 $this->nav_item_position = $this->params['nav_item_position'];
    3285                 $this->nav_item_name     = $this->params['nav_item_name'];
    3286                 $this->display_hook      = $this->params['display_hook'];
    3287                 $this->template_file     = $this->params['template_file'];
    3288 
    3289                 // Configure 'screens': create, admin, and edit contexts
    3290                 $this->setup_screens();
    3291 
    3292                 // Configure access-related settings
    3293                 $this->setup_access_settings();
    3294 
    3295                 // Mirror configuration data so it's accessible to plugins
    3296                 // that look for it in its old locations
    3297                 $this->setup_legacy_properties();
    3298 
    3299                 // Hook the extension into BuddyPress
    3300                 $this->setup_display_hooks();
    3301                 $this->setup_create_hooks();
    3302                 $this->setup_edit_hooks();
    3303                 $this->setup_admin_hooks();
    3304         }
    3305 
    3306         /**
    3307          * Set up some basic info about the Extension.
    3308          *
    3309          * Here we collect the name of the extending class, as well as a
    3310          * ReflectionClass that is used in get_screen_callback() to determine
    3311          * whether your extension overrides certain callback methods.
    3312          *
    3313          * @since BuddyPress (1.8.0)
    3314          */
    3315         protected function setup_class_info() {
    3316                 if ( empty( $this->class_name ) ) {
    3317                         $this->class_name = get_class( $this );
    3318                 }
    3319 
    3320                 if ( is_null( $this->class_reflection ) ) {
    3321                         $this->class_reflection = new ReflectionClass( $this->class_name );
    3322                 }
    3323         }
    3324 
    3325         /**
    3326          * Get the current group ID.
    3327          *
    3328          * Check for:
    3329          *   - current group
    3330          *   - new group
    3331          *   - group admin
    3332          *
    3333          * @since BuddyPress (1.8.0)
    3334          *
    3335          * @return int
    3336          */
    3337         public static function get_group_id() {
    3338 
    3339                 // Usually this will work
    3340                 $group_id = bp_get_current_group_id();
    3341 
    3342                 // On the admin, get the group id out of the $_GET params
    3343                 if ( empty( $group_id ) && is_admin() && ( isset( $_GET['page'] ) && ( 'bp-groups' === $_GET['page'] ) ) && ! empty( $_GET['gid'] ) ) {
    3344                         $group_id = (int) $_GET['gid'];
    3345                 }
    3346 
    3347                 // This fallback will only be hit when the create step is very
    3348                 // early
    3349                 if ( empty( $group_id ) && bp_get_new_group_id() ) {
    3350                         $group_id = bp_get_new_group_id();
    3351                 }
    3352 
    3353                 // On some setups, the group id has to be fetched out of the
    3354                 // $_POST array
    3355                 // @todo Figure out why this is happening during group creation
    3356                 if ( empty( $group_id ) && isset( $_POST['group_id'] ) ) {
    3357                         $group_id = (int) $_POST['group_id'];
    3358                 }
    3359 
    3360                 return $group_id;
    3361         }
    3362 
    3363         /**
    3364          * Gather configuration data about your screens.
    3365          *
    3366          * @since BuddyPress (1.8.0)
    3367          *
    3368          * @return array
    3369          */
    3370         protected function get_default_screens() {
    3371                 $this->setup_class_info();
    3372 
    3373                 $screens = array(
    3374                         'create' => array(
    3375                                 'position' => 81,
    3376                         ),
    3377                         'edit'   => array(
    3378                                 'submit_text' => __( 'Save Changes', 'buddypress' ),
    3379                         ),
    3380                         'admin'  => array(
    3381                                 'metabox_context'  => 'normal',
    3382                                 'metabox_priority' => 'core',
    3383                         ),
    3384                 );
    3385 
    3386                 foreach ( $screens as $context => &$screen ) {
    3387                         $screen['enabled']     = true;
    3388                         $screen['name']        = $this->name;
    3389                         $screen['slug']        = $this->slug;
    3390 
    3391                         $screen['screen_callback']      = $this->get_screen_callback( $context, 'screen'      );
    3392                         $screen['screen_save_callback'] = $this->get_screen_callback( $context, 'screen_save' );
    3393                 }
    3394 
    3395                 return $screens;
    3396         }
    3397 
    3398         /**
    3399          * Set up screens array based on params.
    3400          *
    3401          * @since BuddyPress (1.8.0)
    3402          */
    3403         protected function setup_screens() {
    3404                 foreach ( (array) $this->params['screens'] as $context => $screen ) {
    3405                         if ( empty( $screen['slug'] ) ) {
    3406                                 $screen['slug'] = $this->slug;
    3407                         }
    3408 
    3409                         if ( empty( $screen['name'] ) ) {
    3410                                 $screen['name'] = $this->name;
    3411                         }
    3412 
    3413                         $this->screens[ $context ] = $screen;
    3414                 }
    3415         }
    3416 
    3417         /**
    3418          * Set up access-related settings for this extension.
    3419          *
    3420          * @since BuddyPress (2.1.0)
    3421          */
    3422         protected function setup_access_settings() {
    3423                 // Bail if no group ID is available
    3424                 if ( empty( $this->group_id ) ) {
    3425                         return;
    3426                 }
    3427 
    3428                 // Backward compatibility
    3429                 if ( isset( $this->params['enable_nav_item'] ) ) {
    3430                         $this->enable_nav_item = (bool) $this->params['enable_nav_item'];
    3431                 }
    3432 
    3433                 // Tab Access
    3434                 $this->user_can_visit = false;
    3435 
    3436                 // Backward compatibility for components that do not provide
    3437                 // explicit 'access' parameter
    3438                 if ( empty( $this->params['access'] ) ) {
    3439                         if ( false === $this->enable_nav_item ) {
    3440                                 $this->params['access'] = 'noone';
    3441                         } else {
    3442                                 $group = groups_get_group( array(
    3443                                         'group_id' => $this->group_id,
    3444                                 ) );
    3445 
    3446                                 if ( ! empty( $group->status ) && 'public' === $group->status ) {
    3447                                         // Tabs in public groups are accessible to anyone by default
    3448                                         $this->params['access'] = 'anyone';
    3449                                 } else {
    3450                                         // All other groups have members-only as the default
    3451                                         $this->params['access'] = 'member';
    3452                                 }
    3453                         }
    3454                 }
    3455 
    3456                 // Parse multiple access conditions into an array
    3457                 $access_conditions = $this->params['access'];
    3458                 if ( ! is_array( $access_conditions ) ) {
    3459                         $access_conditions = explode( ',', $access_conditions );
    3460                 }
    3461 
    3462                 // If the current user meets at least one condition, the
    3463                 // get access
    3464                 foreach ( $access_conditions as $access_condition ) {
    3465                         if ( $this->user_meets_access_condition( $access_condition ) ) {
    3466                                 $this->user_can_visit = true;
    3467                                 break;
    3468                         }
    3469                 }
    3470 
    3471                 // Tab Visibility
    3472                 $this->user_can_see_nav_item = false;
    3473 
    3474                 // Backward compatibility for components that do not provide
    3475                 // explicit 'show_tab' parameter
    3476                 if ( empty( $this->params['show_tab'] ) ) {
    3477                         if ( false === $this->params['enable_nav_item'] ) {
    3478                                 // enable_nav_item is only false if it's been
    3479                                 // defined explicitly as such in the
    3480                                 // constructor. So we always trust this value
    3481                                 $this->params['show_tab'] = 'noone';
    3482 
    3483                         } elseif ( isset( $this->params_raw['enable_nav_item'] ) || isset( $this->params_raw['visibility'] ) ) {
    3484                                 // If enable_nav_item or visibility is passed,
    3485                                 // we assume this  is a legacy extension.
    3486                                 // Legacy behavior is that enable_nav_item=true +
    3487                                 // visibility=private implies members-only
    3488                                 if ( 'public' !== $this->visibility ) {
    3489                                         $this->params['show_tab'] = 'member';
    3490                                 } else {
    3491                                         $this->params['show_tab'] = 'anyone';
    3492                                 }
    3493 
    3494                         } else {
    3495                                 // No show_tab or enable_nav_item value is
    3496                                 // available, so match the value of 'access'
    3497                                 $this->params['show_tab'] = $this->params['access'];
    3498                         }
    3499                 }
    3500 
    3501                 // Parse multiple access conditions into an array
    3502                 $access_conditions = $this->params['show_tab'];
    3503                 if ( ! is_array( $access_conditions ) ) {
    3504                         $access_conditions = explode( ',', $access_conditions );
    3505                 }
    3506 
    3507                 // If the current user meets at least one condition, the
    3508                 // get access
    3509                 foreach ( $access_conditions as $access_condition ) {
    3510                         if ( $this->user_meets_access_condition( $access_condition ) ) {
    3511                                 $this->user_can_see_nav_item = true;
    3512                                 break;
    3513                         }
    3514                 }
    3515         }
    3516 
    3517         /**
    3518          * Check whether the current user meets an access condition.
    3519          *
    3520          * @param string $access_condition 'anyone', 'loggedin', 'member',
    3521          *        'mod', 'admin' or 'noone'.
    3522          * @return bool
    3523          */
    3524         protected function user_meets_access_condition( $access_condition ) {
    3525                 $group = groups_get_group( array(
    3526                         'group_id' => $this->group_id,
    3527                 ) );
    3528 
    3529                 switch ( $access_condition ) {
    3530                         case 'admin' :
    3531                                 $meets_condition = groups_is_user_admin( bp_loggedin_user_id(), $this->group_id );
    3532                                 break;
    3533 
    3534                         case 'mod' :
    3535                                 $meets_condition = groups_is_user_mod( bp_loggedin_user_id(), $this->group_id );
    3536                                 break;
    3537 
    3538                         case 'member' :
    3539                                 $meets_condition = groups_is_user_member( bp_loggedin_user_id(), $this->group_id );
    3540                                 break;
    3541 
    3542                         case 'loggedin' :
    3543                                 $meets_condition = is_user_logged_in();
    3544                                 break;
    3545 
    3546                         case 'noone' :
    3547                                 $meets_condition = false;
    3548                                 break;
    3549 
    3550                         case 'anyone' :
    3551                         default :
    3552                                 $meets_condition = true;
    3553                                 break;
    3554                 }
    3555 
    3556                 return $meets_condition;
    3557         }
    3558 
    3559         /** Display ***********************************************************/
    3560 
    3561         /**
    3562          * Hook this extension's group tab into BuddyPress, if necessary.
    3563          *
    3564          * @since BuddyPress (1.8.0)
    3565          */
    3566         protected function setup_display_hooks() {
    3567 
    3568                 // Bail if not a group
    3569                 if ( ! bp_is_group() ) {
    3570                         return;
    3571                 }
    3572 
    3573                 // Backward compatibility only
    3574                 if ( ( 'public' !== $this->visibility ) && ! buddypress()->groups->current_group->user_has_access ) {
    3575                         return;
    3576                 }
    3577 
    3578                 $user_can_see_nav_item = $this->user_can_see_nav_item();
    3579 
    3580                 if ( $user_can_see_nav_item ) {
    3581                         $group_permalink = bp_get_group_permalink( groups_get_current_group() );
    3582 
    3583                         bp_core_new_subnav_item( array(
    3584                                 'name'            => ! $this->nav_item_name ? $this->name : $this->nav_item_name,
    3585                                 'slug'            => $this->slug,
    3586                                 'parent_slug'     => bp_get_current_group_slug(),
    3587                                 'parent_url'      => $group_permalink,
    3588                                 'position'        => $this->nav_item_position,
    3589                                 'item_css_id'     => 'nav-' . $this->slug,
    3590                                 'screen_function' => array( &$this, '_display_hook' ),
    3591                                 'user_has_access' => $user_can_see_nav_item,
    3592                                 'no_access_url'   => $group_permalink,
    3593                         ) );
    3594 
    3595                         // When we are viewing the extension display page, set the title and options title
    3596                         if ( bp_is_current_action( $this->slug ) ) {
    3597                                 add_filter( 'bp_group_user_has_access',   array( $this, 'group_access_protection' ), 10, 2 );
    3598                                 add_action( 'bp_template_content_header', create_function( '', 'echo "' . esc_attr( $this->name ) . '";' ) );
    3599                                 add_action( 'bp_template_title',          create_function( '', 'echo "' . esc_attr( $this->name ) . '";' ) );
    3600                         }
    3601                 }
    3602 
    3603                 // Hook the group home widget
    3604                 if ( ! bp_current_action() && bp_is_current_action( 'home' ) ) {
    3605                         add_action( $this->display_hook, array( &$this, 'widget_display' ) );
    3606                 }
    3607         }
    3608 
    3609         /**
    3610          * Hook the main display method, and loads the template file
    3611          */
    3612         public function _display_hook() {
    3613                 add_action( 'bp_template_content', array( &$this, 'call_display' ) );
    3614                 bp_core_load_template( apply_filters( 'bp_core_template_plugin', $this->template_file ) );
    3615         }
    3616 
    3617         /**
    3618          * Call the display() method.
    3619          *
    3620          * We use this wrapper so that we can pass the group_id to the
    3621          * display() callback.
    3622          *
    3623          * @since BuddyPress (2.1.1)
    3624          */
    3625         public function call_display() {
    3626                 $this->display( $this->group_id );
    3627         }
    3628 
    3629         /**
    3630          * Determine whether the current user should see this nav tab.
    3631          *
    3632          * Note that this controls only the display of the navigation item.
    3633          * Access to the tab is controlled by the user_can_visit() check.
    3634          *
    3635          * @since BuddyPress (2.1.0)
    3636          *
    3637          * @return bool
    3638          */
    3639         public function user_can_see_nav_item( $user_can_see_nav_item = false ) {
    3640                 if ( 'noone' !== $this->params['show_tab'] && current_user_can( 'bp_moderate' ) ) {
    3641                         return true;
    3642                 }
    3643 
    3644                 return $this->user_can_see_nav_item;
    3645         }
    3646 
    3647         /**
    3648          * Determine whether the current user has access to visit this tab.
    3649          *
    3650          * @since BuddyPress (2.1.0)
    3651          *
    3652          * @return bool
    3653          */
    3654         public function user_can_visit( $user_can_visit = false ) {
    3655                 if ( 'noone' !== $this->params['access'] && current_user_can( 'bp_moderate' ) ) {
    3656                         return true;
    3657                 }
    3658 
    3659                 return $this->user_can_visit;
    3660         }
    3661 
    3662         /**
    3663          * Filter the access check in bp_groups_group_access_protection() for this extension.
    3664          *
    3665          * Note that $no_access_args is passed by reference, as there are some
    3666          * circumstances where the bp_core_no_access() arguments need to be
    3667          * modified before the redirect takes place.
    3668          *
    3669          * @since BuddyPress (2.1.0)
    3670          *
    3671          * @param bool $user_can_visit
    3672          * @param array $no_access_args
    3673          * @return bool
    3674          */
    3675         public function group_access_protection( $user_can_visit, &$no_access_args ) {
    3676                 $user_can_visit = $this->user_can_visit();
    3677 
    3678                 if ( ! $user_can_visit && is_user_logged_in() ) {
    3679                         $current_group = groups_get_group( array(
    3680                                 'group_id' => $this->group_id,
    3681                         ) );
    3682 
    3683                         $no_access_args['message'] = __( 'You do not have access to this content.', 'buddypress' );
    3684                         $no_access_args['root'] = bp_get_group_permalink( $current_group ) . 'home/';
    3685                         $no_access_args['redirect'] = false;
    3686                 }
    3687 
    3688                 return $user_can_visit;
    3689         }
    3690 
    3691 
    3692         /** Create ************************************************************/
    3693 
    3694         /**
    3695          * Hook this extension's Create step into BuddyPress, if necessary.
    3696          *
    3697          * @since BuddyPress (1.8.0)
    3698          */
    3699         protected function setup_create_hooks() {
    3700                 if ( ! $this->is_screen_enabled( 'create' ) ) {
    3701                         return;
    3702                 }
    3703 
    3704                 $screen = $this->screens['create'];
    3705 
    3706                 // Insert the group creation step for the new group extension
    3707                 buddypress()->groups->group_creation_steps[ $screen['slug'] ] = array(
    3708                         'name'     => $screen['name'],
    3709                         'slug'     => $screen['slug'],
    3710                         'position' => $screen['position'],
    3711                 );
    3712 
    3713                 // The maybe_ methods check to see whether the create_*
    3714                 // callbacks should be invoked (ie, are we on the
    3715                 // correct group creation step). Hooked in separate
    3716                 // methods because current creation step info not yet
    3717                 // available at this point
    3718                 add_action( 'groups_custom_create_steps', array( $this, 'maybe_create_screen' ) );
    3719                 add_action( 'groups_create_group_step_save_' . $screen['slug'], array( $this, 'maybe_create_screen_save' ) );
    3720         }
    3721 
    3722         /**
    3723          * Call the create_screen() method, if we're on the right page.
    3724          *
    3725          * @since BuddyPress (1.8.0)
    3726          */
    3727         public function maybe_create_screen() {
    3728                 if ( ! bp_is_group_creation_step( $this->screens['create']['slug'] ) ) {
    3729                         return;
    3730                 }
    3731 
    3732                 call_user_func( $this->screens['create']['screen_callback'], $this->group_id );
    3733                 $this->nonce_field( 'create' );
    3734 
    3735                 // The create screen requires an additional nonce field
    3736                 // due to a quirk in the way the templates are built
    3737                 wp_nonce_field( 'groups_create_save_' . bp_get_groups_current_create_step(), '_wpnonce', false );
    3738         }
    3739 
    3740         /**
    3741          * Call the create_screen_save() method, if we're on the right page.
    3742          *
    3743          * @since BuddyPress (1.8.0)
    3744          */
    3745         public function maybe_create_screen_save() {
    3746                 if ( ! bp_is_group_creation_step( $this->screens['create']['slug'] ) ) {
    3747                         return;
    3748                 }
    3749 
    3750                 $this->check_nonce( 'create' );
    3751                 call_user_func( $this->screens['create']['screen_save_callback'], $this->group_id );
    3752         }
    3753 
    3754         /** Edit **************************************************************/
    3755 
    3756         /**
    3757          * Hook this extension's Edit panel into BuddyPress, if necessary.
    3758          *
    3759          * @since BuddyPress (1.8.0)
    3760          */
    3761         protected function setup_edit_hooks() {
    3762                 // Bail if not in a group
    3763                 if ( ! bp_is_group() ) {
    3764                         return;
    3765                 }
    3766 
    3767                 // Bail if not an edit screen
    3768                 if ( ! $this->is_screen_enabled( 'edit' ) || ! bp_is_item_admin() ) {
    3769                         return;
    3770                 }
    3771 
    3772                 $screen = $this->screens['edit'];
    3773 
    3774                 $position = isset( $screen['position'] ) ? (int) $screen['position'] : 10;
    3775                 $position += 40;
    3776 
    3777                 $current_group = groups_get_current_group();
    3778                 $admin_link = trailingslashit( bp_get_group_permalink( $current_group ) . 'admin' );
    3779 
    3780                 $subnav_args = array(
    3781                         'name'            => $screen['name'],
    3782                         'slug'            => $screen['slug'],
    3783                         'parent_slug'     => $current_group->slug . '_manage',
    3784                         'parent_url'      => trailingslashit( bp_get_group_permalink( $current_group ) . 'admin' ),
    3785                         'user_has_access' => bp_is_item_admin(),
    3786                         'position'        => $position,
    3787                         'screen_function' => 'groups_screen_group_admin',
    3788                 );
    3789 
    3790                 // Should we add a menu to the Group's WP Admin Bar
    3791                 if ( ! empty( $screen['show_in_admin_bar'] ) ) {
    3792                         $subnav_args['show_in_admin_bar'] = true;
    3793                 }
    3794 
    3795                 // Add the tab to the manage navigation
    3796                 bp_core_new_subnav_item( $subnav_args );
    3797 
    3798                 // Catch the edit screen and forward it to the plugin template
    3799                 if ( bp_is_groups_component() && bp_is_current_action( 'admin' ) && bp_is_action_variable( $screen['slug'], 0 ) ) {
    3800                         $this->call_edit_screen_save( $this->group_id );
    3801 
    3802                         add_action( 'groups_custom_edit_steps', array( &$this, 'call_edit_screen' ) );
    3803 
    3804                         // Determine the proper template and save for later
    3805                         // loading
    3806                         if ( '' !== bp_locate_template( array( 'groups/single/home.php' ), false ) ) {
    3807                                 $this->edit_screen_template = '/groups/single/home';
    3808                         } else {
    3809                                 add_action( 'bp_template_content_header', create_function( '', 'echo "<ul class=\"content-header-nav\">"; bp_group_admin_tabs(); echo "</ul>";' ) );
    3810                                 add_action( 'bp_template_content', array( &$this, 'call_edit_screen' ) );
    3811                                 $this->edit_screen_template = '/groups/single/plugins';
    3812                         }
    3813 
    3814                         // We load the template at bp_screens, to give all
    3815                         // extensions a chance to load
    3816                         add_action( 'bp_screens', array( $this, 'call_edit_screen_template_loader' ) );
    3817                 }
    3818         }
    3819 
    3820         /**
    3821          * Call the edit_screen() method.
    3822          *
    3823          * Previous versions of BP_Group_Extension required plugins to provide
    3824          * their own Submit button and nonce fields when building markup. In
    3825          * BP 1.8, this requirement was lifted - BP_Group_Extension now handles
    3826          * all required submit buttons and nonces.
    3827          *
    3828          * We put the edit screen markup into an output buffer before echoing.
    3829          * This is so that we can check for the presence of a hardcoded submit
    3830          * button, as would be present in legacy plugins; if one is found, we
    3831          * do not auto-add our own button.
    3832          *
    3833          * @since BuddyPress (1.8.0)
    3834          */
    3835         public function call_edit_screen() {
    3836                 ob_start();
    3837                 call_user_func( $this->screens['edit']['screen_callback'], $this->group_id );
    3838                 $screen = ob_get_contents();
    3839                 ob_end_clean();
    3840 
    3841                 echo $this->maybe_add_submit_button( $screen );
    3842 
    3843                 $this->nonce_field( 'edit' );
    3844         }
    3845 
    3846         /**
    3847          * Check the nonce, and call the edit_screen_save() method.
    3848          *
    3849          * @since BuddyPress (1.8.0)
    3850          */
    3851         public function call_edit_screen_save() {
    3852                 if ( empty( $_POST ) ) {
    3853                         return;
    3854                 }
    3855 
    3856                 // When DOING_AJAX, the POST global will be populated, but we
    3857                 // should assume it's a save
    3858                 if ( defined( 'DOING_AJAX' ) && DOING_AJAX ) {
    3859                         return;
    3860                 }
    3861 
    3862                 $this->check_nonce( 'edit' );
    3863 
    3864                 // Detect whether the screen_save_callback is performing a
    3865                 // redirect, so that we don't do one of our own
    3866                 add_filter( 'wp_redirect', array( $this, 'detect_post_save_redirect' ) );
    3867 
    3868                 // Call the extension's save routine
    3869                 call_user_func( $this->screens['edit']['screen_save_callback'], $this->group_id );
    3870 
    3871                 // Clean up detection filters
    3872                 remove_filter( 'wp_redirect', array( $this, 'detect_post_save_redirect' ) );
    3873 
    3874                 // Perform a redirect only if one has not already taken place
    3875                 if ( empty( $this->post_save_redirect ) ) {
    3876                         $redirect_to = apply_filters( 'bp_group_extension_edit_screen_save_redirect', bp_get_requested_url( ) );
    3877 
    3878                         bp_core_redirect( $redirect_to );
    3879                         die();
    3880                 }
    3881         }
    3882 
    3883         /**
    3884          * Load the template that houses the Edit screen.
    3885          *
    3886          * Separated out into a callback so that it can run after all other
    3887          * Group Extensions have had a chance to register their navigation, to
    3888          * avoid missing tabs.
    3889          *
    3890          * Hooked to 'bp_screens'.
    3891          *
    3892          * @since BuddyPress (1.8.0)
    3893          * @access public So that do_action() has access. Do not call directly.
    3894          *
    3895          * @see BP_Group_Extension::setup_edit_hooks()
    3896          */
    3897         public function call_edit_screen_template_loader() {
    3898                 bp_core_load_template( $this->edit_screen_template );
    3899         }
    3900 
    3901         /**
    3902          * Add a submit button to the edit form, if it needs one.
    3903          *
    3904          * There's an inconsistency in the way that the group Edit and Create
    3905          * screens are rendered: the Create screen has a submit button built
    3906          * in, but the Edit screen does not. This function allows plugin
    3907          * authors to write markup that does not contain the submit button for
    3908          * use on both the Create and Edit screens - BP will provide the button
    3909          * if one is not found.
    3910          *
    3911          * @since BuddyPress (1.8.0)
    3912          *
    3913          * @param string $screen The screen markup, captured in the output
    3914          *        buffer.
    3915          * @param string $screen The same markup, with a submit button added.
    3916          */
    3917         protected function maybe_add_submit_button( $screen = '' ) {
    3918                 if ( $this->has_submit_button( $screen ) ) {
    3919                         return $screen;
    3920                 }
    3921 
    3922                 return $screen . sprintf(
    3923                         '<div id="%s"><input type="submit" name="save" value="%s" id="%s"></div>',
    3924                         'bp-group-edit-' . $this->slug . '-submit-wrapper',
    3925                         $this->screens['edit']['submit_text'],
    3926                         'bp-group-edit-' . $this->slug . '-submit'
    3927                 );
    3928         }
    3929 
    3930         /**
    3931          * Does the given markup have a submit button?
    3932          *
    3933          * @since BuddyPress (1.8.0)
    3934          *
    3935          * @param string $screen The markup to check.
    3936          * @return bool True if a Submit button is found, otherwise false.
    3937          */
    3938         public static function has_submit_button( $screen = '' ) {
    3939                 $pattern = "/<input[^>]+type=[\'\"]submit[\'\"]/";
    3940                 preg_match( $pattern, $screen, $matches );
    3941                 return ! empty( $matches[0] );
    3942         }
    3943 
    3944         /**
    3945          * Detect redirects hardcoded into edit_screen_save() callbacks.
    3946          *
    3947          * @since BuddyPress (2.1.0)
    3948          *
    3949          * @param string $location
    3950          * @return string
    3951          */
    3952         public function detect_post_save_redirect( $redirect = '' ) {
    3953                 if ( ! empty( $redirect ) ) {
    3954                         $this->post_save_redirect = $redirect;
    3955                 }
    3956 
    3957                 return $redirect;
    3958         }
    3959 
    3960         /** Admin *************************************************************/
    3961 
    3962         /**
    3963          * Hook this extension's Admin metabox into BuddyPress, if necessary.
    3964          *
    3965          * @since BuddyPress (1.8.0)
    3966          */
    3967         protected function setup_admin_hooks() {
    3968                 if ( ! $this->is_screen_enabled( 'admin' ) || ! is_admin() ) {
    3969                         return;
    3970                 }
    3971 
    3972                 // Hook the admin screen markup function to the content hook
    3973                 add_action( 'bp_groups_admin_meta_box_content_' . $this->slug, array( $this, 'call_admin_screen' ) );
    3974 
    3975                 // Initialize the metabox
    3976                 add_action( 'bp_groups_admin_meta_boxes', array( $this, '_meta_box_display_callback' ) );
    3977 
    3978                 // Catch the metabox save
    3979                 add_action( 'bp_group_admin_edit_after', array( $this, 'call_admin_screen_save' ), 10 );
    3980         }
    3981 
    3982         /**
    3983          * Call the admin_screen() method, and add a nonce field.
    3984          *
    3985          * @since BuddyPress (1.8.0)
    3986          */
    3987         public function call_admin_screen() {
    3988                 call_user_func( $this->screens['admin']['screen_callback'], $this->group_id );
    3989                 $this->nonce_field( 'admin' );
    3990         }
    3991 
    3992         /**
    3993          * Check the nonce, and call the admin_screen_save() method
    3994          *
    3995          * @since BuddyPress (1.8.0)
    3996          */
    3997         public function call_admin_screen_save() {
    3998                 $this->check_nonce( 'admin' );
    3999                 call_user_func( $this->screens['admin']['screen_save_callback'], $this->group_id );
    4000         }
    4001 
    4002         /**
    4003          * Create the Dashboard meta box for this extension.
    4004          *
    4005          * @since BuddyPress (1.7.0)
    4006          */
    4007         public function _meta_box_display_callback() {
    4008                 $group_id = isset( $_GET['gid'] ) ? (int) $_GET['gid'] : 0;
    4009                 $screen   = $this->screens['admin'];
    4010 
    4011                 add_meta_box(
    4012                         $screen['slug'],
    4013                         $screen['name'],
    4014                         create_function( '', 'do_action( "bp_groups_admin_meta_box_content_' . $this->slug . '", ' . $group_id . ' );' ),
    4015                         get_current_screen()->id,
    4016                         $screen['metabox_context'],
    4017                         $screen['metabox_priority']
    4018                 );
    4019         }
    4020 
    4021 
    4022         /** Utilities *********************************************************/
    4023 
    4024         /**
    4025          * Generate the nonce fields for a settings form.
    4026          *
    4027          * The nonce field name (the second param passed to wp_nonce_field)
    4028          * contains this extension's slug and is thus unique to this extension.
    4029          * This is necessary because in some cases (namely, the Dashboard),
    4030          * more than one extension may generate nonces on the same page, and we
    4031          * must avoid name clashes.
    4032          *
    4033          * @since BuddyPress (1.8.0)
    4034          *
    4035          * @param string $context Screen context. 'create', 'edit', or 'admin'.
    4036          */
    4037         public function nonce_field( $context = '' ) {
    4038                 wp_nonce_field( 'bp_group_extension_' . $this->slug . '_' . $context, '_bp_group_' . $context . '_nonce_' . $this->slug );
    4039         }
    4040 
    4041         /**
    4042          * Check the nonce on a submitted settings form.
    4043          *
    4044          * @since BuddyPress (1.8.0)
    4045          *
    4046          * @param string $context Screen context. 'create', 'edit', or 'admin'.
    4047          */
    4048         public function check_nonce( $context = '' ) {
    4049                 check_admin_referer( 'bp_group_extension_' . $this->slug . '_' . $context, '_bp_group_' . $context . '_nonce_' . $this->slug );
    4050         }
    4051 
    4052         /**
    4053          * Is the specified screen enabled?
    4054          *
    4055          * To be enabled, a screen must both have the 'enabled' key set to true
    4056          * (legacy: $this->enable_create_step, etc), and its screen_callback
    4057          * must also exist and be callable.
    4058          *
    4059          * @since BuddyPress (1.8.0)
    4060          *
    4061          * @param string $context Screen context. 'create', 'edit', or 'admin'.
    4062          * @return bool True if the screen is enabled, otherwise false.
    4063          */
    4064         public function is_screen_enabled( $context = '' ) {
    4065                 $enabled = false;
    4066 
    4067                 if ( isset( $this->screens[ $context ] ) ) {
    4068                         $enabled = $this->screens[ $context ]['enabled'] && is_callable( $this->screens[ $context ]['screen_callback'] );
    4069                 }
    4070 
    4071                 return (bool) $enabled;
    4072         }
    4073 
    4074         /**
    4075          * Get the appropriate screen callback for the specified context/type.
    4076          *
    4077          * BP Group Extensions have three special "screen contexts": create,
    4078          * admin, and edit. Each of these contexts has a corresponding
    4079          * _screen() and _screen_save() method, which allow group extension
    4080          * plugins to define different markup and logic for each context.
    4081          *
    4082          * BP also supports fallback settings_screen() and
    4083          * settings_screen_save() methods, which can be used to define markup
    4084          * and logic that is shared between context. For each context, you may
    4085          * either provide context-specific methods, or you can let BP fall back
    4086          * on the shared settings_* callbacks.
    4087          *
    4088          * For example, consider a BP_Group_Extension implementation that looks
    4089          * like this:
    4090          *
    4091          *   // ...
    4092          *   function create_screen( $group_id ) { ... }
    4093          *   function create_screen_save( $group_id ) { ... }
    4094          *   function settings_screen( $group_id ) { ... }
    4095          *   function settings_screen_save( $group_id ) { ... }
    4096          *   // ...
    4097          *
    4098          * BP_Group_Extension will use your create_* methods for the Create
    4099          * steps, and will use your generic settings_* methods for the Edit
    4100          * and Admin contexts. This schema allows plugin authors maximum
    4101          * flexibility without having to repeat themselves.
    4102          *
    4103          * The get_screen_callback() method uses a ReflectionClass object to
    4104          * determine whether your extension has provided a given callback.
    4105          *
    4106          * @since BuddyPress (1.8.0)
    4107          *
    4108          * @param string $context Screen context. 'create', 'edit', or 'admin'.
    4109          * @param string $type Screen type. 'screen' or 'screen_save'. Default:
    4110          *        'screen'.
    4111          * @return callable A callable function handle.
    4112          */
    4113         public function get_screen_callback( $context = '', $type = 'screen' ) {
    4114                 $callback = '';
    4115 
    4116                 // Try the context-specific callback first
    4117                 $method  = $context . '_' . $type;
    4118                 $rmethod = $this->class_reflection->getMethod( $method );
    4119                 if ( isset( $rmethod->class ) && $this->class_name === $rmethod->class ) {
    4120                         $callback = array( $this, $method );
    4121                 }
    4122 
    4123                 if ( empty( $callback ) ) {
    4124                         $fallback_method  = 'settings_' . $type;
    4125                         $rfallback_method = $this->class_reflection->getMethod( $fallback_method );
    4126                         if ( isset( $rfallback_method->class ) && $this->class_name === $rfallback_method->class ) {
    4127                                 $callback = array( $this, $fallback_method );
    4128                         }
    4129                 }
    4130 
    4131                 return $callback;
    4132         }
    4133 
    4134         /**
    4135          * Recursive argument parsing.
    4136          *
    4137          * This acts like a multi-dimensional version of wp_parse_args() (minus
    4138          * the querystring parsing - you must pass arrays).
    4139          *
    4140          * Values from $a override those from $b; keys in $b that don't exist
    4141          * in $a are passed through.
    4142          *
    4143          * This is different from array_merge_recursive(), both because of the
    4144          * order of preference ($a overrides $b) and because of the fact that
    4145          * array_merge_recursive() combines arrays deep in the tree, rather
    4146          * than overwriting the b array with the a array.
    4147          *
    4148          * The implementation of this function is specific to the needs of
    4149          * BP_Group_Extension, where we know that arrays will always be
    4150          * associative, and that an argument under a given key in one array
    4151          * will be matched by a value of identical depth in the other one. The
    4152          * function is NOT designed for general use, and will probably result
    4153          * in unexpected results when used with data in the wild. See, eg,
    4154          * http://core.trac.wordpress.org/ticket/19888
    4155          *
    4156          * @since BuddyPress (1.8.0)
    4157          *
    4158          * @param array $a First set of arguments.
    4159          * @param array $b Second set of arguments.
    4160          * @return array Parsed arguments.
    4161          */
    4162         public static function parse_args_r( &$a, $b ) {
    4163                 $a = (array) $a;
    4164                 $b = (array) $b;
    4165                 $r = $b;
    4166 
    4167                 foreach ( $a as $k => &$v ) {
    4168                         if ( is_array( $v ) && isset( $r[ $k ] ) ) {
    4169                                 $r[ $k ] = self::parse_args_r( $v, $r[ $k ] );
    4170                         } else {
    4171                                 $r[ $k ] = $v;
    4172                         }
    4173                 }
    4174 
    4175                 return $r;
    4176         }
    4177 
    4178         /** Legacy Support ********************************************************/
    4179 
    4180         /*
    4181          * In BuddyPress 1.8, the recommended technique for configuring
    4182          * extensions changed from directly setting various object properties
    4183          * in the class constructor, to passing a configuration array to
    4184          * parent::init(). The following methods ensure that extensions created
    4185          * in the old way continue to work, by converting legacy configuration
    4186          * data to the new format.
    4187          */
    4188 
    4189         /**
    4190          * Provide access to otherwise unavailable object properties.
    4191          *
    4192          * This magic method is here for backward compatibility with plugins
    4193          * that refer to config properties that have moved to a different
    4194          * location (such as enable_create_step, which is now at
    4195          * $this->screens['create']['enabled']
    4196          *
    4197          * The legacy_properties array is set up in
    4198          * self::setup_legacy_properties().
    4199          *
    4200          * @since BuddyPress (1.8.0)
    4201          *
    4202          * @param string $key Property name.
    4203          * @return mixed The value if found, otherwise null.
    4204          */
    4205         public function __get( $key ) {
    4206                 if ( isset( $this->legacy_properties[ $key ] ) ) {
    4207                         return $this->legacy_properties[ $key ];
    4208                 } elseif ( isset( $this->data[ $key ] ) ) {
    4209                         return $this->data[ $key ];
    4210                 } else {
    4211                         return null;
    4212                 }
    4213         }
    4214 
    4215         /**
    4216          * Provide a fallback for isset( $this->foo ) when foo is unavailable.
    4217          *
    4218          * This magic method is here for backward compatibility with plugins
    4219          * that have set their class config options directly in the class
    4220          * constructor. The parse_legacy_properties() method of the current
    4221          * class needs to check whether any legacy keys have been put into the
    4222          * $this->data array.
    4223          *
    4224          * @since BuddyPress (1.8.0)
    4225          *
    4226          * @param string $key Property name.
    4227          * @return bool True if the value is set, otherwise false.
    4228          */
    4229         public function __isset( $key ) {
    4230                 if ( isset( $this->legacy_properties[ $key ] ) ) {
    4231                         return true;
    4232                 } elseif ( isset( $this->data[ $key ] ) ) {
    4233                         return true;
    4234                 } else {
    4235                         return false;
    4236                 }
    4237         }
    4238 
    4239         /**
    4240          * Allow plugins to set otherwise unavailable object properties.
    4241          *
    4242          * This magic method is here for backward compatibility with plugins
    4243          * that may attempt to modify the group extension by manually assigning
    4244          * a value to an object property that no longer exists, such as
    4245          * $this->enable_create_step.
    4246          *
    4247          * @since BuddyPress (1.8.0)
    4248          *
    4249          * @param string $key Property name.
    4250          * @param mixed $value Property value.
    4251          */
    4252         public function __set( $key, $value ) {
    4253 
    4254                 if ( empty( $this->initialized ) ) {
    4255                         $this->data[ $key ] = $value;
    4256                 }
    4257 
    4258                 switch ( $key ) {
    4259                         case 'enable_create_step' :
    4260                                 $this->screens['create']['enabled'] = $value;
    4261                                 break;
    4262 
    4263                         case 'enable_edit_item' :
    4264                                 $this->screens['edit']['enabled'] = $value;
    4265                                 break;
    4266 
    4267                         case 'enable_admin_item' :
    4268                                 $this->screens['admin']['enabled'] = $value;
    4269                                 break;
    4270 
    4271                         case 'create_step_position' :
    4272                                 $this->screens['create']['position'] = $value;
    4273                                 break;
    4274 
    4275                         // Note: 'admin' becomes 'edit' to distinguish from Dashboard 'admin'
    4276                         case 'admin_name' :
    4277                                 $this->screens['edit']['name'] = $value;
    4278                                 break;
    4279 
    4280                         case 'admin_slug' :
    4281                                 $this->screens['edit']['slug'] = $value;
    4282                                 break;
    4283 
    4284                         case 'create_name' :
    4285                                 $this->screens['create']['name'] = $value;
    4286                                 break;
    4287 
    4288                         case 'create_slug' :
    4289                                 $this->screens['create']['slug'] = $value;
    4290                                 break;
    4291 
    4292                         case 'admin_metabox_context' :
    4293                                 $this->screens['admin']['metabox_context'] = $value;
    4294                                 break;
    4295 
    4296                         case 'admin_metabox_priority' :
    4297                                 $this->screens['admin']['metabox_priority'] = $value;
    4298                                 break;
    4299 
    4300                         default :
    4301                                 $this->data[ $key ] = $value;
    4302                                 break;
    4303                 }
    4304         }
    4305 
    4306         /**
    4307          * Return a list of legacy properties.
    4308          *
    4309          * The legacy implementation of BP_Group_Extension used all of these
    4310          * object properties for configuration. Some have been moved.
    4311          *
    4312          * @since BuddyPress (1.8.0)
    4313          *
    4314          * @return array List of legacy property keys.
    4315          */
    4316         protected function get_legacy_property_list() {
    4317                 return array(
    4318                         'name',
    4319                         'slug',
    4320                         'admin_name',
    4321                         'admin_slug',
    4322                         'create_name',
    4323                         'create_slug',
    4324                         'visibility',
    4325                         'create_step_position',
    4326                         'nav_item_position',
    4327                         'admin_metabox_context',
    4328                         'admin_metabox_priority',
    4329                         'enable_create_step',
    4330                         'enable_nav_item',
    4331                         'enable_edit_item',
    4332                         'enable_admin_item',
    4333                         'nav_item_name',
    4334                         'display_hook',
    4335                         'template_file',
    4336                 );
    4337         }
    4338 
    4339         /**
    4340          * Parse legacy properties.
    4341          *
    4342          * The old standard for BP_Group_Extension was for plugins to register
    4343          * their settings as properties in their constructor. The new method is
    4344          * to pass a config array to the init() method. In order to support
    4345          * legacy plugins, we slurp up legacy properties, and later on we'll
    4346          * parse them into the new init() array.
    4347          *
    4348          * @since BuddyPress (1.8.0)
    4349          */
    4350         protected function parse_legacy_properties() {
    4351 
    4352                 // Only run this one time
    4353                 if ( ! empty( $this->legacy_properties_converted ) ) {
    4354                         return;
    4355                 }
    4356 
    4357                 $properties = $this->get_legacy_property_list();
    4358 
    4359                 // By-reference variable for convenience
    4360                 $lpc =& $this->legacy_properties_converted;
    4361 
    4362                 foreach ( $properties as $property ) {
    4363 
    4364                         // No legacy config exists for this key
    4365                         if ( ! isset( $this->{$property} ) ) {
    4366                                 continue;
    4367                         }
    4368 
    4369                         // Grab the value and record it as appropriate
    4370                         $value = $this->{$property};
    4371 
    4372                         switch ( $property ) {
    4373                                 case 'enable_create_step' :
    4374                                         $lpc['screens']['create']['enabled'] = (bool) $value;
    4375                                         break;
    4376 
    4377                                 case 'enable_edit_item' :
    4378                                         $lpc['screens']['edit']['enabled'] = (bool) $value;
    4379                                         break;
    4380 
    4381                                 case 'enable_admin_item' :
    4382                                         $lpc['screens']['admin']['enabled'] = (bool) $value;
    4383                                         break;
    4384 
    4385                                 case 'create_step_position' :
    4386                                         $lpc['screens']['create']['position'] = $value;
    4387                                         break;
    4388 
    4389                                 // Note: 'admin' becomes 'edit' to distinguish from Dashboard 'admin'
    4390                                 case 'admin_name' :
    4391                                         $lpc['screens']['edit']['name'] = $value;
    4392                                         break;
    4393 
    4394                                 case 'admin_slug' :
    4395                                         $lpc['screens']['edit']['slug'] = $value;
    4396                                         break;
    4397 
    4398                                 case 'create_name' :
    4399                                         $lpc['screens']['create']['name'] = $value;
    4400                                         break;
    4401 
    4402                                 case 'create_slug' :
    4403                                         $lpc['screens']['create']['slug'] = $value;
    4404                                         break;
    4405 
    4406                                 case 'admin_metabox_context' :
    4407                                         $lpc['screens']['admin']['metabox_context'] = $value;
    4408                                         break;
    4409 
    4410                                 case 'admin_metabox_priority' :
    4411                                         $lpc['screens']['admin']['metabox_priority'] = $value;
    4412                                         break;
    4413 
    4414                                 default :
    4415                                         $lpc[ $property ] = $value;
    4416                                         break;
    4417                         }
    4418                 }
    4419         }
    4420 
    4421         /**
    4422          * Set up legacy properties.
    4423          *
    4424          * This method is responsible for ensuring that all legacy config
    4425          * properties are stored in an array $this->legacy_properties, so that
    4426          * they remain available to plugins that reference the variables at
    4427          * their old locations.
    4428          *
    4429          * @since BuddyPress (1.8.0)
    4430          *
    4431          * @see BP_Group_Extension::__get()
    4432          */
    4433         protected function setup_legacy_properties() {
    4434 
    4435                 // Only run this one time
    4436                 if ( ! empty( $this->legacy_properties ) ) {
    4437                         return;
    4438                 }
    4439 
    4440                 $properties = $this->get_legacy_property_list();
    4441                 $params     = $this->params;
    4442                 $lp         =& $this->legacy_properties;
    4443 
    4444                 foreach ( $properties as $property ) {
    4445                         switch ( $property ) {
    4446                                 case 'enable_create_step' :
    4447                                         $lp['enable_create_step'] = $params['screens']['create']['enabled'];
    4448                                         break;
    4449 
    4450                                 case 'enable_edit_item' :
    4451                                         $lp['enable_edit_item'] = $params['screens']['edit']['enabled'];
    4452                                         break;
    4453 
    4454                                 case 'enable_admin_item' :
    4455                                         $lp['enable_admin_item'] = $params['screens']['admin']['enabled'];
    4456                                         break;
    4457 
    4458                                 case 'create_step_position' :
    4459                                         $lp['create_step_position'] = $params['screens']['create']['position'];
    4460                                         break;
    4461 
    4462                                 // Note: 'admin' becomes 'edit' to distinguish from Dashboard 'admin'
    4463                                 case 'admin_name' :
    4464                                         $lp['admin_name'] = $params['screens']['edit']['name'];
    4465                                         break;
    4466 
    4467                                 case 'admin_slug' :
    4468                                         $lp['admin_slug'] = $params['screens']['edit']['slug'];
    4469                                         break;
    4470 
    4471                                 case 'create_name' :
    4472                                         $lp['create_name'] = $params['screens']['create']['name'];
    4473                                         break;
    4474 
    4475                                 case 'create_slug' :
    4476                                         $lp['create_slug'] = $params['screens']['create']['slug'];
    4477                                         break;
    4478 
    4479                                 case 'admin_metabox_context' :
    4480                                         $lp['admin_metabox_context'] = $params['screens']['admin']['metabox_context'];
    4481                                         break;
    4482 
    4483                                 case 'admin_metabox_priority' :
    4484                                         $lp['admin_metabox_priority'] = $params['screens']['admin']['metabox_priority'];
    4485                                         break;
    4486 
    4487                                 default :
    4488                                         // All other items get moved over
    4489                                         $lp[ $property ] = $params[ $property ];
    4490 
    4491                                         // Also reapply to the object, for backpat
    4492                                         $this->{$property} = $params[ $property ];
    4493 
    4494                                         break;
    4495                         }
    4496                 }
    4497         }
    4498 }
    4499 
    4500 /**
    4501  * Register a new Group Extension.
    4502  *
    4503  * @param string Name of the Extension class.
    4504  * @return bool|null Returns false on failure, otherwise null.
    4505  */
    4506 function bp_register_group_extension( $group_extension_class = '' ) {
    4507 
    4508         if ( ! class_exists( $group_extension_class ) ) {
    4509                 return false;
    4510         }
    4511 
    4512         // Register the group extension on the bp_init action so we have access
    4513         // to all plugins.
    4514         add_action( 'bp_init', create_function( '', '
    4515                 $extension = new ' . $group_extension_class . ';
    4516                 add_action( "bp_actions", array( &$extension, "_register" ), 8 );
    4517                 add_action( "admin_init", array( &$extension, "_register" ) );
    4518         ' ), 11 );
    4519 }
    4520 
    4521 /**
    4522  * Adds support for user at-mentions (for users in a specific Group) to the Suggestions API.
    4523  *
    4524  * @since BuddyPress (2.1.0)
    4525  */
    4526 class BP_Groups_Member_Suggestions extends BP_Members_Suggestions {
    4527 
    4528         /**
    4529          * Default arguments for this suggestions service.
    4530          *
    4531          * @since BuddyPress (2.1.0)
    4532          * @access protected
    4533          * @var array $args {
    4534          *     @type int $group_id Positive integers will restrict the search to members in that group.
    4535          *           Negative integers will restrict the search to members in every other group.
    4536          *     @type int $limit Maximum number of results to display. Default: 16.
    4537          *     @type bool $only_friends If true, only match the current user's friends. Default: false.
    4538          *     @type string $term The suggestion service will try to find results that contain this string.
    4539          *           Mandatory.
    4540          * }
    4541          */
    4542         protected $default_args = array(
    4543                 'group_id'     => 0,
    4544                 'limit'        => 16,
    4545                 'only_friends' => false,
    4546                 'term'         => '',
    4547                 'type'         => '',
    4548         );
    4549 
    4550 
    4551         /**
    4552          * Validate and sanitise the parameters for the suggestion service query.
    4553          *
    4554          * @return true|WP_Error If validation fails, return a WP_Error object. On success, return true (bool).
    4555          * @since BuddyPress (2.1.0)
    4556          */
    4557         public function validate() {
    4558                 $this->args['group_id'] = (int) $this->args['group_id'];
    4559                 $this->args             = apply_filters( 'bp_groups_member_suggestions_args', $this->args, $this );
    4560 
    4561                 // Check for invalid or missing mandatory parameters.
    4562                 if ( ! $this->args['group_id'] || ! bp_is_active( 'groups' ) ) {
    4563                         return new WP_Error( 'missing_requirement' );
    4564                 }
    4565 
    4566                 // Check that the specified group_id exists, and that the current user can access it.
    4567                 $the_group = groups_get_group( array(
    4568                         'group_id'        => absint( $this->args['group_id'] ),
    4569                         'populate_extras' => true,
    4570                 ) );
    4571 
    4572                 if ( $the_group->id === 0 || ! $the_group->user_has_access ) {
    4573                         return new WP_Error( 'access_denied' );
    4574                 }
    4575 
    4576                 return apply_filters( 'bp_groups_member_suggestions_validate_args', parent::validate(), $this );
    4577         }
    4578 
    4579         /**
    4580          * Find and return a list of username suggestions that match the query.
    4581          *
    4582          * @return array|WP_Error Array of results. If there were problems, returns a WP_Error object.
    4583          * @since BuddyPress (2.1.0)
    4584          */
    4585         public function get_suggestions() {
    4586                 $user_query = array(
    4587                         'count_total'     => '',  // Prevents total count
    4588                         'populate_extras' => false,
    4589                         'type'            => 'alphabetical',
    4590 
    4591                         'group_role'      => array( 'admin', 'member', 'mod' ),
    4592                         'page'            => 1,
    4593                         'per_page'        => $this->args['limit'],
    4594                         'search_terms'    => $this->args['term'],
    4595                         'search_wildcard' => 'right',
    4596                 );
    4597 
    4598                 // Only return matches of friends of this user.
    4599                 if ( $this->args['only_friends'] && is_user_logged_in() ) {
    4600                         $user_query['user_id'] = get_current_user_id();
    4601                 }
    4602 
    4603                 // Positive Group IDs will restrict the search to members in that group.
    4604                 if ( $this->args['group_id'] > 0 ) {
    4605                         $user_query['group_id'] = $this->args['group_id'];
    4606 
    4607                 // Negative Group IDs will restrict the search to members in every other group.
    4608                 } else {
    4609                         $group_query = array(
    4610                                 'count_total'     => '',  // Prevents total count
    4611                                 'populate_extras' => false,
    4612                                 'type'            => 'alphabetical',
    4613 
    4614                                 'group_id'        => absint( $this->args['group_id'] ),
    4615                                 'group_role'      => array( 'admin', 'member', 'mod' ),
    4616                                 'page'            => 1,
    4617                         );
    4618                         $group_users = new BP_Group_Member_Query( $group_query );
    4619 
    4620                         if ( $group_users->results ) {
    4621                                 $user_query['exclude'] = wp_list_pluck( $group_users->results, 'ID' );
    4622                         } else {
    4623                                 $user_query['include'] = array( 0 );
    4624                         }
    4625                 }
    4626 
    4627                 $user_query = apply_filters( 'bp_groups_member_suggestions_query_args', $user_query, $this );
    4628                 if ( is_wp_error( $user_query ) ) {
    4629                         return $user_query;
    4630                 }
    4631 
    4632 
    4633                 if ( isset( $user_query['group_id'] ) ) {
    4634                         $user_query = new BP_Group_Member_Query( $user_query );
    4635                 } else {
    4636                         $user_query = new BP_User_Query( $user_query );
    4637                 }
    4638 
    4639                 $results = array();
    4640                 foreach ( $user_query->results as $user ) {
    4641                         $result        = new stdClass();
    4642                         $result->ID    = $user->user_nicename;
    4643                         $result->image = bp_core_fetch_avatar( array( 'html' => false, 'item_id' => $user->ID ) );
    4644                         $result->name  = bp_core_get_user_displayname( $user->ID );
    4645 
    4646                         $results[] = $result;
    4647                 }
    4648 
    4649                 return apply_filters( 'bp_groups_member_suggestions_get_suggestions', $results, $this );
    4650         }
    4651 }
     12require __DIR__ . '/classes/class-bp-group-extension.php';
     13require __DIR__ . '/classes/class-bp-group-member-query.php';
     14require __DIR__ . '/classes/class-bp-groups-group.php';
     15require __DIR__ . '/classes/class-bp-groups-member-suggestions.php';
     16require __DIR__ . '/classes/class-bp-groups-member.php';
Note: See TracChangeset for help on using the changeset viewer.