Skip to:
Content

BuddyPress.org


Ignore:
Timestamp:
02/15/2015 12:48:56 AM (10 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-core/bp-core-classes.php

    r9471 r9485  
    1010defined( 'ABSPATH' ) || exit;
    1111
    12 /**
    13  * BuddyPress User Query class.
    14  *
    15  * Used for querying users in a BuddyPress context, in situations where WP_User_Query won't do the trick:
    16  * Member directories, the Friends component, etc.
    17  *
    18  * @since BuddyPress (1.7.0)
    19  *
    20  * @param array $query {
    21  *     Query arguments. All items are optional.
    22  *     @type string            $type            Determines sort order. Select from 'newest', 'active', 'online',
    23  *                                              'random', 'popular', 'alphabetical'. Default: 'newest'.
    24  *     @type int               $per_page Number of results to return. Default: 0 (no limit).
    25  *     @type int               $page            Page offset (together with $per_page). Default: 1.
    26  *     @type int               $user_id         ID of a user. If present, and if the friends component is activated,
    27  *                                              results will be limited to the friends of that user. Default: 0.
    28  *     @type string|bool       $search_terms    Terms to search by. Search happens across xprofile fields. Requires
    29  *                                              XProfile component. Default: false.
    30  *     @type string            $search_wildcard When searching with $search_terms, set where wildcards around the term
    31  *                                              should be positioned. Accepts 'both', 'left', 'right'. Default: 'both'.
    32  *     @type array|string|bool $include         An array or comma-separated list of user IDs to which query should
    33  *                                              be limited. Default: false.
    34  *     @type array|string|bool $exclude         An array or comma-separated list of user IDs that will be excluded from
    35  *                                              query results. Default: false.
    36  *     @type array|string|bool $user_ids        An array or comma-separated list of IDs corresponding to the users
    37  *                                              that should be returned. When this parameter is passed, it will
    38  *                                              override all others; BP User objects will be constructed using these
    39  *                                              IDs only. Default: false.
    40  *     @type array|string      $member_type     Array or comma-separated list of member types to limit results to.
    41  *     @type string|bool       $meta_key        Limit results to users that have usermeta associated with this meta_key.
    42  *                                              Usually used with $meta_value. Default: false.
    43  *     @type string|bool       $meta_value      When used with $meta_key, limits results to users whose usermeta value
    44  *                                              associated with $meta_key matches $meta_value. Default: false.
    45  *     @type array             $xprofile_query  Filter results by xprofile data. Requires the xprofile component. See
    46  *                                              {@see BP_XProfile_Query} for details.
    47  *     @type bool              $populate_extras True if you want to fetch extra metadata
    48  *                                              about returned users, such as total group and friend counts.
    49  *     @type string            $count_total     Determines how BP_User_Query will do a count of total users matching
    50  *                                              the other filter criteria. Default value is 'count_query', which does
    51  *                                              a separate SELECT COUNT query to determine the total.
    52  *                                              'sql_count_found_rows' uses SQL_COUNT_FOUND_ROWS and
    53  *                                              SELECT FOUND_ROWS(). Pass an empty string to skip the total user
    54  *                                              count query.
    55  * }
    56  */
    57 class BP_User_Query {
    58 
    59     /** Variables *************************************************************/
    60 
    61     /**
    62      * Unaltered params as passed to the constructor.
    63      *
    64      * @since BuddyPress (1.8.0)
    65      * @var array
    66      */
    67     public $query_vars_raw = array();
    68 
    69     /**
    70      * Array of variables to query with.
    71      *
    72      * @since BuddyPress (1.7.0)
    73      * @var array
    74      */
    75     public $query_vars = array();
    76 
    77     /**
    78      * List of found users and their respective data.
    79      *
    80      * @access public To allow components to manipulate them.
    81      * @since BuddyPress (1.7.0)
    82      * @var array
    83      */
    84     public $results = array();
    85 
    86     /**
    87      * Total number of found users for the current query.
    88      *
    89      * @access public To allow components to manipulate it.
    90      * @since BuddyPress (1.7.0)
    91      * @var int
    92      */
    93     public $total_users = 0;
    94 
    95     /**
    96      * List of found user IDs.
    97      *
    98      * @access public To allow components to manipulate it.
    99      * @since BuddyPress (1.7.0)
    100      * @var array
    101      */
    102     public $user_ids = array();
    103 
    104     /**
    105      * SQL clauses for the user ID query.
    106      *
    107      * @access public To allow components to manipulate it.
    108      * @since BuddyPress (1.7.0)
    109      * @var array
    110      */
    111     public $uid_clauses = array();
    112 
    113     /**
    114      * SQL table where the user ID is being fetched from.
    115      *
    116      * @since BuddyPress (2.2.0)
    117      * @access public
    118      * @var string
    119      */
    120     public $uid_table = '';
    121 
    122     /**
    123      * SQL database column name to order by.
    124      *
    125      * @since BuddyPress (1.7.0)
    126      * @var string
    127      */
    128     public $uid_name = '';
    129 
    130     /**
    131      * Standard response when the query should not return any rows.
    132      *
    133      * @access protected
    134      * @since BuddyPress (1.7.0)
    135      * @var string
    136      */
    137     protected $no_results = array( 'join' => '', 'where' => '0 = 1' );
    138 
    139 
    140     /** Methods ***************************************************************/
    141 
    142     /**
    143      * Constructor.
    144      *
    145      * @since BuddyPress (1.7.0)
    146      *
    147      * @param string|array $query See {@link BP_User_Query}.
    148      */
    149     public function __construct( $query = null ) {
    150 
    151         // Store the raw query vars for later access
    152         $this->query_vars_raw = $query;
    153 
    154         // Allow extending classes to register action/filter hooks
    155         $this->setup_hooks();
    156 
    157         if ( ! empty( $this->query_vars_raw ) ) {
    158             $this->query_vars = wp_parse_args( $this->query_vars_raw, array(
    159                 'type'            => 'newest',
    160                 'per_page'        => 0,
    161                 'page'            => 1,
    162                 'user_id'         => 0,
    163                 'search_terms'    => false,
    164                 'search_wildcard' => 'both',
    165                 'include'         => false,
    166                 'exclude'         => false,
    167                 'user_ids'        => false,
    168                 'member_type'     => '',
    169                 'meta_key'        => false,
    170                 'meta_value'      => false,
    171                 'xprofile_query'  => false,
    172                 'populate_extras' => true,
    173                 'count_total'     => 'count_query'
    174             ) );
    175 
    176             // Plugins can use this filter to modify query args
    177             // before the query is constructed
    178             do_action_ref_array( 'bp_pre_user_query_construct', array( &$this ) );
    179 
    180             // Get user ids
    181             // If the user_ids param is present, we skip the query
    182             if ( false !== $this->query_vars['user_ids'] ) {
    183                 $this->user_ids = wp_parse_id_list( $this->query_vars['user_ids'] );
    184             } else {
    185                 $this->prepare_user_ids_query();
    186                 $this->do_user_ids_query();
    187             }
    188         }
    189 
    190         // Bail if no user IDs were found
    191         if ( empty( $this->user_ids ) ) {
    192             return;
    193         }
    194 
    195         // Fetch additional data. First, using WP_User_Query
    196         $this->do_wp_user_query();
    197 
    198         // Get BuddyPress specific user data
    199         $this->populate_extras();
    200     }
    201 
    202     /**
    203      * Allow extending classes to set up action/filter hooks.
    204      *
    205      * When extending BP_User_Query, you may need to use some of its
    206      * internal hooks to modify the output. It's not convenient to call
    207      * add_action() or add_filter() in your class constructor, because
    208      * BP_User_Query::__construct() contains a fair amount of logic that
    209      * you may not want to override in your class. Define this method in
    210      * your own class if you need a place where your extending class can
    211      * add its hooks early in the query-building process. See
    212      * {@link BP_Group_Member_Query::setup_hooks()} for an example.
    213      *
    214      * @since BuddyPress (1.8.0)
    215      */
    216     public function setup_hooks() {}
    217 
    218     /**
    219      * Prepare the query for user_ids.
    220      *
    221      * @since BuddyPress (1.7.0)
    222      */
    223     public function prepare_user_ids_query() {
    224         global $wpdb;
    225 
    226         $bp = buddypress();
    227 
    228         // Default query variables used here
    229         $type         = '';
    230         $per_page     = 0;
    231         $page         = 1;
    232         $user_id      = 0;
    233         $include      = false;
    234         $search_terms = false;
    235         $exclude      = false;
    236         $meta_key     = false;
    237         $meta_value   = false;
    238 
    239         extract( $this->query_vars );
    240 
    241         // Setup the main SQL query container
    242         $sql = array(
    243             'select'  => '',
    244             'where'   => array(),
    245             'orderby' => '',
    246             'order'   => '',
    247             'limit'   => ''
    248         );
    249 
    250         /** TYPE **************************************************************/
    251 
    252         // Determines the sort order, which means it also determines where the
    253         // user IDs are drawn from (the SELECT and WHERE statements)
    254         switch ( $type ) {
    255 
    256             // 'online' query happens against the last_activity usermeta key
    257             // Filter 'bp_user_query_online_interval' to modify the
    258             // number of minutes used as an interval
    259             case 'online' :
    260                 $this->uid_name = 'user_id';
    261                 $this->uid_table = $bp->members->table_name_last_activity;
    262                 $sql['select']  = "SELECT u.{$this->uid_name} as id FROM {$this->uid_table} u";
    263                 $sql['where'][] = $wpdb->prepare( "u.component = %s AND u.type = 'last_activity'", buddypress()->members->id );
    264                 $sql['where'][] = $wpdb->prepare( "u.date_recorded >= DATE_SUB( UTC_TIMESTAMP(), INTERVAL %d MINUTE )", apply_filters( 'bp_user_query_online_interval', 15 ) );
    265                 $sql['orderby'] = "ORDER BY u.date_recorded";
    266                 $sql['order']   = "DESC";
    267 
    268                 break;
    269 
    270             // 'active', 'newest', and 'random' queries
    271             // all happen against the last_activity usermeta key
    272             case 'active' :
    273             case 'newest' :
    274             case 'random' :
    275                 $this->uid_name = 'user_id';
    276                 $this->uid_table = $bp->members->table_name_last_activity;
    277                 $sql['select']  = "SELECT u.{$this->uid_name} as id FROM {$this->uid_table} u";
    278                 $sql['where'][] = $wpdb->prepare( "u.component = %s AND u.type = 'last_activity'", buddypress()->members->id );
    279 
    280                 if ( 'newest' == $type ) {
    281                     $sql['orderby'] = "ORDER BY u.user_id";
    282                     $sql['order'] = "DESC";
    283                 } elseif ( 'random' == $type ) {
    284                     $sql['orderby'] = "ORDER BY rand()";
    285                 } else {
    286                     $sql['orderby'] = "ORDER BY u.date_recorded";
    287                     $sql['order'] = "DESC";
    288                 }
    289 
    290                 break;
    291 
    292             // 'popular' sorts by the 'total_friend_count' usermeta
    293             case 'popular' :
    294                 $this->uid_name = 'user_id';
    295                 $this->uid_table = $wpdb->usermeta;
    296                 $sql['select']  = "SELECT u.{$this->uid_name} as id FROM {$this->uid_table} u";
    297                 $sql['where'][] = $wpdb->prepare( "u.meta_key = %s", bp_get_user_meta_key( 'total_friend_count' ) );
    298                 $sql['orderby'] = "ORDER BY CONVERT(u.meta_value, SIGNED)";
    299                 $sql['order']   = "DESC";
    300 
    301                 break;
    302 
    303             // 'alphabetical' sorts depend on the xprofile setup
    304             case 'alphabetical' :
    305 
    306                 // We prefer to do alphabetical sorts against the display_name field
    307                 // of wp_users, because the table is smaller and better indexed. We
    308                 // can do so if xprofile sync is enabled, or if xprofile is inactive.
    309                 //
    310                 // @todo remove need for bp_is_active() check
    311                 if ( ! bp_disable_profile_sync() || ! bp_is_active( 'xprofile' ) ) {
    312                     $this->uid_name = 'ID';
    313                     $this->uid_table = $wpdb->users;
    314                     $sql['select']  = "SELECT u.{$this->uid_name} as id FROM {$this->uid_table} u";
    315                     $sql['orderby'] = "ORDER BY u.display_name";
    316                     $sql['order']   = "ASC";
    317 
    318                 // When profile sync is disabled, alphabetical sorts must happen against
    319                 // the xprofile table
    320                 } else {
    321                     $this->uid_name = 'user_id';
    322                     $this->uid_table = $bp->profile->table_name_data;
    323                     $sql['select']  = "SELECT u.{$this->uid_name} as id FROM {$this->uid_table} u";
    324                     $sql['where'][] = $wpdb->prepare( "u.field_id = %d", bp_xprofile_fullname_field_id() );
    325                     $sql['orderby'] = "ORDER BY u.value";
    326                     $sql['order']   = "ASC";
    327                 }
    328 
    329                 // Alphabetical queries ignore last_activity, while BP uses last_activity
    330                 // to infer spam/deleted/non-activated users. To ensure that these users
    331                 // are filtered out, we add an appropriate sub-query.
    332                 $sql['where'][] = "u.{$this->uid_name} IN ( SELECT ID FROM {$wpdb->users} WHERE " . bp_core_get_status_sql( '' ) . " )";
    333 
    334                 break;
    335 
    336             // Any other 'type' falls through
    337             default :
    338                 $this->uid_name = 'ID';
    339                 $this->uid_table = $wpdb->users;
    340                 $sql['select']  = "SELECT u.{$this->uid_name} as id FROM {$this->uid_table} u";
    341 
    342                 // In this case, we assume that a plugin is
    343                 // handling order, so we leave those clauses
    344                 // blank
    345 
    346                 break;
    347         }
    348 
    349         /** WHERE *************************************************************/
    350 
    351         // 'include' - User ids to include in the results
    352         $include     = false !== $include ? wp_parse_id_list( $include ) : array();
    353         $include_ids = $this->get_include_ids( $include );
    354         if ( ! empty( $include_ids ) ) {
    355             $include_ids    = implode( ',', wp_parse_id_list( $include_ids ) );
    356             $sql['where'][] = "u.{$this->uid_name} IN ({$include_ids})";
    357         }
    358 
    359         // 'exclude' - User ids to exclude from the results
    360         if ( false !== $exclude ) {
    361             $exclude_ids    = implode( ',', wp_parse_id_list( $exclude ) );
    362             $sql['where'][] = "u.{$this->uid_name} NOT IN ({$exclude_ids})";
    363         }
    364 
    365         // 'user_id' - When a user id is passed, limit to the friends of the user
    366         // @todo remove need for bp_is_active() check
    367         if ( ! empty( $user_id ) && bp_is_active( 'friends' ) ) {
    368             $friend_ids = friends_get_friend_user_ids( $user_id );
    369             $friend_ids = implode( ',', wp_parse_id_list( $friend_ids ) );
    370 
    371             if ( ! empty( $friend_ids ) ) {
    372                 $sql['where'][] = "u.{$this->uid_name} IN ({$friend_ids})";
    373 
    374             // If the user has no friends, the query should always
    375             // return no users
    376             } else {
    377                 $sql['where'][] = $this->no_results['where'];
    378             }
    379         }
    380 
    381         /** Search Terms ******************************************************/
    382 
    383         // 'search_terms' searches user_login and user_nicename
    384         // xprofile field matches happen in bp_xprofile_bp_user_query_search()
    385         if ( false !== $search_terms ) {
    386             $search_terms = bp_esc_like( wp_kses_normalize_entities( $search_terms ) );
    387 
    388             if ( $search_wildcard === 'left' ) {
    389                 $search_terms_nospace = '%' . $search_terms;
    390                 $search_terms_space   = '%' . $search_terms . ' %';
    391             } elseif ( $search_wildcard === 'right' ) {
    392                 $search_terms_nospace =        $search_terms . '%';
    393                 $search_terms_space   = '% ' . $search_terms . '%';
    394             } else {
    395                 $search_terms_nospace = '%' . $search_terms . '%';
    396                 $search_terms_space   = '%' . $search_terms . '%';
    397             }
    398 
    399             $sql['where']['search'] = $wpdb->prepare(
    400                 "u.{$this->uid_name} IN ( SELECT ID FROM {$wpdb->users} WHERE ( user_login LIKE %s OR user_login LIKE %s OR user_nicename LIKE %s OR user_nicename LIKE %s ) )",
    401                 $search_terms_nospace,
    402                 $search_terms_space,
    403                 $search_terms_nospace,
    404                 $search_terms_space
    405             );
    406         }
    407 
    408         // Member type.
    409         if ( ! empty( $member_type ) ) {
    410             $member_types = array();
    411 
    412             if ( ! is_array( $member_type ) ) {
    413                 $member_type = preg_split( '/[,\s+]/', $member_type );
    414             }
    415 
    416             foreach ( $member_type as $mt ) {
    417                 if ( ! bp_get_member_type_object( $mt ) ) {
    418                     continue;
    419                 }
    420 
    421                 $member_types[] = $mt;
    422             }
    423 
    424             if ( ! empty( $member_types ) ) {
    425                 $member_type_tq = new WP_Tax_Query( array(
    426                     array(
    427                         'taxonomy' => 'bp_member_type',
    428                         'field'    => 'name',
    429                         'operator' => 'IN',
    430                         'terms'    => $member_types,
    431                     ),
    432                 ) );
    433 
    434                 // Switch to the root blog, where member type taxonomies live.
    435                 switch_to_blog( bp_get_root_blog_id() );
    436 
    437                 $member_type_sql_clauses = $member_type_tq->get_sql( 'u', $this->uid_name );
    438                 restore_current_blog();
    439 
    440 
    441 
    442                 // Grab the first term_relationships clause and convert to a subquery.
    443                 if ( preg_match( '/' . $wpdb->term_relationships . '\.term_taxonomy_id IN \([0-9, ]+\)/', $member_type_sql_clauses['where'], $matches ) ) {
    444                     $sql['where']['member_type'] = "u.{$this->uid_name} IN ( SELECT object_id FROM $wpdb->term_relationships WHERE {$matches[0]} )";
    445                 }
    446             }
    447         }
    448 
    449         // 'meta_key', 'meta_value' allow usermeta search
    450         // To avoid global joins, do a separate query
    451         if ( false !== $meta_key ) {
    452             $meta_sql = $wpdb->prepare( "SELECT user_id FROM {$wpdb->usermeta} WHERE meta_key = %s", $meta_key );
    453 
    454             if ( false !== $meta_value ) {
    455                 $meta_sql .= $wpdb->prepare( " AND meta_value = %s", $meta_value );
    456             }
    457 
    458             $found_user_ids = $wpdb->get_col( $meta_sql );
    459 
    460             if ( ! empty( $found_user_ids ) ) {
    461                 $sql['where'][] = "u.{$this->uid_name} IN (" . implode( ',', wp_parse_id_list( $found_user_ids ) ) . ")";
    462             } else {
    463                 $sql['where'][] = '1 = 0';
    464             }
    465         }
    466 
    467         // 'per_page', 'page' - handles LIMIT
    468         if ( !empty( $per_page ) && !empty( $page ) ) {
    469             $sql['limit'] = $wpdb->prepare( "LIMIT %d, %d", intval( ( $page - 1 ) * $per_page ), intval( $per_page ) );
    470         } else {
    471             $sql['limit'] = '';
    472         }
    473 
    474         // Allow custom filters
    475         $sql = apply_filters_ref_array( 'bp_user_query_uid_clauses', array( $sql, &$this ) );
    476 
    477         // Assemble the query chunks
    478         $this->uid_clauses['select']  = $sql['select'];
    479         $this->uid_clauses['where']   = ! empty( $sql['where'] ) ? 'WHERE ' . implode( ' AND ', $sql['where'] ) : '';
    480         $this->uid_clauses['orderby'] = $sql['orderby'];
    481         $this->uid_clauses['order']   = $sql['order'];
    482         $this->uid_clauses['limit']   = $sql['limit'];
    483 
    484         do_action_ref_array( 'bp_pre_user_query', array( &$this ) );
    485     }
    486 
    487     /**
    488      * Query for IDs of users that match the query parameters.
    489      *
    490      * Perform a database query to specifically get only user IDs, using
    491      * existing query variables set previously in the constructor.
    492      *
    493      * Also used to quickly perform user total counts.
    494      *
    495      * @since BuddyPress (1.7.0)
    496      */
    497     public function do_user_ids_query() {
    498         global $wpdb;
    499 
    500         // If counting using SQL_CALC_FOUND_ROWS, set it up here
    501         if ( 'sql_calc_found_rows' == $this->query_vars['count_total'] ) {
    502             $this->uid_clauses['select'] = str_replace( 'SELECT', 'SELECT SQL_CALC_FOUND_ROWS', $this->uid_clauses['select'] );
    503         }
    504 
    505         // Get the specific user ids
    506         $this->user_ids = $wpdb->get_col( "{$this->uid_clauses['select']} {$this->uid_clauses['where']} {$this->uid_clauses['orderby']} {$this->uid_clauses['order']} {$this->uid_clauses['limit']}" );
    507 
    508         // Get the total user count
    509         if ( 'sql_calc_found_rows' == $this->query_vars['count_total'] ) {
    510             $this->total_users = $wpdb->get_var( apply_filters( 'bp_found_user_query', "SELECT FOUND_ROWS()", $this ) );
    511         } elseif ( 'count_query' == $this->query_vars['count_total'] ) {
    512             $count_select      = preg_replace( '/^SELECT.*?FROM (\S+) u/', "SELECT COUNT(u.{$this->uid_name}) FROM $1 u", $this->uid_clauses['select'] );
    513             $this->total_users = $wpdb->get_var( apply_filters( 'bp_found_user_query', "{$count_select} {$this->uid_clauses['where']}", $this ) );
    514         }
    515     }
    516 
    517     /**
    518      * Use WP_User_Query() to pull data for the user IDs retrieved in the main query.
    519      *
    520      * @since BuddyPress (1.7.0)
    521      */
    522     public function do_wp_user_query() {
    523         $fields = array( 'ID', 'user_login', 'user_pass', 'user_nicename', 'user_email', 'user_url', 'user_registered', 'user_activation_key', 'user_status', 'display_name' );
    524 
    525         if ( is_multisite() ) {
    526             $fields[] = 'spam';
    527             $fields[] = 'deleted';
    528         }
    529 
    530         $wp_user_query = new WP_User_Query( apply_filters( 'bp_wp_user_query_args', array(
    531 
    532             // Relevant
    533             'fields'      => $fields,
    534             'include'     => $this->user_ids,
    535 
    536             // Overrides
    537             'blog_id'     => 0,    // BP does not require blog roles
    538             'count_total' => false // We already have a count
    539 
    540         ), $this ) );
    541 
    542         // WP_User_Query doesn't cache the data it pulls from wp_users,
    543         // and it does not give us a way to save queries by fetching
    544         // only uncached users. However, BP does cache this data, so
    545         // we set it here.
    546         foreach ( $wp_user_query->results as $u ) {
    547             wp_cache_set( 'bp_core_userdata_' . $u->ID, $u, 'bp' );
    548         }
    549 
    550         // We calculate total_users using a standalone query, except
    551         // when a whitelist of user_ids is passed to the constructor.
    552         // This clause covers the latter situation, and ensures that
    553         // pagination works when querying by $user_ids.
    554         if ( empty( $this->total_users ) ) {
    555             $this->total_users = count( $wp_user_query->results );
    556         }
    557 
    558         // Reindex for easier matching
    559         $r = array();
    560         foreach ( $wp_user_query->results as $u ) {
    561             $r[ $u->ID ] = $u;
    562         }
    563 
    564         // Match up to the user ids from the main query
    565         foreach ( $this->user_ids as $uid ) {
    566             if ( isset( $r[ $uid ] ) ) {
    567                 $this->results[ $uid ] = $r[ $uid ];
    568 
    569                 // The BP template functions expect an 'id'
    570                 // (as opposed to 'ID') property
    571                 $this->results[ $uid ]->id = $uid;
    572             }
    573         }
    574     }
    575 
    576     /**
    577      * Fetch the IDs of users to put in the IN clause of the main query.
    578      *
    579      * By default, returns the value passed to it
    580      * ($this->query_vars['include']). Having this abstracted into a
    581      * standalone method means that extending classes can override the
    582      * logic, parsing together their own user_id limits with the 'include'
    583      * ids passed to the class constructor. See {@link BP_Group_Member_Query}
    584      * for an example.
    585      *
    586      * @since BuddyPress (1.8.0)
    587      *
    588      * @param array Sanitized array of user IDs, as passed to the 'include'
    589      *        parameter of the class constructor.
    590      * @return array The list of users to which the main query should be
    591      *         limited.
    592      */
    593     public function get_include_ids( $include = array() ) {
    594         return $include;
    595     }
    596 
    597     /**
    598      * Perform a database query to populate any extra metadata we might need.
    599      *
    600      * Different components will hook into the 'bp_user_query_populate_extras'
    601      * action to loop in the things they want.
    602      *
    603      * @since BuddyPress (1.7.0)
    604      *
    605      * @global WPDB $wpdb Global WordPress database access object.
    606      */
    607     public function populate_extras() {
    608         global $wpdb;
    609 
    610         // Bail if no users
    611         if ( empty( $this->user_ids ) || empty( $this->results ) ) {
    612             return;
    613         }
    614 
    615         // Bail if the populate_extras flag is set to false
    616         // In the case of the 'popular' sort type, we force
    617         // populate_extras to true, because we need the friend counts
    618         if ( 'popular' == $this->query_vars['type'] ) {
    619             $this->query_vars['populate_extras'] = 1;
    620         }
    621 
    622         if ( ! (bool) $this->query_vars['populate_extras'] ) {
    623             return;
    624         }
    625 
    626         // Turn user ID's into a query-usable, comma separated value
    627         $user_ids_sql = implode( ',', wp_parse_id_list( $this->user_ids ) );
    628 
    629         /**
    630          * Use this action to independently populate your own custom extras.
    631          *
    632          * Note that anything you add here should query using $user_ids_sql, to
    633          * avoid running multiple queries per user in the loop.
    634          *
    635          * Two BuddyPress components currently do this:
    636          * - XProfile: To override display names
    637          * - Friends:  To set whether or not a user is the current users friend
    638          *
    639          * @see bp_xprofile_filter_user_query_populate_extras()
    640          * @see bp_friends_filter_user_query_populate_extras()
    641          */
    642         do_action_ref_array( 'bp_user_query_populate_extras', array( $this, $user_ids_sql ) );
    643 
    644         // Fetch last_active data from the activity table
    645         $last_activities = BP_Core_User::get_last_activity( $this->user_ids );
    646 
    647         // Set a last_activity value for each user, even if it's empty
    648         foreach ( $this->results as $user_id => $user ) {
    649             $user_last_activity = isset( $last_activities[ $user_id ] ) ? $last_activities[ $user_id ]['date_recorded'] : '';
    650             $this->results[ $user_id ]->last_activity = $user_last_activity;
    651         }
    652 
    653         // Fetch usermeta data
    654         // We want the three following pieces of info from usermeta:
    655         // - friend count
    656         // - latest update
    657         $total_friend_count_key = bp_get_user_meta_key( 'total_friend_count' );
    658         $bp_latest_update_key   = bp_get_user_meta_key( 'bp_latest_update'   );
    659 
    660         // total_friend_count must be set for each user, even if its
    661         // value is 0
    662         foreach ( $this->results as $uindex => $user ) {
    663             $this->results[$uindex]->total_friend_count = 0;
    664         }
    665 
    666         // Create, prepare, and run the separate usermeta query
    667         $user_metas = $wpdb->get_results( $wpdb->prepare( "SELECT user_id, meta_key, meta_value FROM {$wpdb->usermeta} WHERE meta_key IN (%s,%s) AND user_id IN ({$user_ids_sql})", $total_friend_count_key, $bp_latest_update_key ) );
    668 
    669         // The $members_template global expects the index key to be different
    670         // from the meta_key in some cases, so we rejig things here.
    671         foreach ( $user_metas as $user_meta ) {
    672             switch ( $user_meta->meta_key ) {
    673                 case $total_friend_count_key :
    674                     $key = 'total_friend_count';
    675                     break;
    676 
    677                 case $bp_latest_update_key :
    678                     $key = 'latest_update';
    679                     break;
    680             }
    681 
    682             if ( isset( $this->results[ $user_meta->user_id ] ) ) {
    683                 $this->results[ $user_meta->user_id ]->{$key} = $user_meta->meta_value;
    684             }
    685         }
    686 
    687         // When meta_key or meta_value have been passed to the query,
    688         // fetch the resulting values for use in the template functions
    689         if ( ! empty( $this->query_vars['meta_key'] ) ) {
    690             $meta_sql = array(
    691                 'select' => "SELECT user_id, meta_key, meta_value",
    692                 'from'   => "FROM $wpdb->usermeta",
    693                 'where'  => $wpdb->prepare( "WHERE meta_key = %s", $this->query_vars['meta_key'] )
    694             );
    695 
    696             if ( false !== $this->query_vars['meta_value'] ) {
    697                 $meta_sql['where'] .= $wpdb->prepare( " AND meta_value = %s", $this->query_vars['meta_value'] );
    698             }
    699 
    700             $metas = $wpdb->get_results( "{$meta_sql['select']} {$meta_sql['from']} {$meta_sql['where']}" );
    701 
    702             if ( ! empty( $metas ) ) {
    703                 foreach ( $metas as $meta ) {
    704                     if ( isset( $this->results[ $meta->user_id ] ) ) {
    705                         $this->results[ $meta->user_id ]->meta_key = $meta->meta_key;
    706 
    707                         if ( ! empty( $meta->meta_value ) ) {
    708                             $this->results[ $meta->user_id ]->meta_value = $meta->meta_value;
    709                         }
    710                     }
    711                 }
    712             }
    713         }
    714     }
    715 }
    716 
    717 /**
    718  * Fetch data about a BuddyPress user.
    719  *
    720  * BP_Core_User class can be used by any component. It will fetch useful
    721  * details for any user when provided with a user_id.
    722  *
    723  * Example:
    724  *    $user = new BP_Core_User( $user_id );
    725  *    $user_avatar = $user->avatar;
    726  *    $user_email = $user->email;
    727  *    $user_status = $user->status;
    728  *    etc.
    729  */
    730 class BP_Core_User {
    731 
    732     /**
    733      * ID of the user which the object relates to.
    734      *
    735      * @var integer
    736      */
    737     public $id;
    738 
    739     /**
    740      * The URL to the full size of the avatar for the user.
    741      *
    742      * @var string
    743      */
    744     public $avatar;
    745 
    746     /**
    747      * The URL to the thumb size of the avatar for the user.
    748      *
    749      * @var string
    750      */
    751     public $avatar_thumb;
    752 
    753     /**
    754      * The URL to the mini size of the avatar for the user.
    755      *
    756      * @var string
    757      */
    758     public $avatar_mini;
    759 
    760     /**
    761      * The full name of the user
    762      *
    763      * @var string
    764      */
    765     public $fullname;
    766 
    767     /**
    768      * The email for the user.
    769      *
    770      * @var string
    771      */
    772     public $email;
    773 
    774     /**
    775      * The absolute url for the user's profile.
    776      *
    777      * @var string
    778      */
    779     public $user_url;
    780 
    781     /**
    782      * The HTML for the user link, with the link text being the user's full name.
    783      *
    784      * @var string
    785      */
    786     public $user_link;
    787 
    788     /**
    789      * Contains a formatted string when the last time the user was active.
    790      *
    791      * Example: "active 2 hours and 50 minutes ago"
    792      *
    793      * @var string
    794      */
    795     public $last_active;
    796 
    797     /* Extras */
    798 
    799     /**
    800      * The total number of "Friends" the user has on site.
    801      *
    802      * @var integer
    803      */
    804     public $total_friends;
    805 
    806     /**
    807      * The total number of blog posts posted by the user
    808      *
    809      * @var integer
    810      * @deprecated No longer used
    811      */
    812     public $total_blogs;
    813 
    814     /**
    815      * The total number of groups the user is a part of.
    816      *
    817      * Example: "1 group", "2 groups"
    818      *
    819      * @var string
    820      */
    821     public $total_groups;
    822 
    823     /**
    824      * Profile information for the specific user.
    825      *
    826      * @since BuddyPress (1.2.0)
    827      * @var array
    828      */
    829     public $profile_data;
    830 
    831     /** Public Methods *******************************************************/
    832 
    833     /**
    834      * Class constructor.
    835      *
    836      * @param integer $user_id The ID for the user being queried.
    837      * @param bool $populate_extras Whether to fetch extra information
    838      *        such as group/friendship counts or not. Default: false.
    839      */
    840     public function __construct( $user_id, $populate_extras = false ) {
    841         if ( !empty( $user_id ) ) {
    842             $this->id = $user_id;
    843             $this->populate();
    844 
    845             if ( !empty( $populate_extras ) ) {
    846                 $this->populate_extras();
    847             }
    848         }
    849     }
    850 
    851     /**
    852      * Populate the instantiated class with data based on the User ID provided.
    853      *
    854      * @uses bp_core_get_userurl() Returns the URL with no HTML markup for
    855      *       a user based on their user id.
    856      * @uses bp_core_get_userlink() Returns a HTML formatted link for a
    857      *       user with the user's full name as the link text.
    858      * @uses bp_core_get_user_email() Returns the email address for the
    859      *       user based on user ID.
    860      * @uses bp_get_user_meta() BP function returns the value of passed
    861      *       usermeta name from usermeta table.
    862      * @uses bp_core_fetch_avatar() Returns HTML formatted avatar for a user
    863      * @uses bp_profile_last_updated_date() Returns the last updated date
    864      *       for a user.
    865      */
    866     public function populate() {
    867 
    868         if ( bp_is_active( 'xprofile' ) )
    869             $this->profile_data = $this->get_profile_data();
    870 
    871         if ( !empty( $this->profile_data ) ) {
    872             $full_name_field_name = bp_xprofile_fullname_field_name();
    873 
    874             $this->user_url  = bp_core_get_user_domain( $this->id, $this->profile_data['user_nicename'], $this->profile_data['user_login'] );
    875             $this->fullname  = esc_attr( $this->profile_data[$full_name_field_name]['field_data'] );
    876             $this->user_link = "<a href='{$this->user_url}' title='{$this->fullname}'>{$this->fullname}</a>";
    877             $this->email     = esc_attr( $this->profile_data['user_email'] );
    878         } else {
    879             $this->user_url  = bp_core_get_user_domain( $this->id );
    880             $this->user_link = bp_core_get_userlink( $this->id );
    881             $this->fullname  = esc_attr( bp_core_get_user_displayname( $this->id ) );
    882             $this->email     = esc_attr( bp_core_get_user_email( $this->id ) );
    883         }
    884 
    885         // Cache a few things that are fetched often
    886         wp_cache_set( 'bp_user_fullname_' . $this->id, $this->fullname, 'bp' );
    887         wp_cache_set( 'bp_user_email_' . $this->id, $this->email, 'bp' );
    888         wp_cache_set( 'bp_user_url_' . $this->id, $this->user_url, 'bp' );
    889 
    890         $this->avatar       = bp_core_fetch_avatar( array( 'item_id' => $this->id, 'type' => 'full', 'alt' => sprintf( __( 'Profile photo of %s', 'buddypress' ), $this->fullname ) ) );
    891         $this->avatar_thumb = bp_core_fetch_avatar( array( 'item_id' => $this->id, 'type' => 'thumb', 'alt' => sprintf( __( 'Profile photo of %s', 'buddypress' ), $this->fullname ) ) );
    892         $this->avatar_mini  = bp_core_fetch_avatar( array( 'item_id' => $this->id, 'type' => 'thumb', 'alt' => sprintf( __( 'Profile photo of %s', 'buddypress' ), $this->fullname ), 'width' => 30, 'height' => 30 ) );
    893         $this->last_active  = bp_core_get_last_activity( bp_get_user_last_activity( $this->id ), __( 'active %s', 'buddypress' ) );
    894     }
    895 
    896     /**
    897      * Populates extra fields such as group and friendship counts.
    898      */
    899     public function populate_extras() {
    900 
    901         if ( bp_is_active( 'friends' ) ) {
    902             $this->total_friends = BP_Friends_Friendship::total_friend_count( $this->id );
    903         }
    904 
    905         if ( bp_is_active( 'groups' ) ) {
    906             $this->total_groups = BP_Groups_Member::total_group_count( $this->id );
    907             $this->total_groups = sprintf( _n( '%d group', '%d groups', $this->total_groups, 'buddypress' ), $this->total_groups );
    908         }
    909     }
    910 
    911     /**
    912      * Fetch xprofile data for the current user.
    913      *
    914      * @see BP_XProfile_ProfileData::get_all_for_user() for description of
    915      *      return value.
    916      *
    917      * @return array See {@link BP_XProfile_Profile_Data::get_all_for_user()}.
    918      */
    919     public function get_profile_data() {
    920         return BP_XProfile_ProfileData::get_all_for_user( $this->id );
    921     }
    922 
    923     /** Static Methods ********************************************************/
    924 
    925     /**
    926      * Get a list of users that match the query parameters.
    927      *
    928      * Since BuddyPress 1.7, use {@link BP_User_Query} instead.
    929      *
    930      * @deprecated 1.7.0 Use {@link BP_User_Query}.
    931      *
    932      * @see BP_User_Query for a description of parameters, most of which
    933      *      are used there in the same way.
    934      *
    935      * @param string $type See {@link BP_User_Query}.
    936      * @param int $limit See {@link BP_User_Query}. Default: 0.
    937      * @param int $page See {@link BP_User_Query}. Default: 1.
    938      * @param int $user_id See {@link BP_User_Query}. Default: 0.
    939      * @param mixed $include See {@link BP_User_Query}. Default: false.
    940      * @param string|bool $search_terms See {@link BP_User_Query}.
    941      *        Default: false.
    942      * @param bool $populate_extras See {@link BP_User_Query}.
    943      *        Default: true.
    944      * @param mixed $exclude See {@link BP_User_Query}. Default: false.
    945      * @param string|bool $meta_key See {@link BP_User_Query}.
    946      *        Default: false.
    947      * @param string|bool $meta_value See {@link BP_User_Query}.
    948      *        Default: false.
    949      * @return array {
    950      *     @type int $total_users Total number of users matched by query
    951      *           params.
    952      *     @type array $paged_users The current page of users matched by
    953      *           query params.
    954      * }
    955      */
    956     public static function get_users( $type, $limit = 0, $page = 1, $user_id = 0, $include = false, $search_terms = false, $populate_extras = true, $exclude = false, $meta_key = false, $meta_value = false ) {
    957         global $wpdb;
    958 
    959         _deprecated_function( __METHOD__, '1.7', 'BP_User_Query' );
    960 
    961         $bp = buddypress();
    962 
    963         $sql = array();
    964 
    965         $sql['select_main'] = "SELECT DISTINCT u.ID as id, u.user_registered, u.user_nicename, u.user_login, u.display_name, u.user_email";
    966 
    967         if ( 'active' == $type || 'online' == $type || 'newest' == $type  ) {
    968             $sql['select_active'] = ", um.meta_value as last_activity";
    969         }
    970 
    971         if ( 'popular' == $type ) {
    972             $sql['select_popular'] = ", um.meta_value as total_friend_count";
    973         }
    974 
    975         if ( 'alphabetical' == $type ) {
    976             $sql['select_alpha'] = ", pd.value as fullname";
    977         }
    978 
    979         if ( $meta_key ) {
    980             $sql['select_meta'] = ", umm.meta_key";
    981 
    982             if ( $meta_value ) {
    983                 $sql['select_meta'] .= ", umm.meta_value";
    984             }
    985         }
    986 
    987         $sql['from'] = "FROM {$wpdb->users} u LEFT JOIN {$wpdb->usermeta} um ON um.user_id = u.ID";
    988 
    989         // We search against xprofile fields, so we must join the table
    990         if ( $search_terms && bp_is_active( 'xprofile' ) ) {
    991             $sql['join_profiledata_search'] = "LEFT JOIN {$bp->profile->table_name_data} spd ON u.ID = spd.user_id";
    992         }
    993 
    994         // Alphabetical sorting is done by the xprofile Full Name field
    995         if ( 'alphabetical' == $type ) {
    996             $sql['join_profiledata_alpha'] = "LEFT JOIN {$bp->profile->table_name_data} pd ON u.ID = pd.user_id";
    997         }
    998 
    999         if ( $meta_key ) {
    1000             $sql['join_meta'] = "LEFT JOIN {$wpdb->usermeta} umm ON umm.user_id = u.ID";
    1001         }
    1002 
    1003         $sql['where'] = 'WHERE ' . bp_core_get_status_sql( 'u.' );
    1004 
    1005         if ( 'active' == $type || 'online' == $type || 'newest' == $type ) {
    1006             $sql['where_active'] = $wpdb->prepare( "AND um.meta_key = %s", bp_get_user_meta_key( 'last_activity' ) );
    1007         }
    1008 
    1009         if ( 'popular' == $type ) {
    1010             $sql['where_popular'] = $wpdb->prepare( "AND um.meta_key = %s", bp_get_user_meta_key( 'total_friend_count' ) );
    1011         }
    1012 
    1013         if ( 'online' == $type ) {
    1014             $sql['where_online'] = "AND DATE_ADD( um.meta_value, INTERVAL 5 MINUTE ) >= UTC_TIMESTAMP()";
    1015         }
    1016 
    1017         if ( 'alphabetical' == $type ) {
    1018             $sql['where_alpha'] = "AND pd.field_id = 1";
    1019         }
    1020 
    1021         if ( !empty( $exclude ) ) {
    1022             $exclude              = implode( ',', wp_parse_id_list( $exclude ) );
    1023             $sql['where_exclude'] = "AND u.ID NOT IN ({$exclude})";
    1024         }
    1025 
    1026         // Passing an $include value of 0 or '0' will necessarily result in an empty set
    1027         // returned. The default value of false will hit the 'else' clause.
    1028         if ( 0 === $include || '0' === $include ) {
    1029             $sql['where_users'] = "AND 0 = 1";
    1030         } else {
    1031             if ( !empty( $include ) ) {
    1032                 $include = implode( ',',  wp_parse_id_list( $include ) );
    1033                 $sql['where_users'] = "AND u.ID IN ({$include})";
    1034             } elseif ( !empty( $user_id ) && bp_is_active( 'friends' ) ) {
    1035                 $friend_ids = friends_get_friend_user_ids( $user_id );
    1036 
    1037                 if ( !empty( $friend_ids ) ) {
    1038                     $friend_ids = implode( ',', wp_parse_id_list( $friend_ids ) );
    1039                     $sql['where_friends'] = "AND u.ID IN ({$friend_ids})";
    1040 
    1041                 // User has no friends, return false since there will be no users to fetch.
    1042                 } else {
    1043                     return false;
    1044                 }
    1045             }
    1046         }
    1047 
    1048         if ( !empty( $search_terms ) && bp_is_active( 'xprofile' ) ) {
    1049             $search_terms_like        = '%' . bp_esc_like( $search_terms ) . '%';
    1050             $sql['where_searchterms'] = $wpdb->prepare( "AND spd.value LIKE %s", $search_terms_like );
    1051         }
    1052 
    1053         if ( !empty( $meta_key ) ) {
    1054             $sql['where_meta'] = $wpdb->prepare( " AND umm.meta_key = %s", $meta_key );
    1055 
    1056             // If a meta value is provided, match it
    1057             if ( $meta_value ) {
    1058                 $sql['where_meta'] .= $wpdb->prepare( " AND umm.meta_value = %s", $meta_value );
    1059             }
    1060         }
    1061 
    1062         switch ( $type ) {
    1063             case 'active': case 'online': default:
    1064                 $sql[] = "ORDER BY um.meta_value DESC";
    1065                 break;
    1066             case 'newest':
    1067                 $sql[] = "ORDER BY u.ID DESC";
    1068                 break;
    1069             case 'alphabetical':
    1070                 $sql[] = "ORDER BY pd.value ASC";
    1071                 break;
    1072             case 'random':
    1073                 $sql[] = "ORDER BY rand()";
    1074                 break;
    1075             case 'popular':
    1076                 $sql[] = "ORDER BY CONVERT(um.meta_value, SIGNED) DESC";
    1077                 break;
    1078         }
    1079 
    1080         if ( !empty( $limit ) && !empty( $page ) ) {
    1081             $sql['pagination'] = $wpdb->prepare( "LIMIT %d, %d", intval( ( $page - 1 ) * $limit), intval( $limit ) );
    1082         }
    1083 
    1084         // Get paginated results
    1085         $paged_users_sql = apply_filters( 'bp_core_get_paged_users_sql', join( ' ', (array) $sql ), $sql );
    1086         $paged_users     = $wpdb->get_results( $paged_users_sql );
    1087 
    1088         // Re-jig the SQL so we can get the total user count
    1089         unset( $sql['select_main'] );
    1090 
    1091         if ( !empty( $sql['select_active'] ) ) {
    1092             unset( $sql['select_active'] );
    1093         }
    1094 
    1095         if ( !empty( $sql['select_popular'] ) ) {
    1096             unset( $sql['select_popular'] );
    1097         }
    1098 
    1099         if ( !empty( $sql['select_alpha'] ) ) {
    1100             unset( $sql['select_alpha'] );
    1101         }
    1102 
    1103         if ( !empty( $sql['pagination'] ) ) {
    1104             unset( $sql['pagination'] );
    1105         }
    1106 
    1107         array_unshift( $sql, "SELECT COUNT(u.ID)" );
    1108 
    1109         // Get total user results
    1110         $total_users_sql = apply_filters( 'bp_core_get_total_users_sql', join( ' ', (array) $sql ), $sql );
    1111         $total_users     = $wpdb->get_var( $total_users_sql );
    1112 
    1113         /***
    1114          * Lets fetch some other useful data in a separate queries, this will be faster than querying the data for every user in a list.
    1115          * We can't add these to the main query above since only users who have this information will be returned (since the much of the data is in usermeta and won't support any type of directional join)
    1116          */
    1117         if ( !empty( $populate_extras ) ) {
    1118             $user_ids = array();
    1119 
    1120             foreach ( (array) $paged_users as $user ) {
    1121                 $user_ids[] = $user->id;
    1122             }
    1123 
    1124             // Add additional data to the returned results
    1125             $paged_users = BP_Core_User::get_user_extras( $paged_users, $user_ids, $type );
    1126         }
    1127 
    1128         return array( 'users' => $paged_users, 'total' => $total_users );
    1129     }
    1130 
    1131 
    1132     /**
    1133      * Fetch the details for all users whose usernames start with the given letter.
    1134      *
    1135      * @global wpdb $wpdb WordPress database object.
    1136      *
    1137      * @param string $letter The letter the users names are to start with.
    1138      * @param int $limit The number of users we wish to retrive.
    1139      * @param int $page The page number we are currently on, used in
    1140      *        conjunction with $limit to get the start position for the
    1141      *        limit.
    1142      * @param bool $populate_extras Populate extra user fields?
    1143      * @param string $exclude Comma-separated IDs of users whose results
    1144      *        aren't to be fetched.
    1145      * @return mixed False on error, otherwise associative array of results.
    1146      */
    1147     public static function get_users_by_letter( $letter, $limit = null, $page = 1, $populate_extras = true, $exclude = '' ) {
    1148         global $wpdb;
    1149 
    1150         $pag_sql = '';
    1151         if ( $limit && $page ) {
    1152             $pag_sql = $wpdb->prepare( " LIMIT %d, %d", intval( ( $page - 1 ) * $limit), intval( $limit ) );
    1153         }
    1154 
    1155         // Multibyte compliance
    1156         if ( function_exists( 'mb_strlen' ) ) {
    1157             if ( mb_strlen( $letter, 'UTF-8' ) > 1 || is_numeric( $letter ) || !$letter ) {
    1158                 return false;
    1159             }
    1160         } else {
    1161             if ( strlen( $letter ) > 1 || is_numeric( $letter ) || !$letter ) {
    1162                 return false;
    1163             }
    1164         }
    1165 
    1166         $bp = buddypress();
    1167 
    1168         $letter_like = bp_esc_like( $letter ) . '%';
    1169         $status_sql  = bp_core_get_status_sql( 'u.' );
    1170 
    1171         if ( !empty( $exclude ) ) {
    1172             $exclude     = implode( ',', wp_parse_id_list( $exclude ) );
    1173             $exclude_sql = " AND u.id NOT IN ({$exclude})";
    1174         } else {
    1175             $exclude_sql = '';
    1176         }
    1177 
    1178         $total_users_sql = apply_filters( 'bp_core_users_by_letter_count_sql', $wpdb->prepare( "SELECT COUNT(DISTINCT u.ID) FROM {$wpdb->users} u LEFT JOIN {$bp->profile->table_name_data} pd ON u.ID = pd.user_id LEFT JOIN {$bp->profile->table_name_fields} pf ON pd.field_id = pf.id WHERE {$status_sql} AND pf.name = %s {$exclude_sql} AND pd.value LIKE %s ORDER BY pd.value ASC", bp_xprofile_fullname_field_name(), $letter_like ) );
    1179         $paged_users_sql = apply_filters( 'bp_core_users_by_letter_sql',       $wpdb->prepare( "SELECT DISTINCT u.ID as id, u.user_registered, u.user_nicename, u.user_login, u.user_email FROM {$wpdb->users} u LEFT JOIN {$bp->profile->table_name_data} pd ON u.ID = pd.user_id LEFT JOIN {$bp->profile->table_name_fields} pf ON pd.field_id = pf.id WHERE {$status_sql} AND pf.name = %s {$exclude_sql} AND pd.value LIKE %s ORDER BY pd.value ASC{$pag_sql}", bp_xprofile_fullname_field_name(), $letter_like ) );
    1180 
    1181         $total_users = $wpdb->get_var( $total_users_sql );
    1182         $paged_users = $wpdb->get_results( $paged_users_sql );
    1183 
    1184         /***
    1185          * Lets fetch some other useful data in a separate queries, this will be
    1186          * faster than querying the data for every user in a list. We can't add
    1187          * these to the main query above since only users who have this
    1188          * information will be returned (since the much of the data is in
    1189          * usermeta and won't support any type of directional join)
    1190          */
    1191         $user_ids = array();
    1192         foreach ( (array) $paged_users as $user )
    1193             $user_ids[] = (int) $user->id;
    1194 
    1195         // Add additional data to the returned results
    1196         if ( $populate_extras ) {
    1197             $paged_users = BP_Core_User::get_user_extras( $paged_users, $user_ids );
    1198         }
    1199 
    1200         return array( 'users' => $paged_users, 'total' => $total_users );
    1201     }
    1202 
    1203     /**
    1204      * Get details of specific users from the database.
    1205      *
    1206      * Use {@link BP_User_Query} with the 'user_ids' param instead.
    1207      *
    1208      * @global wpdb $wpdb WordPress database object.
    1209      * @param array $user_ids The user IDs of the users who we wish to
    1210      *        fetch information on.
    1211      * @param int $limit The limit of results we want.
    1212      * @param int $page The page we are on for pagination.
    1213      * @param bool $populate_extras Populate extra user fields?
    1214      * @return array Associative array.
    1215      */
    1216     public static function get_specific_users( $user_ids, $limit = null, $page = 1, $populate_extras = true ) {
    1217         global $wpdb;
    1218 
    1219         $pag_sql = '';
    1220         if ( $limit && $page )
    1221             $pag_sql = $wpdb->prepare( " LIMIT %d, %d", intval( ( $page - 1 ) * $limit), intval( $limit ) );
    1222 
    1223         $user_ids   = implode( ',', wp_parse_id_list( $user_ids ) );
    1224         $status_sql = bp_core_get_status_sql();
    1225 
    1226         $total_users_sql = apply_filters( 'bp_core_get_specific_users_count_sql', "SELECT COUNT(ID) FROM {$wpdb->users} WHERE {$status_sql} AND ID IN ({$user_ids})" );
    1227         $paged_users_sql = apply_filters( 'bp_core_get_specific_users_count_sql', "SELECT ID as id, user_registered, user_nicename, user_login, user_email FROM {$wpdb->users} WHERE {$status_sql} AND ID IN ({$user_ids}) {$pag_sql}" );
    1228 
    1229         $total_users = $wpdb->get_var( $total_users_sql );
    1230         $paged_users = $wpdb->get_results( $paged_users_sql );
    1231 
    1232         /***
    1233          * Lets fetch some other useful data in a separate queries, this will be
    1234          * faster than querying the data for every user in a list. We can't add
    1235          * these to the main query above since only users who have this
    1236          * information will be returned (since the much of the data is in
    1237          * usermeta and won't support any type of directional join)
    1238          */
    1239 
    1240         // Add additional data to the returned results
    1241         if ( !empty( $populate_extras ) ) {
    1242             $paged_users = BP_Core_User::get_user_extras( $paged_users, $user_ids );
    1243         }
    1244 
    1245         return array( 'users' => $paged_users, 'total' => $total_users );
    1246     }
    1247 
    1248     /**
    1249      * Find users who match on the value of an xprofile data.
    1250      *
    1251      * @global wpdb $wpdb WordPress database object.
    1252      *
    1253      * @param string $search_terms The terms to search the profile table
    1254      *        value column for.
    1255      * @param integer $limit The limit of results we want.
    1256      * @param integer $page The page we are on for pagination.
    1257      * @param boolean $populate_extras Populate extra user fields?
    1258      * @return array Associative array.
    1259      */
    1260     public static function search_users( $search_terms, $limit = null, $page = 1, $populate_extras = true ) {
    1261         global $wpdb;
    1262 
    1263         $bp = buddypress();
    1264 
    1265         $user_ids = array();
    1266         $pag_sql  = $limit && $page ? $wpdb->prepare( " LIMIT %d, %d", intval( ( $page - 1 ) * intval( $limit ) ), intval( $limit ) ) : '';
    1267 
    1268         $search_terms_like = '%' . bp_esc_like( $search_terms ) . '%';
    1269         $status_sql        = bp_core_get_status_sql( 'u.' );
    1270 
    1271         $total_users_sql = apply_filters( 'bp_core_search_users_count_sql', $wpdb->prepare( "SELECT COUNT(DISTINCT u.ID) as id FROM {$wpdb->users} u LEFT JOIN {$bp->profile->table_name_data} pd ON u.ID = pd.user_id WHERE {$status_sql} AND pd.value LIKE %s ORDER BY pd.value ASC", $search_terms_like ), $search_terms );
    1272         $paged_users_sql = apply_filters( 'bp_core_search_users_sql',       $wpdb->prepare( "SELECT DISTINCT u.ID as id, u.user_registered, u.user_nicename, u.user_login, u.user_email FROM {$wpdb->users} u LEFT JOIN {$bp->profile->table_name_data} pd ON u.ID = pd.user_id WHERE {$status_sql} AND pd.value LIKE %s ORDER BY pd.value ASC{$pag_sql}", $search_terms_like ), $search_terms, $pag_sql );
    1273 
    1274         $total_users = $wpdb->get_var( $total_users_sql );
    1275         $paged_users = $wpdb->get_results( $paged_users_sql );
    1276 
    1277         /***
    1278          * Lets fetch some other useful data in a separate queries, this will be faster than querying the data for every user in a list.
    1279          * We can't add these to the main query above since only users who have this information will be returned (since the much of the data is in usermeta and won't support any type of directional join)
    1280          */
    1281         foreach ( (array) $paged_users as $user )
    1282             $user_ids[] = $user->id;
    1283 
    1284         // Add additional data to the returned results
    1285         if ( $populate_extras )
    1286             $paged_users = BP_Core_User::get_user_extras( $paged_users, $user_ids );
    1287 
    1288         return array( 'users' => $paged_users, 'total' => $total_users );
    1289     }
    1290 
    1291     /**
    1292      * Fetch extra user information, such as friend count and last profile update message.
    1293      *
    1294      * Accepts multiple user IDs to fetch data for.
    1295      *
    1296      * @global wpdb $wpdb WordPress database object.
    1297      *
    1298      * @param array $paged_users An array of stdClass containing the users.
    1299      * @param string $user_ids The user ids to select information about.
    1300      * @param string $type The type of fields we wish to get.
    1301      * @return mixed False on error, otherwise associative array of results.
    1302      */
    1303     public static function get_user_extras( &$paged_users, &$user_ids, $type = false ) {
    1304         global $wpdb;
    1305 
    1306         $bp = buddypress();
    1307 
    1308         if ( empty( $user_ids ) )
    1309             return $paged_users;
    1310 
    1311         // Sanitize user IDs
    1312         $user_ids = implode( ',', wp_parse_id_list( $user_ids ) );
    1313 
    1314         // Fetch the user's full name
    1315         if ( bp_is_active( 'xprofile' ) && 'alphabetical' != $type ) {
    1316             $names = $wpdb->get_results( $wpdb->prepare( "SELECT pd.user_id as id, pd.value as fullname FROM {$bp->profile->table_name_fields} pf, {$bp->profile->table_name_data} pd WHERE pf.id = pd.field_id AND pf.name = %s AND pd.user_id IN ( {$user_ids} )", bp_xprofile_fullname_field_name() ) );
    1317             for ( $i = 0, $count = count( $paged_users ); $i < $count; ++$i ) {
    1318                 foreach ( (array) $names as $name ) {
    1319                     if ( $name->id == $paged_users[$i]->id )
    1320                         $paged_users[$i]->fullname = $name->fullname;
    1321                 }
    1322             }
    1323         }
    1324 
    1325         // Fetch the user's total friend count
    1326         if ( 'popular' != $type ) {
    1327             $friend_count = $wpdb->get_results( $wpdb->prepare( "SELECT user_id as id, meta_value as total_friend_count FROM {$wpdb->usermeta} WHERE meta_key = %s AND user_id IN ( {$user_ids} )", bp_get_user_meta_key( 'total_friend_count' ) ) );
    1328             for ( $i = 0, $count = count( $paged_users ); $i < $count; ++$i ) {
    1329                 foreach ( (array) $friend_count as $fcount ) {
    1330                     if ( $fcount->id == $paged_users[$i]->id )
    1331                         $paged_users[$i]->total_friend_count = (int) $fcount->total_friend_count;
    1332                 }
    1333             }
    1334         }
    1335 
    1336         // Fetch whether or not the user is a friend
    1337         if ( bp_is_active( 'friends' ) ) {
    1338             $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() ) );
    1339             for ( $i = 0, $count = count( $paged_users ); $i < $count; ++$i ) {
    1340                 foreach ( (array) $friend_status as $status ) {
    1341                     if ( $status->initiator_user_id == $paged_users[$i]->id || $status->friend_user_id == $paged_users[$i]->id )
    1342                         $paged_users[$i]->is_friend = $status->is_confirmed;
    1343                 }
    1344             }
    1345         }
    1346 
    1347         if ( 'active' != $type ) {
    1348             $user_activity = $wpdb->get_results( $wpdb->prepare( "SELECT user_id as id, meta_value as last_activity FROM {$wpdb->usermeta} WHERE meta_key = %s AND user_id IN ( {$user_ids} )", bp_get_user_meta_key( 'last_activity' ) ) );
    1349             for ( $i = 0, $count = count( $paged_users ); $i < $count; ++$i ) {
    1350                 foreach ( (array) $user_activity as $activity ) {
    1351                     if ( $activity->id == $paged_users[$i]->id )
    1352                         $paged_users[$i]->last_activity = $activity->last_activity;
    1353                 }
    1354             }
    1355         }
    1356 
    1357         // Fetch the user's last_activity
    1358         if ( 'active' != $type ) {
    1359             $user_activity = $wpdb->get_results( $wpdb->prepare( "SELECT user_id as id, meta_value as last_activity FROM {$wpdb->usermeta} WHERE meta_key = %s AND user_id IN ( {$user_ids} )", bp_get_user_meta_key( 'last_activity' ) ) );
    1360             for ( $i = 0, $count = count( $paged_users ); $i < $count; ++$i ) {
    1361                 foreach ( (array) $user_activity as $activity ) {
    1362                     if ( $activity->id == $paged_users[$i]->id )
    1363                         $paged_users[$i]->last_activity = $activity->last_activity;
    1364                 }
    1365             }
    1366         }
    1367 
    1368         // Fetch the user's latest update
    1369         $user_update = $wpdb->get_results( $wpdb->prepare( "SELECT user_id as id, meta_value as latest_update FROM {$wpdb->usermeta} WHERE meta_key = %s AND user_id IN ( {$user_ids} )", bp_get_user_meta_key( 'bp_latest_update' ) ) );
    1370         for ( $i = 0, $count = count( $paged_users ); $i < $count; ++$i ) {
    1371             foreach ( (array) $user_update as $update ) {
    1372                 if ( $update->id == $paged_users[$i]->id )
    1373                     $paged_users[$i]->latest_update = $update->latest_update;
    1374             }
    1375         }
    1376 
    1377         return $paged_users;
    1378     }
    1379 
    1380     /**
    1381      * Get WordPress user details for a specified user.
    1382      *
    1383      * @global wpdb $wpdb WordPress database object.
    1384      *
    1385      * @param integer $user_id User ID.
    1386      * @return array Associative array.
    1387      */
    1388     public static function get_core_userdata( $user_id ) {
    1389         global $wpdb;
    1390 
    1391         if ( !$user = $wpdb->get_row( $wpdb->prepare( "SELECT * FROM {$wpdb->users} WHERE ID = %d LIMIT 1", $user_id ) ) )
    1392             return false;
    1393 
    1394         return $user;
    1395     }
    1396 
    1397     /**
    1398      * Get last activity data for a user or set of users.
    1399      *
    1400      * @param int|array User IDs or multiple user IDs.
    1401      * @return array
    1402      */
    1403     public static function get_last_activity( $user_id ) {
    1404         global $wpdb;
    1405 
    1406         // Sanitize and remove empty values
    1407         $user_ids = array_filter( wp_parse_id_list( $user_id ) );
    1408 
    1409         if ( empty( $user_ids ) ) {
    1410             return false;
    1411         }
    1412 
    1413         $uncached_user_ids = bp_get_non_cached_ids( $user_ids, 'bp_last_activity' );
    1414         if ( ! empty( $uncached_user_ids ) ) {
    1415             $bp = buddypress();
    1416 
    1417             $user_ids_sql = implode( ',', $uncached_user_ids );
    1418             $user_count   = count( $uncached_user_ids );
    1419 
    1420             $last_activities = $wpdb->get_results( $wpdb->prepare( "SELECT id, user_id, date_recorded FROM {$bp->members->table_name_last_activity} WHERE component = %s AND type = 'last_activity' AND user_id IN ({$user_ids_sql}) LIMIT {$user_count}", $bp->members->id ) );
    1421 
    1422             foreach ( $last_activities as $last_activity ) {
    1423                 wp_cache_set( $last_activity->user_id, array(
    1424                     'user_id'       => $last_activity->user_id,
    1425                     'date_recorded' => $last_activity->date_recorded,
    1426                     'activity_id'   => $last_activity->id,
    1427                 ), 'bp_last_activity' );
    1428             }
    1429         }
    1430 
    1431         // Fetch all user data from the cache
    1432         $retval = array();
    1433         foreach ( $user_ids as $user_id ) {
    1434             $retval[ $user_id ] = wp_cache_get( $user_id, 'bp_last_activity' );
    1435         }
    1436 
    1437         return $retval;
    1438     }
    1439 
    1440     /**
    1441      * Set a user's last_activity value.
    1442      *
    1443      * Will create a new entry if it does not exist. Otherwise updates the
    1444      * existing entry.
    1445      *
    1446      * @since 2.0
    1447      *
    1448      * @param int $user_id ID of the user whose last_activity you are updating.
    1449      * @param string $time MySQL-formatted time string.
    1450      * @return bool True on success, false on failure.
    1451      */
    1452     public static function update_last_activity( $user_id, $time ) {
    1453         global $wpdb;
    1454 
    1455         $table_name = buddypress()->members->table_name_last_activity;
    1456 
    1457         $activity = self::get_last_activity( $user_id );
    1458 
    1459         if ( ! empty( $activity[ $user_id ] ) ) {
    1460             $updated = $wpdb->update(
    1461                 $table_name,
    1462 
    1463                 // Data to update
    1464                 array(
    1465                     'date_recorded' => $time,
    1466                 ),
    1467 
    1468                 // WHERE
    1469                 array(
    1470                     'id' => $activity[ $user_id ]['activity_id'],
    1471                 ),
    1472 
    1473                 // Data sanitization format
    1474                 array(
    1475                     '%s',
    1476                 ),
    1477 
    1478                 // WHERE sanitization format
    1479                 array(
    1480                     '%d',
    1481                 )
    1482             );
    1483 
    1484             // add new date to existing activity entry for caching
    1485             $activity[ $user_id ]['date_recorded'] = $time;
    1486 
    1487         } else {
    1488             $updated = $wpdb->insert(
    1489                 $table_name,
    1490 
    1491                 // Data
    1492                 array(
    1493                     'user_id'       => $user_id,
    1494                     'component'     => buddypress()->members->id,
    1495                     'type'          => 'last_activity',
    1496                     'action'        => '',
    1497                     'content'       => '',
    1498                     'primary_link'  => '',
    1499                     'item_id'       => 0,
    1500                     'date_recorded' => $time,
    1501                 ),
    1502 
    1503                 // Data sanitization format
    1504                 array(
    1505                     '%d',
    1506                     '%s',
    1507                     '%s',
    1508                     '%s',
    1509                     '%s',
    1510                     '%s',
    1511                     '%d',
    1512                     '%s',
    1513                 )
    1514             );
    1515 
    1516             // set up activity array for caching
    1517             // view the foreach loop in the get_last_activity() method for format
    1518             $activity = array();
    1519             $activity[ $user_id ] = array(
    1520                 'user_id'       => $user_id,
    1521                 'date_recorded' => $time,
    1522                 'activity_id'   => $wpdb->insert_id,
    1523             );
    1524         }
    1525 
    1526         // set cache
    1527         wp_cache_set( $user_id, $activity[ $user_id ], 'bp_last_activity' );
    1528 
    1529         return $updated;
    1530     }
    1531 
    1532     /**
    1533      * Delete a user's last_activity value.
    1534      *
    1535      * @since 2.0
    1536      *
    1537      * @param int $user_id
    1538      * @return bool True on success, false on failure or if no last_activity
    1539      *         is found for the user.
    1540      */
    1541     public static function delete_last_activity( $user_id ) {
    1542         global $wpdb;
    1543 
    1544         $existing = self::get_last_activity( $user_id );
    1545 
    1546         if ( empty( $existing ) ) {
    1547             return false;
    1548         }
    1549 
    1550         $deleted = $wpdb->delete(
    1551             buddypress()->members->table_name_last_activity,
    1552 
    1553             // WHERE
    1554             array(
    1555                 'id' => $existing[ $user_id ]['activity_id'],
    1556             ),
    1557 
    1558             // WHERE sanitization format
    1559             array(
    1560                 '%s',
    1561             )
    1562         );
    1563 
    1564         wp_cache_delete( $user_id, 'bp_last_activity' );
    1565 
    1566         return $deleted;
    1567     }
    1568 }
    1569 
    1570 if ( class_exists( 'WP_Date_Query' ) ) :
    1571 /**
    1572  * BuddyPress date query class.
    1573  *
    1574  * Extends the {@link WP_Date_Query} class for use with BuddyPress.
    1575  *
    1576  * @since BuddyPress (2.1.0)
    1577  *
    1578  * @param array $date_query {
    1579  *     Date query arguments.  See first parameter of {@link WP_Date_Query::__construct()}.
    1580  * }
    1581  * @param string $column The DB column to query against.
    1582  */
    1583 class BP_Date_Query extends WP_Date_Query {
    1584     /**
    1585      * The column to query against. Can be changed via the query arguments.
    1586      *
    1587      * @var string
    1588      */
    1589     public $column;
    1590 
    1591     /**
    1592      * Constructor.
    1593      *
    1594      * @see WP_Date_Query::__construct()
    1595      */
    1596     public function __construct( $date_query, $column = '' ) {
    1597         if ( ! empty( $column ) ) {
    1598             $this->column = $column;
    1599             add_filter( 'date_query_valid_columns', array( $this, 'register_date_column' ) );
    1600         }
    1601 
    1602         parent::__construct( $date_query, $column );
    1603     }
    1604 
    1605     /**
    1606      * Destructor.
    1607      */
    1608     public function __destruct() {
    1609         remove_filter( 'date_query_valid_columns', array( $this, 'register_date_column' ) );
    1610     }
    1611 
    1612     /**
    1613      * Registers our date column with WP Date Query to pass validation.
    1614      *
    1615      * @param array $retval Current DB columns
    1616      * @return array
    1617      */
    1618     public function register_date_column( $retval = array() ) {
    1619         $retval[] = $this->column;
    1620         return $retval;
    1621     }
    1622 }
    1623 endif;
    1624 
    1625 /**
    1626  * BP_Core_Notification is deprecated.
    1627  *
    1628  * Use BP_Notifications_Notification instead.
    1629  *
    1630  * @package BuddyPress Core
    1631  * @deprecated since BuddyPress (1.9)
    1632  */
    1633 class BP_Core_Notification {
    1634 
    1635     /**
    1636      * The notification id
    1637      *
    1638      * @var integer
    1639      */
    1640     public $id;
    1641 
    1642     /**
    1643      * The ID to which the notification relates to within the component.
    1644      *
    1645      * @var integer
    1646      */
    1647     public $item_id;
    1648 
    1649     /**
    1650      * The secondary ID to which the notification relates to within the component.
    1651      *
    1652      * @var integer
    1653      */
    1654     public $secondary_item_id = null;
    1655 
    1656     /**
    1657      * The user ID for who the notification is for.
    1658      *
    1659      * @var integer
    1660      */
    1661     public $user_id;
    1662 
    1663     /**
    1664      * The name of the component that the notification is for.
    1665      *
    1666      * @var string
    1667      */
    1668     public $component_name;
    1669 
    1670     /**
    1671      * The action within the component which the notification is related to.
    1672      *
    1673      * @var string
    1674      */
    1675     public $component_action;
    1676 
    1677     /**
    1678      * The date the notification was created.
    1679      *
    1680      * @var string
    1681      */
    1682     public $date_notified;
    1683 
    1684     /**
    1685      * Is the notification new or has it already been read.
    1686      *
    1687      * @var boolean
    1688      */
    1689     public $is_new;
    1690 
    1691     /** Public Methods ********************************************************/
    1692 
    1693     /**
    1694      * Constructor
    1695      *
    1696      * @param integer $id
    1697      */
    1698     public function __construct( $id = 0 ) {
    1699         if ( !empty( $id ) ) {
    1700             $this->id = $id;
    1701             $this->populate();
    1702         }
    1703     }
    1704 
    1705     /**
    1706      * Update or insert notification details into the database.
    1707      *
    1708      * @global wpdb $wpdb WordPress database object
    1709      * @return bool Success or failure
    1710      */
    1711     public function save() {
    1712         global $wpdb;
    1713 
    1714         $bp = buddypress();
    1715 
    1716         // Update
    1717         if ( !empty( $this->id ) ) {
    1718             $sql = $wpdb->prepare( "UPDATE {$bp->core->table_name_notifications} SET item_id = %d, secondary_item_id = %d, user_id = %d, component_name = %s, component_action = %d, date_notified = %s, is_new = %d ) WHERE id = %d", $this->item_id, $this->secondary_item_id, $this->user_id, $this->component_name, $this->component_action, $this->date_notified, $this->is_new, $this->id );
    1719 
    1720         // Save
    1721         } else {
    1722             $sql = $wpdb->prepare( "INSERT INTO {$bp->core->table_name_notifications} ( item_id, secondary_item_id, user_id, component_name, component_action, date_notified, is_new ) VALUES ( %d, %d, %d, %s, %s, %s, %d )", $this->item_id, $this->secondary_item_id, $this->user_id, $this->component_name, $this->component_action, $this->date_notified, $this->is_new );
    1723         }
    1724 
    1725         if ( !$result = $wpdb->query( $sql ) )
    1726             return false;
    1727 
    1728         $this->id = $wpdb->insert_id;
    1729 
    1730         return true;
    1731     }
    1732 
    1733     /** Private Methods *******************************************************/
    1734 
    1735     /**
    1736      * Fetches the notification data from the database.
    1737      *
    1738      * @global wpdb $wpdb WordPress database object
    1739      */
    1740     public function populate() {
    1741         global $wpdb;
    1742 
    1743         $bp = buddypress();
    1744 
    1745         if ( $notification = $wpdb->get_row( $wpdb->prepare( "SELECT * FROM {$bp->core->table_name_notifications} WHERE id = %d", $this->id ) ) ) {
    1746             $this->item_id = $notification->item_id;
    1747             $this->secondary_item_id = $notification->secondary_item_id;
    1748             $this->user_id           = $notification->user_id;
    1749             $this->component_name    = $notification->component_name;
    1750             $this->component_action  = $notification->component_action;
    1751             $this->date_notified     = $notification->date_notified;
    1752             $this->is_new            = $notification->is_new;
    1753         }
    1754     }
    1755 
    1756     /** Static Methods ********************************************************/
    1757 
    1758     public static function check_access( $user_id, $notification_id ) {
    1759         global $wpdb;
    1760 
    1761         $bp = buddypress();
    1762 
    1763         return $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(id) FROM {$bp->core->table_name_notifications} WHERE id = %d AND user_id = %d", $notification_id, $user_id ) );
    1764     }
    1765 
    1766     /**
    1767      * Fetches all the notifications in the database for a specific user.
    1768      *
    1769      * @global wpdb $wpdb WordPress database object
    1770      * @param integer $user_id User ID
    1771      * @param string $status 'is_new' or 'all'
    1772      * @return array Associative array
    1773      * @static
    1774      */
    1775     public static function get_all_for_user( $user_id, $status = 'is_new' ) {
    1776         global $wpdb;
    1777 
    1778         $bp = buddypress();
    1779 
    1780         $is_new = ( 'is_new' === $status )
    1781             ? ' AND is_new = 1 '
    1782             : '';
    1783 
    1784         return $wpdb->get_results( $wpdb->prepare( "SELECT * FROM {$bp->core->table_name_notifications} WHERE user_id = %d {$is_new}", $user_id ) );
    1785     }
    1786 
    1787     /**
    1788      * Delete all the notifications for a user based on the component name and action.
    1789      *
    1790      * @global wpdb $wpdb WordPress database object
    1791      * @param integer $user_id
    1792      * @param string $component_name
    1793      * @param string $component_action
    1794      * @static
    1795      */
    1796     public static function delete_for_user_by_type( $user_id, $component_name, $component_action ) {
    1797         global $wpdb;
    1798 
    1799         $bp = buddypress();
    1800 
    1801         return $wpdb->query( $wpdb->prepare( "DELETE FROM {$bp->core->table_name_notifications} WHERE user_id = %d AND component_name = %s AND component_action = %s", $user_id, $component_name, $component_action ) );
    1802     }
    1803 
    1804     /**
    1805      * Delete all the notifications that have a specific item id, component name and action.
    1806      *
    1807      * @global wpdb $wpdb WordPress database object
    1808      * @param integer $user_id The ID of the user who the notifications are for.
    1809      * @param integer $item_id The item ID of the notifications we wish to delete.
    1810      * @param string $component_name The name of the component that the notifications we wish to delete.
    1811      * @param string $component_action The action of the component that the notifications we wish to delete.
    1812      * @param integer $secondary_item_id (optional) The secondary item id of the notifications that we wish to use to delete.
    1813      * @static
    1814      */
    1815     public static function delete_for_user_by_item_id( $user_id, $item_id, $component_name, $component_action, $secondary_item_id = false ) {
    1816         global $wpdb;
    1817 
    1818         $bp = buddypress();
    1819 
    1820         $secondary_item_sql = !empty( $secondary_item_id )
    1821             ? $wpdb->prepare( " AND secondary_item_id = %d", $secondary_item_id )
    1822             : '';
    1823 
    1824         return $wpdb->query( $wpdb->prepare( "DELETE FROM {$bp->core->table_name_notifications} WHERE user_id = %d AND item_id = %d AND component_name = %s AND component_action = %s{$secondary_item_sql}", $user_id, $item_id, $component_name, $component_action ) );
    1825     }
    1826 
    1827     /**
    1828      * Deletes all the notifications sent by a specific user, by component and action.
    1829      *
    1830      * @global wpdb $wpdb WordPress database object
    1831      * @param integer $user_id The ID of the user whose sent notifications we wish to delete.
    1832      * @param string $component_name The name of the component the notification was sent from.
    1833      * @param string $component_action The action of the component the notification was sent from.
    1834      * @static
    1835      */
    1836     public static function delete_from_user_by_type( $user_id, $component_name, $component_action ) {
    1837         global $wpdb;
    1838 
    1839         $bp = buddypress();
    1840 
    1841         return $wpdb->query( $wpdb->prepare( "DELETE FROM {$bp->core->table_name_notifications} WHERE item_id = %d AND component_name = %s AND component_action = %s", $user_id, $component_name, $component_action ) );
    1842     }
    1843 
    1844     /**
    1845      * Deletes all the notifications for all users by item id, and optional secondary item id, and component name and action.
    1846      *
    1847      * @global wpdb $wpdb WordPress database object
    1848      * @param string $item_id The item id that they notifications are to be for.
    1849      * @param string $component_name The component that the notifications are to be from.
    1850      * @param string $component_action The action that the notifications are to be from.
    1851      * @param string $secondary_item_id Optional secondary item id that the notifications are to have.
    1852      * @static
    1853      */
    1854     public static function delete_all_by_type( $item_id, $component_name, $component_action, $secondary_item_id ) {
    1855         global $wpdb;
    1856 
    1857         if ( $component_action )
    1858             $component_action_sql = $wpdb->prepare( "AND component_action = %s", $component_action );
    1859         else
    1860             $component_action_sql = '';
    1861 
    1862         if ( $secondary_item_id )
    1863             $secondary_item_sql = $wpdb->prepare( "AND secondary_item_id = %d", $secondary_item_id );
    1864         else
    1865             $secondary_item_sql = '';
    1866 
    1867         $bp = buddypress();
    1868 
    1869         return $wpdb->query( $wpdb->prepare( "DELETE FROM {$bp->core->table_name_notifications} WHERE item_id = %d AND component_name = %s {$component_action_sql} {$secondary_item_sql}", $item_id, $component_name ) );
    1870     }
    1871 }
    1872 
    1873 /**
    1874  * API to create BuddyPress buttons.
    1875  *
    1876  * @since BuddyPress (1.2.6)
    1877  *
    1878  * @param array $args {
    1879  *     Array of arguments.
    1880  *     @type string $id String describing the button type.
    1881  *     @type string $component The name of the component the button belongs to.
    1882  *           Default: 'core'.
    1883  *     @type bool $must_be_logged_in Optional. Does the user need to be logged
    1884  *           in to see this button? Default: true.
    1885  *     @type bool $block_self Optional. True if the button should be hidden
    1886  *           when a user is viewing his own profile. Default: true.
    1887  *     @type string|bool $wrapper Optional. HTML element type that should wrap
    1888  *           the button: 'div', 'span', 'p', or 'li'. False for no wrapper at
    1889  *           all. Default: 'div'.
    1890  *     @type string $wrapper_id Optional. DOM ID of the button wrapper element.
    1891  *           Default: ''.
    1892  *     @type string $wrapper_class Optional. DOM class of the button wrapper
    1893  *           element. Default: ''.
    1894  *     @type string $link_href Optional. Destination link of the button.
    1895  *           Default: ''.
    1896  *     @type string $link_class Optional. DOM class of the button. Default: ''.
    1897  *     @type string $link_id Optional. DOM ID of the button. Default: ''.
    1898  *     @type string $link_rel Optional. DOM 'rel' attribute of the button.
    1899  *           Default: ''.
    1900  *     @type string $link_title Optional. Title attribute of the button.
    1901  *           Default: ''.
    1902  *     @type string $link_text Optional. Text to appear on the button.
    1903  *           Default: ''.
    1904  * }
    1905  */
    1906 class BP_Button {
    1907 
    1908     /** Button properties *****************************************************/
    1909 
    1910     /**
    1911      * The button ID.
    1912      *
    1913      * @var string
    1914      */
    1915     public $id = '';
    1916 
    1917     /**
    1918      * The name of the component that the button belongs to.
    1919      *
    1920      * @var string
    1921      */
    1922     public $component = 'core';
    1923 
    1924     /**
    1925      * Does the user need to be logged in to see this button?
    1926      *
    1927      * @var bool
    1928      */
    1929     public $must_be_logged_in = true;
    1930 
    1931     /**
    1932      * Whether the button should be hidden when viewing your own profile.
    1933      *
    1934      * @var bool
    1935      */
    1936     public $block_self = true;
    1937 
    1938     /** Wrapper ***************************************************************/
    1939 
    1940     /**
    1941      * The type of DOM element to use for a wrapper.
    1942      *
    1943      * @var string|bool 'div', 'span', 'p', 'li', or false for no wrapper.
    1944      */
    1945     public $wrapper = 'div';
    1946 
    1947     /**
    1948      * The DOM class of the button wrapper.
    1949      *
    1950      * @var string
    1951      */
    1952     public $wrapper_class = '';
    1953 
    1954     /**
    1955      * The DOM ID of the button wrapper.
    1956      *
    1957      * @var string
    1958      */
    1959     public $wrapper_id = '';
    1960 
    1961     /** Button ****************************************************************/
    1962 
    1963     /**
    1964      * The destination link of the button.
    1965      *
    1966      * @var string
    1967      */
    1968     public $link_href = '';
    1969 
    1970     /**
    1971      * The DOM class of the button link.
    1972      *
    1973      * @var string
    1974      */
    1975     public $link_class = '';
    1976 
    1977     /**
    1978      * The DOM ID of the button link.
    1979      *
    1980      * @var string
    1981      */
    1982     public $link_id = '';
    1983 
    1984     /**
    1985      * The DOM rel value of the button link.
    1986      *
    1987      * @var string
    1988      */
    1989     public $link_rel = '';
    1990 
    1991     /**
    1992      * Title of the button link.
    1993      *
    1994      * @var string
    1995      */
    1996     public $link_title = '';
    1997 
    1998     /**
    1999      * The contents of the button link.
    2000      *
    2001      * @var string
    2002      */
    2003     public $link_text = '';
    2004 
    2005     /** HTML result ***********************************************************/
    2006 
    2007     public $contents = '';
    2008 
    2009     /** Methods ***************************************************************/
    2010 
    2011     /**
    2012      * Builds the button based on class parameters.
    2013      *
    2014      * @since BuddyPress (1.2.6)
    2015      *
    2016      * @param array $args See {@BP_Button}.
    2017      * @return bool|null Returns false when the button is not allowed for
    2018      *         the current context.
    2019      */
    2020     public function __construct( $args = '' ) {
    2021 
    2022         $r = wp_parse_args( $args, get_class_vars( __CLASS__ ) );
    2023 
    2024         // Required button properties
    2025         $this->id                = $r['id'];
    2026         $this->component         = $r['component'];
    2027         $this->must_be_logged_in = (bool) $r['must_be_logged_in'];
    2028         $this->block_self        = (bool) $r['block_self'];
    2029         $this->wrapper           = $r['wrapper'];
    2030 
    2031         // $id and $component are required
    2032         if ( empty( $r['id'] ) || empty( $r['component'] ) )
    2033             return false;
    2034 
    2035         // No button if component is not active
    2036         if ( ! bp_is_active( $this->component ) )
    2037             return false;
    2038 
    2039         // No button for guests if must be logged in
    2040         if ( true == $this->must_be_logged_in && ! is_user_logged_in() )
    2041             return false;
    2042 
    2043         // block_self
    2044         if ( true == $this->block_self ) {
    2045             // No button if you are the current user in a members loop
    2046             // This condition takes precedence, because members loops
    2047             // can be found on user profiles
    2048             if ( bp_get_member_user_id() ) {
    2049                 if ( is_user_logged_in() && bp_loggedin_user_id() == bp_get_member_user_id() ) {
    2050                     return false;
    2051                 }
    2052 
    2053             // No button if viewing your own profile (and not in
    2054             // a members loop)
    2055             } elseif ( bp_is_my_profile() ) {
    2056                 return false;
    2057             }
    2058         }
    2059 
    2060         // Wrapper properties
    2061         if ( false !== $this->wrapper ) {
    2062 
    2063             // Wrapper ID
    2064             if ( !empty( $r['wrapper_id'] ) ) {
    2065                 $this->wrapper_id    = ' id="' . $r['wrapper_id'] . '"';
    2066             }
    2067 
    2068             // Wrapper class
    2069             if ( !empty( $r['wrapper_class'] ) ) {
    2070                 $this->wrapper_class = ' class="generic-button ' . $r['wrapper_class'] . '"';
    2071             } else {
    2072                 $this->wrapper_class = ' class="generic-button"';
    2073             }
    2074 
    2075             // Set before and after
    2076             $before = '<' . $r['wrapper'] . $this->wrapper_class . $this->wrapper_id . '>';
    2077             $after  = '</' . $r['wrapper'] . '>';
    2078 
    2079         // No wrapper
    2080         } else {
    2081             $before = $after = '';
    2082         }
    2083 
    2084         // Link properties
    2085         if ( !empty( $r['link_id']    ) ) $this->link_id    = ' id="' .    $r['link_id']    . '"';
    2086         if ( !empty( $r['link_href']  ) ) $this->link_href  = ' href="' .  $r['link_href']  . '"';
    2087         if ( !empty( $r['link_title'] ) ) $this->link_title = ' title="' . $r['link_title'] . '"';
    2088         if ( !empty( $r['link_rel']   ) ) $this->link_rel   = ' rel="' .   $r['link_rel']   . '"';
    2089         if ( !empty( $r['link_class'] ) ) $this->link_class = ' class="' . $r['link_class'] . '"';
    2090         if ( !empty( $r['link_text']  ) ) $this->link_text  =              $r['link_text'];
    2091 
    2092         // Build the button
    2093         $this->contents = $before . '<a'. $this->link_href . $this->link_title . $this->link_id . $this->link_rel . $this->link_class . '>' . $this->link_text . '</a>' . $after;
    2094 
    2095         // Allow button to be manipulated externally
    2096         $this->contents = apply_filters( 'bp_button_' . $this->component . '_' . $this->id, $this->contents, $this, $before, $after );
    2097     }
    2098 
    2099     /**
    2100      * Return the markup for the generated button.
    2101      *
    2102      * @since BuddyPress (1.2.6)
    2103      *
    2104      * @return string Button markup.
    2105      */
    2106     public function contents() {
    2107         return $this->contents;
    2108     }
    2109 
    2110     /**
    2111      * Output the markup of button.
    2112      *
    2113      * @since BuddyPress (1.2.6)
    2114      */
    2115     public function display() {
    2116         if ( !empty( $this->contents ) )
    2117             echo $this->contents;
    2118     }
    2119 }
    2120 
    2121 /**
    2122  * Enable oEmbeds in BuddyPress contexts.
    2123  *
    2124  * Extends WP_Embed class for use with BuddyPress.
    2125  *
    2126  * @since BuddyPress (1.5.0)
    2127  *
    2128  * @see WP_Embed
    2129  */
    2130 class BP_Embed extends WP_Embed {
    2131 
    2132     /**
    2133      * Constructor
    2134      *
    2135      * @global WP_Embed $wp_embed
    2136      */
    2137     public function __construct() {
    2138         global $wp_embed;
    2139 
    2140         // Make sure we populate the WP_Embed handlers array.
    2141         // These are providers that use a regex callback on the URL in question.
    2142         // Do not confuse with oEmbed providers, which require an external ping.
    2143         // Used in WP_Embed::shortcode()
    2144         $this->handlers = $wp_embed->handlers;
    2145 
    2146         if ( bp_use_embed_in_activity() ) {
    2147             add_filter( 'bp_get_activity_content_body', array( &$this, 'autoembed' ), 8 );
    2148             add_filter( 'bp_get_activity_content_body', array( &$this, 'run_shortcode' ), 7 );
    2149         }
    2150 
    2151         if ( bp_use_embed_in_activity_replies() ) {
    2152             add_filter( 'bp_get_activity_content', array( &$this, 'autoembed' ), 8 );
    2153             add_filter( 'bp_get_activity_content', array( &$this, 'run_shortcode' ), 7 );
    2154         }
    2155 
    2156         if ( bp_use_embed_in_forum_posts() ) {
    2157             add_filter( 'bp_get_the_topic_post_content', array( &$this, 'autoembed' ), 8 );
    2158             add_filter( 'bp_get_the_topic_post_content', array( &$this, 'run_shortcode' ), 7 );
    2159         }
    2160 
    2161         if ( bp_use_embed_in_private_messages() ) {
    2162             add_filter( 'bp_get_the_thread_message_content', array( &$this, 'autoembed' ), 8 );
    2163             add_filter( 'bp_get_the_thread_message_content', array( &$this, 'run_shortcode' ), 7 );
    2164         }
    2165 
    2166         do_action_ref_array( 'bp_core_setup_oembed', array( &$this ) );
    2167     }
    2168 
    2169     /**
    2170      * The {@link do_shortcode()} callback function.
    2171      *
    2172      * Attempts to convert a URL into embed HTML. Starts by checking the
    2173      * URL against the regex of the registered embed handlers. Next, checks
    2174      * the URL against the regex of registered {@link WP_oEmbed} providers
    2175      * if oEmbed discovery is false. If none of the regex matches and it's
    2176      * enabled, then the URL will be passed to {@link BP_Embed::parse_oembed()}
    2177      * for oEmbed parsing.
    2178      *
    2179      * @uses wp_parse_args()
    2180      * @uses wp_embed_defaults()
    2181      * @uses current_user_can()
    2182      * @uses _wp_oembed_get_object()
    2183      * @uses WP_Embed::maybe_make_link()
    2184      *
    2185      * @param array $attr Shortcode attributes.
    2186      * @param string $url The URL attempting to be embeded.
    2187      * @return string The embed HTML on success, otherwise the original URL.
    2188      */
    2189     public function shortcode( $attr, $url = '' ) {
    2190         if ( empty( $url ) )
    2191             return '';
    2192 
    2193         $rawattr = $attr;
    2194         $attr = wp_parse_args( $attr, wp_embed_defaults() );
    2195 
    2196         // kses converts & into &amp; and we need to undo this
    2197         // See http://core.trac.wordpress.org/ticket/11311
    2198         $url = str_replace( '&amp;', '&', $url );
    2199 
    2200         // Look for known internal handlers
    2201         ksort( $this->handlers );
    2202         foreach ( $this->handlers as $priority => $handlers ) {
    2203             foreach ( $handlers as $hid => $handler ) {
    2204                 if ( preg_match( $handler['regex'], $url, $matches ) && is_callable( $handler['callback'] ) ) {
    2205                     if ( false !== $return = call_user_func( $handler['callback'], $matches, $attr, $url, $rawattr ) ) {
    2206                         return apply_filters( 'embed_handler_html', $return, $url, $attr );
    2207                     }
    2208                 }
    2209             }
    2210         }
    2211 
    2212         // Get object ID
    2213         $id = apply_filters( 'embed_post_id', 0 );
    2214 
    2215         // Is oEmbed discovery on?
    2216         $attr['discover'] = ( apply_filters( 'bp_embed_oembed_discover', false ) && current_user_can( 'unfiltered_html' ) );
    2217 
    2218         // Set up a new WP oEmbed object to check URL with registered oEmbed providers
    2219         require_once( ABSPATH . WPINC . '/class-oembed.php' );
    2220         $oembed_obj = _wp_oembed_get_object();
    2221 
    2222         // If oEmbed discovery is true, skip oEmbed provider check
    2223         $is_oembed_link = false;
    2224         if ( !$attr['discover'] ) {
    2225             foreach ( (array) $oembed_obj->providers as $provider_matchmask => $provider ) {
    2226                 $regex = ( $is_regex = $provider[1] ) ? $provider_matchmask : '#' . str_replace( '___wildcard___', '(.+)', preg_quote( str_replace( '*', '___wildcard___', $provider_matchmask ), '#' ) ) . '#i';
    2227 
    2228                 if ( preg_match( $regex, $url ) )
    2229                     $is_oembed_link = true;
    2230             }
    2231 
    2232             // If url doesn't match a WP oEmbed provider, stop parsing
    2233             if ( !$is_oembed_link )
    2234                 return $this->maybe_make_link( $url );
    2235         }
    2236 
    2237         return $this->parse_oembed( $id, $url, $attr, $rawattr );
    2238     }
    2239 
    2240     /**
    2241      * Base function so BP components/plugins can parse links to be embedded.
    2242      *
    2243      * View an example to add support in {@link bp_activity_embed()}.
    2244      *
    2245      * @uses apply_filters() Filters cache.
    2246      * @uses do_action() To save cache.
    2247      * @uses wp_oembed_get() Connects to oEmbed provider and returns HTML
    2248      *       on success.
    2249      * @uses WP_Embed::maybe_make_link() Process URL for hyperlinking on
    2250      *       oEmbed failure.
    2251      *
    2252      * @param int $id ID to do the caching for.
    2253      * @param string $url The URL attempting to be embedded.
    2254      * @param array $attr Shortcode attributes from {@link WP_Embed::shortcode()}.
    2255      * @param array $rawattr Untouched shortcode attributes from
    2256      *        {@link WP_Embed::shortcode()}.
    2257      * @return string The embed HTML on success, otherwise the original URL.
    2258      */
    2259     public function parse_oembed( $id, $url, $attr, $rawattr ) {
    2260         $id = intval( $id );
    2261 
    2262         if ( $id ) {
    2263             // Setup the cachekey
    2264             $cachekey = '_oembed_' . md5( $url . serialize( $attr ) );
    2265 
    2266             // Let components / plugins grab their cache
    2267             $cache = '';
    2268             $cache = apply_filters( 'bp_embed_get_cache', $cache, $id, $cachekey, $url, $attr, $rawattr );
    2269 
    2270             // Grab cache and return it if available
    2271             if ( !empty( $cache ) ) {
    2272                 return apply_filters( 'bp_embed_oembed_html', $cache, $url, $attr, $rawattr );
    2273 
    2274             // If no cache, ping the oEmbed provider and cache the result
    2275             } else {
    2276                 $html = wp_oembed_get( $url, $attr );
    2277                 $cache = ( $html ) ? $html : $url;
    2278 
    2279                 // Let components / plugins save their cache
    2280                 do_action( 'bp_embed_update_cache', $cache, $cachekey, $id );
    2281 
    2282                 // If there was a result, return it
    2283                 if ( $html )
    2284                     return apply_filters( 'bp_embed_oembed_html', $html, $url, $attr, $rawattr );
    2285             }
    2286         }
    2287 
    2288         // Still unknown
    2289         return $this->maybe_make_link( $url );
    2290     }
    2291 }
    2292 
    2293 /**
    2294  * Create HTML list of BP nav items.
    2295  *
    2296  * @since BuddyPress (1.7.0)
    2297  */
    2298 class BP_Walker_Nav_Menu extends Walker_Nav_Menu {
    2299 
    2300     /**
    2301      * Description of fields indexes for building markup.
    2302      *
    2303      * @since BuddyPress (1.7.0)
    2304      * @var array
    2305      */
    2306     var $db_fields = array( 'id' => 'css_id', 'parent' => 'parent' );
    2307 
    2308     /**
    2309      * Tree type.
    2310      *
    2311      * @since BuddyPress (1.7.0)
    2312      * @var string
    2313      */
    2314     var $tree_type = array();
    2315 
    2316     /**
    2317      * Display array of elements hierarchically.
    2318      *
    2319      * This method is almost identical to the version in {@link Walker::walk()}.
    2320      * The only change is on one line which has been commented. An IF was
    2321      * comparing 0 to a non-empty string which was preventing child elements
    2322      * being grouped under their parent menu element.
    2323      *
    2324      * This caused a problem for BuddyPress because our primary/secondary
    2325      * navigations don't have a unique numerical ID that describes a
    2326      * hierarchy (we use a slug). Obviously, WordPress Menus use Posts, and
    2327      * those have ID/post_parent.
    2328      *
    2329      * @since BuddyPress (1.7.0)
    2330      *
    2331      * @see Walker::walk()
    2332      *
    2333      * @param array $elements See {@link Walker::walk()}.
    2334      * @param int $max_depth See {@link Walker::walk()}.
    2335      * @return string See {@link Walker::walk()}.
    2336      */
    2337     public function walk( $elements, $max_depth ) {
    2338         $func_args = func_get_args();
    2339 
    2340         $args   = array_slice( $func_args, 2 );
    2341         $output = '';
    2342 
    2343         if ( $max_depth < -1 ) // invalid parameter
    2344             return $output;
    2345 
    2346         if ( empty( $elements ) ) // nothing to walk
    2347             return $output;
    2348 
    2349         $parent_field = $this->db_fields['parent'];
    2350 
    2351         // flat display
    2352         if ( -1 == $max_depth ) {
    2353 
    2354             $empty_array = array();
    2355             foreach ( $elements as $e )
    2356                 $this->display_element( $e, $empty_array, 1, 0, $args, $output );
    2357 
    2358             return $output;
    2359         }
    2360 
    2361         /*
    2362          * need to display in hierarchical order
    2363          * separate elements into two buckets: top level and children elements
    2364          * children_elements is two dimensional array, eg.
    2365          * children_elements[10][] contains all sub-elements whose parent is 10.
    2366          */
    2367         $top_level_elements = array();
    2368         $children_elements  = array();
    2369 
    2370         foreach ( $elements as $e ) {
    2371             // BuddyPress: changed '==' to '==='. This is the only change from version in Walker::walk().
    2372             if ( 0 === $e->$parent_field )
    2373                 $top_level_elements[] = $e;
    2374             else
    2375                 $children_elements[$e->$parent_field][] = $e;
    2376         }
    2377 
    2378         /*
    2379          * when none of the elements is top level
    2380          * assume the first one must be root of the sub elements
    2381          */
    2382         if ( empty( $top_level_elements ) ) {
    2383 
    2384             $first              = array_slice( $elements, 0, 1 );
    2385             $root               = $first[0];
    2386             $top_level_elements = array();
    2387             $children_elements  = array();
    2388 
    2389             foreach ( $elements as $e ) {
    2390                 if ( $root->$parent_field == $e->$parent_field )
    2391                     $top_level_elements[] = $e;
    2392                 else
    2393                     $children_elements[$e->$parent_field][] = $e;
    2394             }
    2395         }
    2396 
    2397         foreach ( $top_level_elements as $e )
    2398             $this->display_element( $e, $children_elements, $max_depth, 0, $args, $output );
    2399 
    2400         /*
    2401          * if we are displaying all levels, and remaining children_elements is not empty,
    2402          * then we got orphans, which should be displayed regardless
    2403          */
    2404         if ( ( $max_depth == 0 ) && count( $children_elements ) > 0 ) {
    2405             $empty_array = array();
    2406 
    2407             foreach ( $children_elements as $orphans )
    2408                 foreach ( $orphans as $op )
    2409                     $this->display_element( $op, $empty_array, 1, 0, $args, $output );
    2410          }
    2411 
    2412          return $output;
    2413     }
    2414 
    2415     /**
    2416      * Display the current <li> that we are on.
    2417      *
    2418      * @see Walker::start_el() for complete description of parameters .
    2419      *
    2420      * @since BuddyPress (1.7.0)
    2421      *
    2422      * @param string $output Passed by reference. Used to append
    2423      *        additional content.
    2424      * @param object $item Menu item data object.
    2425      * @param int $depth Depth of menu item. Used for padding. Optional,
    2426      *        defaults to 0.
    2427      * @param array $args Optional. See {@link Walker::start_el()}.
    2428      * @param int $current_page Menu item ID. Optional.
    2429      */
    2430     public function start_el( &$output, $item, $depth = 0, $args = array(), $id = 0 ) {
    2431         // If we're someway down the tree, indent the HTML with the appropriate number of tabs
    2432         $indent = $depth ? str_repeat( "\t", $depth ) : '';
    2433 
    2434         // Add HTML classes
    2435         $class_names = join( ' ', apply_filters( 'bp_nav_menu_css_class', array_filter( $item->class ), $item, $args ) );
    2436         $class_names = ! empty( $class_names ) ? ' class="' . esc_attr( $class_names ) . '"' : '';
    2437 
    2438         // Add HTML ID
    2439         $id = sanitize_html_class( $item->css_id . '-personal-li' );  // Backpat with BP pre-1.7
    2440         $id = apply_filters( 'bp_nav_menu_item_id', $id, $item, $args );
    2441         $id = ! empty( $id ) ? ' id="' . esc_attr( $id ) . '"' : '';
    2442 
    2443         // Opening tag; closing tag is handled in Walker_Nav_Menu::end_el().
    2444         $output .= $indent . '<li' . $id . $class_names . '>';
    2445 
    2446         // Add href attribute
    2447         $attributes = ! empty( $item->link ) ? ' href="' . esc_attr( esc_url( $item->link ) ) . '"' : '';
    2448 
    2449         // Construct the link
    2450         $item_output = $args->before;
    2451         $item_output .= '<a' . $attributes . '>';
    2452         $item_output .= $args->link_before . apply_filters( 'the_title', $item->name, 0 ) . $args->link_after;
    2453         $item_output .= '</a>';
    2454         $item_output .= $args->after;
    2455 
    2456         // $output is byref
    2457         $output .= apply_filters( 'bp_walker_nav_menu_start_el', $item_output, $item, $depth, $args );
    2458     }
    2459 }
    2460 
    2461 /**
    2462  * Create a set of BuddyPress-specific links for use in the Menus admin UI.
    2463  *
    2464  * Borrowed heavily from {@link Walker_Nav_Menu_Checklist}, but modified so as not
    2465  * to require an actual post type or taxonomy, and to force certain CSS classes
    2466  *
    2467  * @since BuddyPress (1.9.0)
    2468  */
    2469 class BP_Walker_Nav_Menu_Checklist extends Walker_Nav_Menu {
    2470 
    2471     /**
    2472      * Constructor.
    2473      *
    2474      * @see Walker_Nav_Menu::__construct() for a description of parameters.
    2475      *
    2476      * @param array $fields See {@link Walker_Nav_Menu::__construct()}.
    2477      */
    2478     public function __construct( $fields = false ) {
    2479         if ( $fields ) {
    2480             $this->db_fields = $fields;
    2481         }
    2482     }
    2483 
    2484     /**
    2485      * Create the markup to start a tree level.
    2486      *
    2487      * @see Walker_Nav_Menu::start_lvl() for description of parameters.
    2488      *
    2489      * @param string $output See {@Walker_Nav_Menu::start_lvl()}.
    2490      * @param int $depth See {@Walker_Nav_Menu::start_lvl()}.
    2491      * @param array $args See {@Walker_Nav_Menu::start_lvl()}.
    2492      */
    2493     public function start_lvl( &$output, $depth = 0, $args = array() ) {
    2494         $indent = str_repeat( "\t", $depth );
    2495         $output .= "\n$indent<ul class='children'>\n";
    2496     }
    2497 
    2498     /**
    2499      * Create the markup to end a tree level.
    2500      *
    2501      * @see Walker_Nav_Menu::end_lvl() for description of parameters.
    2502      *
    2503      * @param string $output See {@Walker_Nav_Menu::end_lvl()}.
    2504      * @param int $depth See {@Walker_Nav_Menu::end_lvl()}.
    2505      * @param array $args See {@Walker_Nav_Menu::end_lvl()}.
    2506      */
    2507     public function end_lvl( &$output, $depth = 0, $args = array() ) {
    2508         $indent = str_repeat( "\t", $depth );
    2509         $output .= "\n$indent</ul>";
    2510     }
    2511 
    2512     /**
    2513      * Create the markup to start an element.
    2514      *
    2515      * @see Walker::start_el() for description of parameters.
    2516      *
    2517      * @param string $output Passed by reference. Used to append additional
    2518      *        content.
    2519      * @param object $item Menu item data object.
    2520      * @param int $depth Depth of menu item. Used for padding.
    2521      * @param object $args See {@Walker::start_el()}.
    2522      * @param int $id See {@Walker::start_el()}.
    2523      */
    2524     function start_el( &$output, $item, $depth = 0, $args = array(), $id = 0 ) {
    2525         global $_nav_menu_placeholder;
    2526 
    2527         $_nav_menu_placeholder = ( 0 > $_nav_menu_placeholder ) ? intval($_nav_menu_placeholder) - 1 : -1;
    2528         $possible_object_id = isset( $item->post_type ) && 'nav_menu_item' == $item->post_type ? $item->object_id : $_nav_menu_placeholder;
    2529         $possible_db_id = ( ! empty( $item->ID ) ) && ( 0 < $possible_object_id ) ? (int) $item->ID : 0;
    2530 
    2531         $indent = ( $depth ) ? str_repeat( "\t", $depth ) : '';
    2532 
    2533         $output .= $indent . '<li>';
    2534         $output .= '<label class="menu-item-title">';
    2535         $output .= '<input type="checkbox" class="menu-item-checkbox';
    2536 
    2537         if ( property_exists( $item, 'label' ) ) {
    2538             $title = $item->label;
    2539         }
    2540 
    2541         $output .= '" name="menu-item[' . $possible_object_id . '][menu-item-object-id]" value="'. esc_attr( $item->object_id ) .'" /> ';
    2542         $output .= isset( $title ) ? esc_html( $title ) : esc_html( $item->title );
    2543         $output .= '</label>';
    2544 
    2545         if ( empty( $item->url ) ) {
    2546             $item->url = $item->guid;
    2547         }
    2548 
    2549         if ( ! in_array( array( 'bp-menu', 'bp-'. $item->post_excerpt .'-nav' ), $item->classes ) ) {
    2550             $item->classes[] = 'bp-menu';
    2551             $item->classes[] = 'bp-'. $item->post_excerpt .'-nav';
    2552         }
    2553 
    2554         // Menu item hidden fields
    2555         $output .= '<input type="hidden" class="menu-item-db-id" name="menu-item[' . $possible_object_id . '][menu-item-db-id]" value="' . $possible_db_id . '" />';
    2556         $output .= '<input type="hidden" class="menu-item-object" name="menu-item[' . $possible_object_id . '][menu-item-object]" value="'. esc_attr( $item->object ) .'" />';
    2557         $output .= '<input type="hidden" class="menu-item-parent-id" name="menu-item[' . $possible_object_id . '][menu-item-parent-id]" value="'. esc_attr( $item->menu_item_parent ) .'" />';
    2558         $output .= '<input type="hidden" class="menu-item-type" name="menu-item[' . $possible_object_id . '][menu-item-type]" value="custom" />';
    2559         $output .= '<input type="hidden" class="menu-item-title" name="menu-item[' . $possible_object_id . '][menu-item-title]" value="'. esc_attr( $item->title ) .'" />';
    2560         $output .= '<input type="hidden" class="menu-item-url" name="menu-item[' . $possible_object_id . '][menu-item-url]" value="'. esc_attr( $item->url ) .'" />';
    2561         $output .= '<input type="hidden" class="menu-item-target" name="menu-item[' . $possible_object_id . '][menu-item-target]" value="'. esc_attr( $item->target ) .'" />';
    2562         $output .= '<input type="hidden" class="menu-item-attr_title" name="menu-item[' . $possible_object_id . '][menu-item-attr_title]" value="'. esc_attr( $item->attr_title ) .'" />';
    2563         $output .= '<input type="hidden" class="menu-item-classes" name="menu-item[' . $possible_object_id . '][menu-item-classes]" value="'. esc_attr( implode( ' ', $item->classes ) ) .'" />';
    2564         $output .= '<input type="hidden" class="menu-item-xfn" name="menu-item[' . $possible_object_id . '][menu-item-xfn]" value="'. esc_attr( $item->xfn ) .'" />';
    2565     }
    2566 }
    2567 
    2568 /**
    2569  * Base class for the BuddyPress Suggestions API.
    2570  *
    2571  * Originally built to power BuddyPress' at-mentions suggestions, it's flexible enough to be used
    2572  * for similar kinds of future core requirements, or those desired by third-party developers.
    2573  *
    2574  * To implement a new suggestions service, create a new class that extends this one, and update
    2575  * the list of default services in {@link bp_core_get_suggestions()}. If you're building a plugin,
    2576  * it's recommend that you use the `bp_suggestions_services` filter to do this. :)
    2577  *
    2578  * While the implementation of the query logic is left to you, it should be as quick and efficient
    2579  * as possible. When implementing the abstract methods in this class, pay close attention to the
    2580  * recommendations provided in the phpDoc blocks, particularly the expected return types.
    2581  *
    2582  * @since BuddyPress (2.1.0)
    2583  */
    2584 abstract class BP_Suggestions {
    2585 
    2586     /**
    2587      * Default arguments common to all suggestions services.
    2588      *
    2589      * If your custom service requires further defaults, add them here.
    2590      *
    2591      * @since BuddyPress (2.1.0)
    2592      * @var array
    2593      */
    2594     protected $default_args = array(
    2595         'limit' => 16,
    2596         'term'  => '',
    2597         'type'  => '',
    2598     );
    2599 
    2600     /**
    2601      * Holds the arguments for the query (about to made to the suggestions service).
    2602      *
    2603      * This includes `$default_args`, as well as the user-supplied values.
    2604      *
    2605      * @since BuddyPress (2.1.0)
    2606      * @var array
    2607      */
    2608     protected $args = array(
    2609     );
    2610 
    2611 
    2612     /**
    2613      * Constructor.
    2614      *
    2615      * @param array $args Optional. If set, used as the parameters for the suggestions service query.
    2616      * @since BuddyPress (2.1.0)
    2617      */
    2618     public function __construct( array $args = array() ) {
    2619         if ( ! empty( $args ) ) {
    2620             $this->set_query( $args );
    2621         }
    2622     }
    2623 
    2624     /**
    2625      * Set the parameters for the suggestions service query.
    2626      *
    2627      * @param array $args {
    2628      *     @type int $limit Maximum number of results to display. Optional, default: 16.
    2629      *     @type string $type The name of the suggestion service to use for the request. Mandatory.
    2630      *     @type string $term The suggestion service will try to find results that contain this string.
    2631      *           Mandatory.
    2632      * }
    2633      * @since BuddyPress (2.1.0)
    2634      */
    2635     public function set_query( array $args = array() ) {
    2636         $this->args = wp_parse_args( $args, $this->default_args );
    2637     }
    2638 
    2639     /**
    2640      * Validate and sanitise the parameters for the suggestion service query.
    2641      *
    2642      * Be sure to call this class' version of this method when implementing it in your own service.
    2643      * If validation fails, you must return a WP_Error object.
    2644      *
    2645      * @return true|WP_Error If validation fails, return a WP_Error object. On success, return true (bool).
    2646      * @since BuddyPress (2.1.0)
    2647      */
    2648     public function validate() {
    2649         $this->args['limit'] = absint( $this->args['limit'] );
    2650         $this->args['term']  = trim( sanitize_text_field( $this->args['term'] ) );
    2651         $this->args          = apply_filters( 'bp_suggestions_args', $this->args, $this );
    2652 
    2653 
    2654         // Check for invalid or missing mandatory parameters.
    2655         if ( ! $this->args['limit'] || ! $this->args['term'] ) {
    2656             return new WP_Error( 'missing_parameter' );
    2657         }
    2658 
    2659         // Check for blocked users (e.g. deleted accounts, or spammers).
    2660         if ( is_user_logged_in() && ! bp_is_user_active( get_current_user_id() ) ) {
    2661             return new WP_Error( 'invalid_user' );
    2662         }
    2663 
    2664         return apply_filters( 'bp_suggestions_validate_args', true, $this );
    2665     }
    2666 
    2667     /**
    2668      * Find and return a list of suggestions that match the query.
    2669      *
    2670      * The return type is important. If no matches are found, an empty array must be returned.
    2671      * Matches must be returned as objects in an array.
    2672      *
    2673      * The object format for each match must be: { 'ID': string, 'image': string, 'name': string }
    2674      * For example: { 'ID': 'admin', 'image': 'http://example.com/logo.png', 'name': 'Name Surname' }
    2675      *
    2676      * @return array|WP_Error Array of results. If there were problems, returns a WP_Error object.
    2677      * @since BuddyPress (2.1.0)
    2678      */
    2679     abstract public function get_suggestions();
    2680 }
    2681 
    2682 /**
    2683  * Adds support for user at-mentions to the Suggestions API.
    2684  *
    2685  * This class is in the Core component because it's required by a class in the Groups component,
    2686  * and Groups is loaded before Members (alphabetical order).
    2687  *
    2688  * @since BuddyPress (2.1.0)
    2689  */
    2690 class BP_Members_Suggestions extends BP_Suggestions {
    2691 
    2692     /**
    2693      * Default arguments for this suggestions service.
    2694      *
    2695      * @since BuddyPress (2.1.0)
    2696      * @var array $args {
    2697      *     @type int $limit Maximum number of results to display. Default: 16.
    2698      *     @type bool $only_friends If true, only match the current user's friends. Default: false.
    2699      *     @type string $term The suggestion service will try to find results that contain this string.
    2700      *           Mandatory.
    2701      * }
    2702      */
    2703     protected $default_args = array(
    2704         'limit'        => 10,
    2705         'only_friends' => false,
    2706         'term'         => '',
    2707         'type'         => '',
    2708     );
    2709 
    2710 
    2711     /**
    2712      * Validate and sanitise the parameters for the suggestion service query.
    2713      *
    2714      * @return true|WP_Error If validation fails, return a WP_Error object. On success, return true (bool).
    2715      * @since BuddyPress (2.1.0)
    2716      */
    2717     public function validate() {
    2718         $this->args['only_friends'] = (bool) $this->args['only_friends'];
    2719         $this->args                 = apply_filters( 'bp_members_suggestions_args', $this->args, $this );
    2720 
    2721         // Check for invalid or missing mandatory parameters.
    2722         if ( $this->args['only_friends'] && ( ! bp_is_active( 'friends' ) || ! is_user_logged_in() ) ) {
    2723             return new WP_Error( 'missing_requirement' );
    2724         }
    2725 
    2726         return apply_filters( 'bp_members_suggestions_validate_args', parent::validate(), $this );
    2727     }
    2728 
    2729     /**
    2730      * Find and return a list of username suggestions that match the query.
    2731      *
    2732      * @return array|WP_Error Array of results. If there were problems, returns a WP_Error object.
    2733      * @since BuddyPress (2.1.0)
    2734      */
    2735     public function get_suggestions() {
    2736         $user_query = array(
    2737             'count_total'     => '',  // Prevents total count
    2738             'populate_extras' => false,
    2739             'type'            => 'alphabetical',
    2740 
    2741             'page'            => 1,
    2742             'per_page'        => $this->args['limit'],
    2743             'search_terms'    => $this->args['term'],
    2744             'search_wildcard' => 'right',
    2745         );
    2746 
    2747         // Only return matches of friends of this user.
    2748         if ( $this->args['only_friends'] && is_user_logged_in() ) {
    2749             $user_query['user_id'] = get_current_user_id();
    2750         }
    2751 
    2752         $user_query = apply_filters( 'bp_members_suggestions_query_args', $user_query, $this );
    2753         if ( is_wp_error( $user_query ) ) {
    2754             return $user_query;
    2755         }
    2756 
    2757 
    2758         $user_query = new BP_User_Query( $user_query );
    2759         $results    = array();
    2760 
    2761         foreach ( $user_query->results as $user ) {
    2762             $result        = new stdClass();
    2763             $result->ID    = $user->user_nicename;
    2764             $result->image = bp_core_fetch_avatar( array( 'html' => false, 'item_id' => $user->ID ) );
    2765             $result->name  = bp_core_get_user_displayname( $user->ID );
    2766 
    2767             $results[] = $result;
    2768         }
    2769 
    2770         return apply_filters( 'bp_members_suggestions_get_suggestions', $results, $this );
    2771     }
    2772 }
    2773 
    2774 /**
    2775  * Base class for creating query classes that generate SQL fragments for filtering results based on recursive query params.
    2776  *
    2777  * @since BuddyPress (2.2.0)
    2778  */
    2779 abstract class BP_Recursive_Query {
    2780 
    2781     /**
    2782      * Query arguments passed to the constructor.
    2783      *
    2784      * @since BuddyPress (2.2.0)
    2785      * @access public
    2786      * @var array
    2787      */
    2788     public $queries = array();
    2789 
    2790     /**
    2791      * Generate SQL clauses to be appended to a main query.
    2792      *
    2793      * Extending classes should call this method from within a publicly
    2794      * accessible get_sql() method, and manipulate the SQL as necessary.
    2795      * For example, {@link BP_XProfile_Query::get_sql()} is merely a wrapper for
    2796      * get_sql_clauses(), while {@link BP_Activity_Query::get_sql()} discards
    2797      * the empty 'join' clause, and only passes the 'where' clause.
    2798      *
    2799      * @since BuddyPress (2.2.0)
    2800      * @access protected
    2801      *
    2802      * @param  string $primary_table
    2803      * @param  string $primary_id_column
    2804      * @return array
    2805      */
    2806     protected function get_sql_clauses() {
    2807         $sql = $this->get_sql_for_query( $this->queries );
    2808 
    2809         if ( ! empty( $sql['where'] ) ) {
    2810             $sql['where'] = ' AND ' . "\n" . $sql['where'] . "\n";
    2811         }
    2812 
    2813         return $sql;
    2814     }
    2815 
    2816     /**
    2817      * Generate SQL clauses for a single query array.
    2818      *
    2819      * If nested subqueries are found, this method recurses the tree to
    2820      * produce the properly nested SQL.
    2821      *
    2822      * Subclasses generally do not need to call this method. It is invoked
    2823      * automatically from get_sql_clauses().
    2824      *
    2825      * @since BuddyPress (2.2.0)
    2826      * @access protected
    2827      *
    2828      * @param  array $query Query to parse.
    2829      * @param  int   $depth Optional. Number of tree levels deep we
    2830      *                      currently are. Used to calculate indentation.
    2831      * @return array
    2832      */
    2833     protected function get_sql_for_query( $query, $depth = 0 ) {
    2834         $sql_chunks = array(
    2835             'join'  => array(),
    2836             'where' => array(),
    2837         );
    2838 
    2839         $sql = array(
    2840             'join'  => '',
    2841             'where' => '',
    2842         );
    2843 
    2844         $indent = '';
    2845         for ( $i = 0; $i < $depth; $i++ ) {
    2846             $indent .= "\t";
    2847         }
    2848 
    2849         foreach ( $query as $key => $clause ) {
    2850             if ( 'relation' === $key ) {
    2851                 $relation = $query['relation'];
    2852             } elseif ( is_array( $clause ) ) {
    2853                 // This is a first-order clause
    2854                 if ( $this->is_first_order_clause( $clause ) ) {
    2855                     $clause_sql = $this->get_sql_for_clause( $clause, $query );
    2856 
    2857                     $where_count = count( $clause_sql['where'] );
    2858                     if ( ! $where_count ) {
    2859                         $sql_chunks['where'][] = '';
    2860                     } elseif ( 1 === $where_count ) {
    2861                         $sql_chunks['where'][] = $clause_sql['where'][0];
    2862                     } else {
    2863                         $sql_chunks['where'][] = '( ' . implode( ' AND ', $clause_sql['where'] ) . ' )';
    2864                     }
    2865 
    2866                     $sql_chunks['join'] = array_merge( $sql_chunks['join'], $clause_sql['join'] );
    2867                 // This is a subquery
    2868                 } else {
    2869                     $clause_sql = $this->get_sql_for_query( $clause, $depth + 1 );
    2870 
    2871                     $sql_chunks['where'][] = $clause_sql['where'];
    2872                     $sql_chunks['join'][]  = $clause_sql['join'];
    2873                 }
    2874             }
    2875         }
    2876 
    2877         // Filter empties
    2878         $sql_chunks['join']  = array_filter( $sql_chunks['join'] );
    2879         $sql_chunks['where'] = array_filter( $sql_chunks['where'] );
    2880 
    2881         if ( empty( $relation ) ) {
    2882             $relation = 'AND';
    2883         }
    2884 
    2885         if ( ! empty( $sql_chunks['join'] ) ) {
    2886             $sql['join'] = implode( ' ', array_unique( $sql_chunks['join'] ) );
    2887         }
    2888 
    2889         if ( ! empty( $sql_chunks['where'] ) ) {
    2890             $sql['where'] = '( ' . "\n\t" . $indent . implode( ' ' . "\n\t" . $indent . $relation . ' ' . "\n\t" . $indent, $sql_chunks['where'] ) . "\n" . $indent . ')' . "\n";
    2891         }
    2892 
    2893         return $sql;
    2894     }
    2895 
    2896     /**
    2897      * Recursive-friendly query sanitizer.
    2898      *
    2899      * Ensures that each query-level clause has a 'relation' key, and that
    2900      * each first-order clause contains all the necessary keys from
    2901      * $defaults.
    2902      *
    2903      * Extend this method if your class uses different sanitizing logic.
    2904      *
    2905      * @since BuddyPress (2.2.0)
    2906      * @access public
    2907      *
    2908      * @param  array $queries Array of query clauses.
    2909      * @return array Sanitized array of query clauses.
    2910      */
    2911     protected function sanitize_query( $queries ) {
    2912         $clean_queries = array();
    2913 
    2914         if ( ! is_array( $queries ) ) {
    2915             return $clean_queries;
    2916         }
    2917 
    2918         foreach ( $queries as $key => $query ) {
    2919             if ( 'relation' === $key ) {
    2920                 $relation = $query;
    2921 
    2922             } elseif ( ! is_array( $query ) ) {
    2923                 continue;
    2924 
    2925             // First-order clause.
    2926             } elseif ( $this->is_first_order_clause( $query ) ) {
    2927                 if ( isset( $query['value'] ) && array() === $query['value'] ) {
    2928                     unset( $query['value'] );
    2929                 }
    2930 
    2931                 $clean_queries[] = $query;
    2932 
    2933             // Otherwise, it's a nested query, so we recurse.
    2934             } else {
    2935                 $cleaned_query = $this->sanitize_query( $query );
    2936 
    2937                 if ( ! empty( $cleaned_query ) ) {
    2938                     $clean_queries[] = $cleaned_query;
    2939                 }
    2940             }
    2941         }
    2942 
    2943         if ( empty( $clean_queries ) ) {
    2944             return $clean_queries;
    2945         }
    2946 
    2947         // Sanitize the 'relation' key provided in the query.
    2948         if ( isset( $relation ) && 'OR' === strtoupper( $relation ) ) {
    2949             $clean_queries['relation'] = 'OR';
    2950 
    2951         /*
    2952          * If there is only a single clause, call the relation 'OR'.
    2953          * This value will not actually be used to join clauses, but it
    2954          * simplifies the logic around combining key-only queries.
    2955          */
    2956         } elseif ( 1 === count( $clean_queries ) ) {
    2957             $clean_queries['relation'] = 'OR';
    2958 
    2959         // Default to AND.
    2960         } else {
    2961             $clean_queries['relation'] = 'AND';
    2962         }
    2963 
    2964         return $clean_queries;
    2965     }
    2966 
    2967     /**
    2968      * Generate JOIN and WHERE clauses for a first-order clause.
    2969      *
    2970      * Must be overridden in a subclass.
    2971      *
    2972      * @since BuddyPress (2.2.0)
    2973      * @access protected
    2974      *
    2975      * @param  array $clause       Array of arguments belonging to the clause.
    2976      * @param  array $parent_query Parent query to which the clause belongs.
    2977      * @return array {
    2978      *     @type array $join  Array of subclauses for the JOIN statement.
    2979      *     @type array $where Array of subclauses for the WHERE statement.
    2980      * }
    2981      */
    2982     abstract protected function get_sql_for_clause( $clause, $parent_query );
    2983 
    2984     /**
    2985      * Determine whether a clause is first-order.
    2986      *
    2987      * Must be overridden in a subclass.
    2988      *
    2989      * @since BuddyPress (2.2.0)
    2990      * @access protected
    2991      *
    2992      * @param  array $q Clause to check.
    2993      * @return bool
    2994      */
    2995     abstract protected function is_first_order_clause( $query );
    2996 }
     12require __DIR__ . '/classes/class-bp-user-query.php';
     13require __DIR__ . '/classes/class-bp-core-user.php';
     14require __DIR__ . '/classes/class-bp-date-query.php';
     15require __DIR__ . '/classes/class-bp-core-notification.php';
     16require __DIR__ . '/classes/class-bp-button.php';
     17require __DIR__ . '/classes/class-bp-embed.php';
     18require __DIR__ . '/classes/class-bp-walker-nav-menu.php';
     19require __DIR__ . '/classes/class-bp-walker-nav-menu-checklist.php';
     20require __DIR__ . '/classes/class-bp-suggestions.php';
     21require __DIR__ . '/classes/class-bp-members-suggestions.php';
     22require __DIR__ . '/classes/class-bp-recursive-query.php';
Note: See TracChangeset for help on using the changeset viewer.