Skip to:
Content

BuddyPress.org

Ticket #4060: 4060.02.patch

File 4060.02.patch, 19.8 KB (added by johnjamesjacoby, 13 years ago)
  • bp-core/bp-core-classes.php

     
    11<?php
     2
    23// Exit if accessed directly
    34if ( !defined( 'ABSPATH' ) ) exit;
    45
    56/**
     7 * BuddyPress User Query class
     8 *
     9 * Used for querying users in a BuddyPress context, in situations where more
     10 * than usermeta is needed. (Friends, Activity, etc...)
     11 *
     12 * @since BuddyPress (1.7)
     13 */
     14class BP_User_Query {
     15
     16        /** Variables *************************************************************/
     17
     18        /**
     19         * Array of variables to query with
     20         *
     21         * @since BuddyPress (1.7)
     22         * @var array
     23         */
     24        public $query_vars = array();
     25
     26        /**
     27         * List of found users and their respective data
     28         *
     29         * @since BuddyPress (1.7)
     30         * @access public To allow components to manipulate them
     31         * @var array
     32         */
     33        public $results = array();
     34
     35        /**
     36         * Total number of found users for the current query
     37         *
     38         * @since BuddyPress (1.7)
     39         * @access public To allow components to manipulate it
     40         * @var int
     41         */
     42        public $total_users = 0;
     43
     44        /**
     45         * List of found user ID's
     46         *
     47         * @since BuddyPress (1.7)
     48         * @access private To disallow components from manipulating them
     49         * @var array
     50         */
     51        private $user_ids = array();
     52
     53        /**
     54         * SQL clauses for the user ID query
     55         *
     56         * @since BuddyPress (1.7)
     57         * @access protected To disallow
     58         * @var array()
     59         */
     60        private $uid_clauses = array();
     61
     62        /**
     63         * SQL database column name to order by
     64         *
     65         * @since BuddyPress (1.7)
     66         * @var string
     67         */
     68        private $uid_name = '';
     69
     70        /** Methods ***************************************************************/
     71
     72        /**
     73         * Constructor
     74         *
     75         * @since 1.7
     76         *
     77         * @param string|array $query The query variables
     78         */
     79        public function __construct( $query = null ) {
     80                if ( ! empty( $query ) ) {
     81                        $this->query_vars = wp_parse_args( $query, array(
     82                                'type'            => 'newest',
     83                                'per_page'        => 0,
     84                                'page'            => 1,
     85                                'user_id'         => 0,
     86                                'include'         => false,
     87                                'search_terms'    => false,
     88                                'exclude'         => false,
     89                                'meta_key'        => false,
     90                                'meta_value'      => false,
     91                                'populate_extras' => true,
     92                                'count_total'     => 'count_query'
     93                        ) );
     94
     95                        // Get user ids
     96                        $this->prepare_user_ids_query();
     97                        $this->do_user_ids_query();
     98                }
     99
     100                // Bail if no user IDs were found
     101                if ( empty( $this->user_ids ) ) {
     102                        return;
     103                }
     104
     105                // Fetch additional data. First, using WP_User_Query
     106                $this->do_wp_user_query();
     107
     108                // Get BuddyPress specific user data
     109                if ( !empty( $this->query_vars['populate_extras'] ) ) {
     110                        $this->populate_extras();
     111                }
     112        }
     113
     114        /**
     115         * Prepare the query for user_ids
     116         *
     117         * @since BuddyPress (1.7)
     118         */
     119        protected function prepare_user_ids_query() {
     120                global $wpdb, $bp;
     121
     122                // Default query variables used here
     123                $type         = '';
     124                $per_page     = 0;
     125                $page         = 1;
     126                $user_id      = 0;
     127                $include      = false;
     128                $search_terms = false;
     129                $exclude      = false;
     130                $meta_key     = false;
     131                $meta_value   = false;
     132
     133                extract( $this->query_vars );
     134
     135                // Setup the main SQL query container
     136                $sql = array(
     137                        'select'  => '',
     138                        'where'   => array(),
     139                        'orderby' => '',
     140                        'order'   => '',
     141                        'limit'   => ''
     142                );
     143
     144                /** TYPE **************************************************************/
     145
     146                // Determines the sort order, which means it also determines where the
     147                // user IDs are drawn from (the SELECT and WHERE statements)
     148                if ( in_array( $type, array( 'active', 'online', 'newest', 'popular' ) ) ) {
     149                        $this->uid_name = 'user_id';
     150                        $sql['select']  = "SELECT DISTINCT u.{$this->uid_name} as id FROM {$wpdb->usermeta} u";
     151                        $sql['where'][] = "u.meta_key = 'last_activity'";
     152
     153                        if ( 'newest' == $type ) {
     154                                $sql['orderby'] = "ORDER BY u.user_id";
     155                        } else {
     156                                $sql['orderby'] = "ORDER BY u.meta_value";
     157                        }
     158
     159                        $sql['order'] = "DESC";
     160
     161                // Order by username, which gets tricky when xprofile is activated
     162                } elseif ( 'alphabetical' == $type ) {
     163
     164                        // We prefer to do alphabetical sorts against the display_name field
     165                        // of wp_users, because the table is smaller and better indexed. We
     166                        // can do so if xprofile sync is enabled, or if xprofile is inactive.
     167                        //
     168                        // @todo remove need for bp_is_active() check
     169                        if ( ! bp_disable_profile_sync() || ! bp_is_active( 'xprofile' ) ) {
     170                                $this->uid_name = 'ID';
     171                                $sql['select']  = "SELECT DISTINCT u.{$this->uid_name} as id FROM {$wpdb->users} u";
     172                                $sql['orderby'] = "ORDER BY u.display_name";
     173                                $sql['order']   = "ASC";
     174
     175                        // When profile sync is disabled, alphabetical sorts must happen against
     176                        // the xprofile table
     177                        } else {
     178                                $fullname_field_id = $wpdb->get_var( $wpdb->prepare( "SELECT id FROM {$bp->profile->table_name_fields} WHERE name = %s", bp_xprofile_fullname_field_name() ) );
     179
     180                                $this->uid_name = 'user_id';
     181                                $sql['select']  = "SELECT DISTINCT u.{$this->uid_name} as id FROM {$bp->profile->table_name_data} u";
     182                                $sql['where'][] = "u.field_id = {$fullname_field_id}";
     183                                $sql['orderby'] = "ORDER BY u.value";
     184                                $sql['order']   = "ASC";
     185                        }
     186                }
     187
     188                /** WHERE *************************************************************/
     189
     190                // 'include' - User ids to include in the results
     191                if ( false !== $include ) {
     192                        $include        = wp_parse_id_list( $include );
     193                        $include_ids    = $wpdb->escape( implode( ',', (array) $include ) );
     194                        $sql['where'][] = "u.{$this->uid_name} IN ({$include_ids})";
     195                }
     196
     197                // 'exclude' - User ids to exclude from the results
     198                if ( false !== $exclude ) {
     199                        $exclude        = wp_parse_id_list( $exclude );
     200                        $exclude_ids    = $wpdb->escape( implode( ',', (array) $exclude ) );
     201                        $sql['where'][] = "u.{$this->uid_name} NOT IN ({$exclude_ids})";
     202                }
     203
     204                // 'user_id' - When a user id is passed, limit to the friends of the user
     205                // @todo remove need for bp_is_active() check
     206                if ( !empty( $user_id ) && bp_is_active( 'friends' ) ) {
     207                        $friend_ids = friends_get_friend_user_ids( $user_id );
     208                        $friend_ids = $wpdb->escape( implode( ',', (array) $friend_ids ) );
     209
     210                        if ( !empty( $friend_ids ) ) {
     211                                $sql['where'][] = "u.{$this->uid_name} NOT IN ({$friend_ids})";
     212                        } else {
     213                                // If the user has no friends, make sure the query returns null
     214                                $sql['where'][] = "0 = 1";
     215                        }
     216                }
     217
     218                /** Search Terms ******************************************************/
     219
     220                // To avoid global joins, do a separate query
     221                // @todo remove need for bp_is_active() check
     222                if ( false !== $search_terms && bp_is_active( 'xprofile' ) ) {
     223                        $found_user_ids = $wpdb->get_row( $wpdb->prepare( "SELECT user_id FROM {$bp->profile->table_name_data} WHERE value LIKE %s", '%%' . like_escape( $search_terms ) . '%%' ), ARRAY_N );
     224
     225                        if ( ! empty( $found_user_ids ) ) {
     226                                $sql['where'][] = "u.{$this->uid_name} IN (" . implode( ',', wp_parse_id_list( $found_user_ids ) ) . ")";
     227                        }
     228                }
     229
     230                // 'meta_key', 'meta_value' - join against usermeta
     231                // @todo Use WP_User_Query to get IDs for an IN clause?
     232                if ( false !== $meta_key ) {
     233                        $sql['select']  .= " LEFT JOIN {$wpdb->usermeta} um ON (um.user_id = u.{$this->uid_name})";
     234                        $sql['where'][]  = $wpdb->prepare( "um.meta_key = %s", $meta_key );
     235
     236                        // If a meta value is provided, match it
     237                        if ( false !== $meta_value ) {
     238                                $sql['where'][] = $wpdb->prepare( "um.meta_value = %s", $meta_value );
     239                        }
     240                }
     241
     242                // 'per_page', 'page' - handles LIMIT
     243                if ( !empty( $per_page ) && !empty( $page ) ) {
     244                        $sql['limit'] = $wpdb->prepare( "LIMIT %d, %d", intval( ( $page - 1 ) * $per_page ), intval( $per_page ) );
     245                } else {
     246                        $sql['limit'] = '';
     247                }
     248
     249                // Assemble the query chunks
     250                $this->uid_clauses['select']  = $sql['select'];
     251                $this->uid_clauses['where']   = ! empty( $sql['where'] ) ? 'WHERE ' . implode( ' AND ', $sql['where'] ) : '';
     252                $this->uid_clauses['orderby'] = $sql['orderby'];
     253                $this->uid_clauses['order']   = $sql['order'];
     254                $this->uid_clauses['limit']   = $sql['limit'];
     255
     256                do_action_ref_array( 'bp_pre_user_query', array( &$this ) );
     257        }
     258
     259        /**
     260         * Perform a database query to specifically get only user IDs, using
     261         * existing query variables set previously in the constructor.
     262         *
     263         * Also used to quickly perform user total counts.
     264         *
     265         * @since BuddyPress (1.7)
     266         */
     267        protected function do_user_ids_query() {
     268                global $wpdb;
     269
     270                // If counting using SQL_CALC_FOUND_ROWS, set it up here
     271                if ( 'sql_calc_found_rows' == $this->query_vars['count_total'] ) {
     272                        $this->uid_clauses['select'] = str_replace( 'SELECT', 'SELECT SQL_CALC_FOUND_ROWS', $this->uid_clauses['select'] );
     273                }
     274
     275                // Get the specific user ids
     276                $this->user_ids = $wpdb->get_col( $wpdb->prepare( "{$this->uid_clauses['select']} {$this->uid_clauses['where']} {$this->uid_clauses['orderby']} {$this->uid_clauses['order']} {$this->uid_clauses['limit']}" ) );
     277
     278                // Get the total user count
     279                if ( 'sql_calc_found_rows' == $this->query_vars['count_total'] ) {
     280                        $this->total_users = $wpdb->get_var( apply_filters( 'bp_found_user_query', "SELECT FOUND_ROWS()", $this ) );
     281                } elseif ( 'count_query' == $this->query_vars['count_total'] ) {
     282                        $count_select      = preg_replace( '/^SELECT.*?FROM (\S+) u/', "SELECT COUNT(DISTINCT u.{$this->uid_name}) FROM $1 u", $this->uid_clauses['select'] );
     283                        $this->total_users = $wpdb->get_var( apply_filters( 'bp_found_user_query', "{$count_select} {$this->uid_clauses['where']}", $this ) );
     284                }
     285        }
     286
     287        /**
     288         * Perform a database query using the WP_User_Query() object, using existing
     289         * fields, variables, and user ID's set previously in this class.
     290         *
     291         * @since BuddyPress (1.7)
     292         */
     293        protected function do_wp_user_query() {
     294                $wp_user_query = new WP_User_Query( apply_filters( 'bp_wp_user_query_args', array(
     295
     296                        // Relevant
     297                        'fields'      => array( 'ID', 'user_registered', 'user_login', 'user_nicename', 'display_name', 'user_email' ),
     298                        'include'     => $this->user_ids,
     299
     300                        // Overrides
     301                        'blog_id'     => 0,    // BP does not require blog roles
     302                        'count_total' => false // We already have a count
     303
     304                ), $this ) );
     305
     306                // Reindex for easier matching
     307                $r = array();
     308                foreach ( $wp_user_query->results as $u ) {
     309                        $r[ $u->ID ] = $u;
     310                }
     311
     312                // Match up to the user ids from the main query
     313                foreach ( $this->user_ids as $uid ) {
     314                        if ( isset( $r[ $uid ] ) ) {
     315                                $this->results[ $uid ] = $r[ $uid ];
     316
     317                                // The BP template functions expect an 'id'
     318                                // (as opposed to 'ID') property
     319                                $this->results[ $uid ]->id = $uid;
     320                        }
     321                }
     322        }
     323
     324        /**
     325         * Perform a database query to populate any extra metadata we might need.
     326         * Different components will hook into the 'bp_user_query_populate_extras'
     327         * action to loop in the things they want.
     328         *
     329         * @since BuddyPress (1.7)
     330         *
     331         * @global BuddyPress $bp
     332         * @global WPDB $wpdb
     333         * @return
     334         */
     335        protected function populate_extras() {
     336                global $wpdb;
     337
     338                // Bail if no users
     339                if ( empty( $this->user_ids ) || empty( $this->results ) )
     340                        return;
     341
     342                // Turn user ID's into a query-usable, comma separated value
     343                $user_ids_sql = implode( ',', wp_parse_id_list( $this->user_ids ) );
     344
     345                /**
     346                 * Use this action to independently populate your own custom extras.
     347                 *
     348                 * Note that anything you add here should query using $user_ids_sql, to
     349                 * avoid running multiple queries per user in the loop.
     350                 *
     351                 * Two BuddyPress components currently do this:
     352                 * - XProfile: To override display names
     353                 * - Friends:  To set whether or not a user is the current users friend
     354                 *
     355                 * @see bp_xprofile_filter_user_query_populate_extras()
     356                 * @see bp_friends_filter_user_query_populate_extras()
     357                 */
     358                do_action_ref_array( 'bp_user_query_populate_extras', array( $this, $user_ids_sql ) );
     359
     360                // Fetch usermeta data
     361                // We want the three following pieces of info from usermeta:
     362                // - friend count
     363                // - last activity
     364                // - latest update
     365                $total_friend_count_key = bp_get_user_meta_key( 'total_friend_count' );
     366                $last_activity_key      = bp_get_user_meta_key( 'last_activity'      );
     367                $bp_latest_update_key   = bp_get_user_meta_key( 'bp_latest_update'   );
     368
     369                // Create, prepare, and run the seperate usermeta query
     370                $user_metas = $wpdb->get_results( $wpdb->prepare( "SELECT user_id, meta_key, meta_value FROM {$wpdb->usermeta} WHERE meta_key IN (%s,%s,%s) AND user_id IN ({$user_ids_sql})", $total_friend_count_key, $last_activity_key, $bp_latest_update_key ) );
     371
     372                // The $members_template global expects the index key to be different
     373                // from the meta_key in some cases, so we rejig things here.
     374                foreach ( $user_metas as $user_meta ) {
     375                        switch ( $user_meta->meta_key ) {
     376                                case $total_friend_count_key :
     377                                        $key = 'total_friend_count';
     378                                        break;
     379
     380                                case $last_activity_key :
     381                                        $key = 'last_activity';
     382                                        break;
     383
     384                                case $bp_latest_update_key :
     385                                        $key = 'latest_update';
     386                                        break;
     387                        }
     388
     389                        if ( isset( $this->results[ $user_meta->user_id ] ) ) {
     390                                $this->results[ $user_meta->user_id ]->{$key} = $user_meta->meta_value;
     391                        }
     392                }
     393        }
     394}
     395
     396/**
    6397 * BP_Core_User class can be used by any component. It will fetch useful
    7398 * details for any user when provided with a user_id.
    8399 *
     
    201592        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 ) {
    202593                global $wpdb, $bp;
    203594
     595                _deprecated_function( __METHOD__, '1.7', 'BP_User_Query' );
     596
    204597                $sql = array();
    205598
    206599                $sql['select_main'] = "SELECT DISTINCT u.ID as id, u.user_registered, u.user_nicename, u.user_login, u.display_name, u.user_email";
  • bp-friends/bp-friends-filters.php

     
     1<?php
     2
     3/**
     4 * BuddyPress Friend Filters
     5 *
     6 * @package BuddyPress
     7 * @subpackage FriendsFilters
     8 */
     9
     10/**
     11 * Filter BP_User_Query::populate_extras to override each queries users fullname
     12 *
     13 * @since BuddyPress (1.7)
     14 *
     15 * @global BuddyPress $bp
     16 * @global WPDB $wpdb
     17 * @param BP_User_Query $user_query
     18 * @param string $user_ids_sql
     19 */
     20function bp_friends_filter_user_query_populate_extras( BP_User_Query $user_query, $user_ids_sql ) {
     21        global $bp, $wpdb;
     22
     23        // Fetch whether or not the user is a friend of the current user
     24        $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_sql} ) ) OR (initiator_user_id IN ( {$user_ids_sql} ) AND friend_user_id = %d )", bp_loggedin_user_id(), bp_loggedin_user_id() ) );
     25
     26        // The "friend" is the user ID in the pair who is *not* the logged in user
     27        foreach ( $friend_status as $fs ) {
     28                $friend_id = bp_loggedin_user_id() == $fs->initiator_user_id ? $fs->friend_user_id : $fs->initiator_user_id;
     29
     30                if ( isset( $user_query->results[ $friend_id ] ) ) {
     31                        $user_query->results[ $friend_id ]->is_friend = $fs->is_confirmed;
     32                }
     33        }
     34}
     35add_filter( 'bp_user_query_populate_extras', 'bp_friends_filter_user_query_populate_extras', 4, 2 );
  • bp-members/bp-members-functions.php

     
    7171/**
    7272 * Return an array of users IDs based on the parameters passed.
    7373 *
     74 * Since BuddyPress 1.7, bp_core_get_users() uses BP_User_Query. If you
     75 * need backward compatibility with BP_Core_User::get_users(), filter the
     76 * bp_use_legacy_user_query value, returning true.
     77 *
    7478 * @package BuddyPress Core
    7579 */
    7680function bp_core_get_users( $args = '' ) {
    7781
    78         $defaults = array(
    79                 'type'            => 'active', // active, newest, alphabetical, random or popular
    80                 'user_id'         => false,    // Pass a user_id to limit to only friend connections for this user
    81                 'exclude'         => false,    // Users to exclude from results
    82                 'search_terms'    => false,    // Limit to users that match these search terms
    83                 'meta_key'        => false,    // Limit to users who have this piece of usermeta
    84                 'meta_value'      => false,    // With meta_key, limit to users where usermeta matches this value
     82        // Parse the user query arguments
     83        $params = wp_parse_args( $args, array(
     84                'type'            => 'active',     // active, newest, alphabetical, random or popular
     85                'user_id'         => false,        // Pass a user_id to limit to only friend connections for this user
     86                'exclude'         => false,        // Users to exclude from results
     87                'search_terms'    => false,        // Limit to users that match these search terms
     88                'meta_key'        => false,        // Limit to users who have this piece of usermeta
     89                'meta_value'      => false,        // With meta_key, limit to users where usermeta matches this value
     90                'include'         => false,        // Pass comma separated list of user_ids to limit to only these users
     91                'per_page'        => 20,           // The number of results to return per page
     92                'page'            => 1,            // The page to return if limiting per page
     93                'populate_extras' => true,         // Fetch the last active, where the user is a friend, total friend count, latest update
     94                'count_total'     => 'count_query' // What kind of total user count to do, if any. 'count_query', 'sql_calc_found_rows', or false
     95        ) );
    8596
    86                 'include'         => false,    // Pass comma separated list of user_ids to limit to only these users
    87                 'per_page'        => 20,       // The number of results to return per page
    88                 'page'            => 1,        // The page to return if limiting per page
    89                 'populate_extras' => true,     // Fetch the last active, where the user is a friend, total friend count, latest update
    90         );
     97        // For legacy users. Use of BP_Core_User::get_users() is deprecated.
     98        if ( apply_filters( 'bp_use_legacy_user_query', false, __FUNCTION__, $params ) ) {
     99                extract( $params, EXTR_SKIP );
     100                $retval = BP_Core_User::get_users( $type, $per_page, $page, $user_id, $include, $search_terms, $populate_extras, $exclude, $meta_key, $meta_value );
    91101
    92         $params = wp_parse_args( $args, $defaults );
    93         extract( $params, EXTR_SKIP );
     102        // Default behavior as of BuddyPress 1.7
     103        } else {               
    94104
    95         return apply_filters( 'bp_core_get_users', BP_Core_User::get_users( $type, $per_page, $page, $user_id, $include, $search_terms, $populate_extras, $exclude, $meta_key, $meta_value ), $params );
     105                // Get users like we were asked to do...
     106                $users = new BP_User_Query( $params );
     107
     108                // ...but reformat the results to match bp_core_get_users() behavior.
     109                $retval = array(
     110                        'users' => array_values( $users->results ),
     111                        'total' => $users->total_users
     112                );
     113        }
     114
     115        return apply_filters( 'bp_core_get_users', $retval, $params );
    96116}
    97117
    98118/**
  • bp-xprofile/bp-xprofile-filters.php

     
    219219}
    220220add_filter( 'comments_array', 'xprofile_filter_comments', 10, 2 );
    221221
     222/**
     223 * Filter BP_User_Query::populate_extras to override each queries users fullname
     224 *
     225 * @since BuddyPress (1.7)
     226 *
     227 * @global BuddyPress $bp
     228 * @global WPDB $wpdb
     229 * @param BP_User_Query $user_query
     230 * @param string $user_ids_sql
     231 */
     232function bp_xprofile_filter_user_query_populate_extras( BP_User_Query $user_query, $user_ids_sql ) {
     233        global $bp, $wpdb;
    222234
     235        if ( bp_is_active( 'xprofile' ) && ( 'alphabetical' != $user_query->query_vars['type'] ) ) {
     236                $fullname_field_id = $wpdb->get_var( $wpdb->prepare( "SELECT id FROM {$bp->profile->table_name_fields} WHERE name = %s", bp_xprofile_fullname_field_name() ) );
     237                $user_id_names     = $wpdb->get_results( $wpdb->prepare( "SELECT user_id, value as fullname FROM {$bp->profile->table_name_data} WHERE user_id IN ({$user_ids_sql}) AND field_id = {$fullname_field_id}" ) );
    223238
     239                // Loop through names and override each user's fullname
     240                foreach ( $user_id_names as $user ) {
     241                        if ( isset( $user_query->results[ $user->user_id ] ) ) {
     242                                $user_query->results[ $user->user_id ]->fullname = $user->fullname;
     243                        }
     244                }
     245        }
     246}
     247add_filter( 'bp_user_query_populate_extras', 'bp_xprofile_filter_user_query_populate_extras', 2, 2 );
     248
    224249?>