[ Index ] |
PHP Cross Reference of BuddyPress |
[Summary view] [Print] [Text view]
1 <?php 2 /** 3 * Core component classes. 4 * 5 * @package BuddyPress 6 * @subpackage Core 7 * @since 1.7.0 8 */ 9 10 // Exit if accessed directly. 11 defined( 'ABSPATH' ) || exit; 12 13 /** 14 * BuddyPress User Query class. 15 * 16 * Used for querying users in a BuddyPress context, in situations where WP_User_Query won't do the trick: 17 * Member directories, the Friends component, etc. 18 * 19 * @since 1.7.0 20 * @since 10.0.0 Added $date_query parameter. 21 * 22 * @param array $query { 23 * Query arguments. All items are optional. 24 * @type string $type Determines sort order. Select from 'newest', 'active', 'online', 25 * 'random', 'popular', 'alphabetical'. Default: 'newest'. 26 * @type int $per_page Number of results to return. Default: 0 (no limit). 27 * @type int $page Page offset (together with $per_page). Default: 1. 28 * @type int $user_id ID of a user. If present, and if the friends component is activated, 29 * results will be limited to the friends of that user. Default: 0. 30 * @type string|bool $search_terms Terms to search by. Search happens across xprofile fields. Requires 31 * XProfile component. Default: false. 32 * @type string $search_wildcard When searching with $search_terms, set where wildcards around the 33 * term should be positioned. Accepts 'both', 'left', 'right'. 34 * Default: 'both'. 35 * @type array|string|bool $include An array or comma-separated list of user IDs to which query should 36 * be limited. Default: false. 37 * @type array|string|bool $exclude An array or comma-separated list of user IDs that will be excluded 38 * from query results. Default: false. 39 * @type array|string|bool $user_ids An array or comma-separated list of IDs corresponding to the users 40 * that should be returned. When this parameter is passed, it will 41 * override all others; BP User objects will be constructed using these 42 * IDs only. Default: false. 43 * @type array|string $member_type Array or comma-separated list of member types to limit results to. 44 * @type array|string $member_type__in Array or comma-separated list of member types to limit results to. 45 * @type array|string $member_type__not_in Array or comma-separated list of member types that will be 46 * excluded from results. 47 * @type string|bool $meta_key Limit results to users that have usermeta associated with this meta_key. 48 * Usually used with $meta_value. Default: false. 49 * @type string|bool $meta_value When used with $meta_key, limits results to users whose usermeta value 50 * associated with $meta_key matches $meta_value. Default: false. 51 * @type array $xprofile_query Filter results by xprofile data. Requires the xprofile component. 52 * See {@see BP_XProfile_Query} for details. 53 * @type array $date_query Filter results by member last activity date. See first parameter of 54 * {@link WP_Date_Query::__construct()} for syntax. Only applicable if 55 * $type is either 'active', 'random', 'newest', or 'online'. 56 * @type bool $populate_extras True if you want to fetch extra metadata 57 * about returned users, such as total group and friend counts. 58 * @type string $count_total Determines how BP_User_Query will do a count of total users matching 59 * the other filter criteria. Default value is 'count_query', which 60 * does a separate SELECT COUNT query to determine the total. 61 * 'sql_count_found_rows' uses SQL_COUNT_FOUND_ROWS and 62 * SELECT FOUND_ROWS(). Pass an empty string to skip the total user 63 * count query. 64 * } 65 */ 66 class BP_User_Query { 67 68 /** Variables *************************************************************/ 69 70 /** 71 * Unaltered params as passed to the constructor. 72 * 73 * @since 1.8.0 74 * @var array 75 */ 76 public $query_vars_raw = array(); 77 78 /** 79 * Array of variables to query with. 80 * 81 * @since 1.7.0 82 * @var array 83 */ 84 public $query_vars = array(); 85 86 /** 87 * List of found users and their respective data. 88 * 89 * @since 1.7.0 90 * @var array 91 */ 92 public $results = array(); 93 94 /** 95 * Total number of found users for the current query. 96 * 97 * @since 1.7.0 98 * @var int 99 */ 100 public $total_users = 0; 101 102 /** 103 * List of found user IDs. 104 * 105 * @since 1.7.0 106 * @var array 107 */ 108 public $user_ids = array(); 109 110 /** 111 * SQL clauses for the user ID query. 112 * 113 * @since 1.7.0 114 * @var array 115 */ 116 public $uid_clauses = array(); 117 118 /** 119 * SQL table where the user ID is being fetched from. 120 * 121 * @since 2.2.0 122 * @var string 123 */ 124 public $uid_table = ''; 125 126 /** 127 * SQL database column name to order by. 128 * 129 * @since 1.7.0 130 * @var string 131 */ 132 public $uid_name = ''; 133 134 /** 135 * Standard response when the query should not return any rows. 136 * 137 * @since 1.7.0 138 * @var string 139 */ 140 protected $no_results = array( 'join' => '', 'where' => '0 = 1' ); 141 142 143 /** Methods ***************************************************************/ 144 145 /** 146 * Constructor. 147 * 148 * @since 1.7.0 149 * 150 * @param string|array|null $query See {@link BP_User_Query}. 151 */ 152 public function __construct( $query = null ) { 153 154 // Store the raw query vars for later access. 155 $this->query_vars_raw = $query; 156 157 // Allow extending classes to register action/filter hooks. 158 $this->setup_hooks(); 159 160 if ( ! empty( $this->query_vars_raw ) ) { 161 $this->query_vars = bp_parse_args( 162 $this->query_vars_raw, 163 array( 164 'type' => 'newest', 165 'per_page' => 0, 166 'page' => 1, 167 'user_id' => 0, 168 'search_terms' => false, 169 'search_wildcard' => 'both', 170 'include' => false, 171 'exclude' => false, 172 'user_ids' => false, 173 'member_type' => '', 174 'member_type__in' => '', 175 'member_type__not_in' => '', 176 'meta_key' => false, 177 'meta_value' => false, 178 'xprofile_query' => false, 179 'date_query' => false, 180 'populate_extras' => true, 181 'count_total' => 'count_query', 182 ) 183 ); 184 185 /** 186 * Fires before the construction of the BP_User_Query query. 187 * 188 * @since 1.7.0 189 * 190 * @param BP_User_Query $this Current instance of the BP_User_Query. Passed by reference. 191 */ 192 do_action_ref_array( 'bp_pre_user_query_construct', array( &$this ) ); 193 194 // Get user ids. 195 // If the user_ids param is present, we skip the query. 196 if ( false !== $this->query_vars['user_ids'] ) { 197 $this->user_ids = wp_parse_id_list( $this->query_vars['user_ids'] ); 198 } else { 199 $this->prepare_user_ids_query(); 200 $this->do_user_ids_query(); 201 } 202 } 203 204 // Bail if no user IDs were found. 205 if ( empty( $this->user_ids ) ) { 206 return; 207 } 208 209 // Fetch additional data. First, using WP_User_Query. 210 $this->do_wp_user_query(); 211 212 // Get BuddyPress specific user data. 213 $this->populate_extras(); 214 } 215 216 /** 217 * Allow extending classes to set up action/filter hooks. 218 * 219 * When extending BP_User_Query, you may need to use some of its 220 * internal hooks to modify the output. It's not convenient to call 221 * add_action() or add_filter() in your class constructor, because 222 * BP_User_Query::__construct() contains a fair amount of logic that 223 * you may not want to override in your class. Define this method in 224 * your own class if you need a place where your extending class can 225 * add its hooks early in the query-building process. See 226 * {@link BP_Group_Member_Query::setup_hooks()} for an example. 227 * 228 * @since 1.8.0 229 */ 230 public function setup_hooks() {} 231 232 /** 233 * Prepare the query for user_ids. 234 * 235 * @since 1.7.0 236 */ 237 public function prepare_user_ids_query() { 238 global $wpdb; 239 240 $bp = buddypress(); 241 242 // Default query variables used here. 243 $type = ''; 244 $per_page = 0; 245 $page = 1; 246 $user_id = 0; 247 $include = false; 248 $search_terms = false; 249 $exclude = false; 250 $meta_key = false; 251 $meta_value = false; 252 253 extract( $this->query_vars ); 254 255 // Setup the main SQL query container. 256 $sql = array( 257 'select' => '', 258 'where' => array('1=1'), 259 'orderby' => '', 260 'order' => '', 261 'limit' => '' 262 ); 263 264 /* TYPE **************************************************************/ 265 266 // Determines the sort order, which means it also determines where the 267 // user IDs are drawn from (the SELECT and WHERE statements). 268 switch ( $type ) { 269 270 // 'online' query happens against the last_activity usermeta key 271 // Filter 'bp_user_query_online_interval' to modify the 272 // number of minutes used as an interval. 273 case 'online' : 274 $this->uid_name = 'user_id'; 275 $this->uid_table = $bp->members->table_name_last_activity; 276 $sql['select'] = "SELECT u.{$this->uid_name} as id FROM {$this->uid_table} u"; 277 $sql['where'][] = $wpdb->prepare( "u.component = %s AND u.type = 'last_activity'", buddypress()->members->id ); 278 279 /** 280 * Filters the threshold for activity timestamp minutes since to indicate online status. 281 * 282 * @since 1.8.0 283 * 284 * @param int $value Amount of minutes for threshold. Default 15. 285 */ 286 $sql['where'][] = $wpdb->prepare( "u.date_recorded >= DATE_SUB( UTC_TIMESTAMP(), INTERVAL %d MINUTE )", apply_filters( 'bp_user_query_online_interval', 15 ) ); 287 $sql['orderby'] = "ORDER BY u.date_recorded"; 288 $sql['order'] = "DESC"; 289 290 // Date query. 291 $date_query = BP_Date_Query::get_where_sql( $date_query, 'u.date_recorded' ); 292 if ( ! empty( $date_query ) ) { 293 $sql['where']['date_query'] = $date_query; 294 } 295 296 break; 297 298 // 'active', 'newest', and 'random' queries 299 // all happen against the last_activity usermeta key. 300 case 'active' : 301 case 'newest' : 302 case 'random' : 303 $this->uid_name = 'user_id'; 304 $this->uid_table = $bp->members->table_name_last_activity; 305 $sql['select'] = "SELECT u.{$this->uid_name} as id FROM {$this->uid_table} u"; 306 $sql['where'][] = $wpdb->prepare( "u.component = %s AND u.type = 'last_activity'", buddypress()->members->id ); 307 308 if ( 'newest' == $type ) { 309 $sql['orderby'] = "ORDER BY u.user_id"; 310 $sql['order'] = "DESC"; 311 } elseif ( 'random' == $type ) { 312 $sql['orderby'] = "ORDER BY rand()"; 313 } else { 314 $sql['orderby'] = "ORDER BY u.date_recorded"; 315 $sql['order'] = "DESC"; 316 } 317 318 // Date query. 319 $date_query = BP_Date_Query::get_where_sql( $date_query, 'u.date_recorded' ); 320 if ( ! empty( $date_query ) ) { 321 $sql['where']['date_query'] = $date_query; 322 } 323 324 break; 325 326 // 'popular' sorts by the 'total_friend_count' usermeta. 327 case 'popular' : 328 $this->uid_name = 'user_id'; 329 $this->uid_table = $wpdb->usermeta; 330 $sql['select'] = "SELECT u.{$this->uid_name} as id FROM {$this->uid_table} u"; 331 $sql['where'][] = $wpdb->prepare( "u.meta_key = %s", bp_get_user_meta_key( 'total_friend_count' ) ); 332 $sql['orderby'] = "ORDER BY CONVERT(u.meta_value, SIGNED)"; 333 $sql['order'] = "DESC"; 334 335 break; 336 337 // 'alphabetical' sorts depend on the xprofile setup. 338 case 'alphabetical' : 339 340 // We prefer to do alphabetical sorts against the display_name field 341 // of wp_users, because the table is smaller and better indexed. We 342 // can do so if xprofile sync is enabled, or if xprofile is inactive. 343 // 344 // @todo remove need for bp_is_active() check. 345 if ( ! bp_disable_profile_sync() || ! bp_is_active( 'xprofile' ) ) { 346 $this->uid_name = 'ID'; 347 $this->uid_table = $wpdb->users; 348 $sql['select'] = "SELECT u.{$this->uid_name} as id FROM {$this->uid_table} u"; 349 $sql['orderby'] = "ORDER BY u.display_name"; 350 $sql['order'] = "ASC"; 351 352 // When profile sync is disabled, alphabetical sorts must happen against 353 // the xprofile table. 354 } else { 355 $this->uid_name = 'user_id'; 356 $this->uid_table = $bp->profile->table_name_data; 357 $sql['select'] = "SELECT u.{$this->uid_name} as id FROM {$this->uid_table} u"; 358 $sql['where'][] = $wpdb->prepare( "u.field_id = %d", bp_xprofile_fullname_field_id() ); 359 $sql['orderby'] = "ORDER BY u.value"; 360 $sql['order'] = "ASC"; 361 } 362 363 // Alphabetical queries ignore last_activity, while BP uses last_activity 364 // to infer spam/deleted/non-activated users. To ensure that these users 365 // are filtered out, we add an appropriate sub-query. 366 $sql['where'][] = "u.{$this->uid_name} IN ( SELECT ID FROM {$wpdb->users} WHERE " . bp_core_get_status_sql( '' ) . " )"; 367 368 break; 369 370 // Any other 'type' falls through. 371 default : 372 $this->uid_name = 'ID'; 373 $this->uid_table = $wpdb->users; 374 $sql['select'] = "SELECT u.{$this->uid_name} as id FROM {$this->uid_table} u"; 375 376 // In this case, we assume that a plugin is 377 // handling order, so we leave those clauses 378 // blank. 379 break; 380 } 381 382 /* WHERE *************************************************************/ 383 384 // 'include' - User ids to include in the results. 385 $include = false !== $include ? wp_parse_id_list( $include ) : array(); 386 $include_ids = $this->get_include_ids( $include ); 387 388 // An array containing nothing but 0 should always fail. 389 if ( 1 === count( $include_ids ) && 0 == reset( $include_ids ) ) { 390 $sql['where'][] = $this->no_results['where']; 391 } elseif ( ! empty( $include_ids ) ) { 392 $include_ids = implode( ',', wp_parse_id_list( $include_ids ) ); 393 $sql['where'][] = "u.{$this->uid_name} IN ({$include_ids})"; 394 } 395 396 // 'exclude' - User ids to exclude from the results. 397 if ( ! empty( $exclude ) ) { 398 $exclude_ids = implode( ',', wp_parse_id_list( $exclude ) ); 399 $sql['where'][] = "u.{$this->uid_name} NOT IN ({$exclude_ids})"; 400 } 401 402 // 'user_id' - When a user id is passed, limit to the friends of the user 403 // @todo remove need for bp_is_active() check. 404 if ( ! empty( $user_id ) && bp_is_active( 'friends' ) ) { 405 $friend_ids = friends_get_friend_user_ids( $user_id ); 406 $friend_ids = implode( ',', wp_parse_id_list( $friend_ids ) ); 407 408 if ( ! empty( $friend_ids ) ) { 409 $sql['where'][] = "u.{$this->uid_name} IN ({$friend_ids})"; 410 411 // If the user has no friends, the query should always 412 // return no users. 413 } else { 414 $sql['where'][] = $this->no_results['where']; 415 } 416 } 417 418 /* Search Terms ******************************************************/ 419 420 // 'search_terms' searches user_login and user_nicename 421 // xprofile field matches happen in bp_xprofile_bp_user_query_search(). 422 if ( false !== $search_terms ) { 423 $search_terms = bp_esc_like( wp_kses_normalize_entities( $search_terms ) ); 424 425 if ( $search_wildcard === 'left' ) { 426 $search_terms_nospace = '%' . $search_terms; 427 $search_terms_space = '%' . $search_terms . ' %'; 428 } elseif ( $search_wildcard === 'right' ) { 429 $search_terms_nospace = $search_terms . '%'; 430 $search_terms_space = '% ' . $search_terms . '%'; 431 } else { 432 $search_terms_nospace = '%' . $search_terms . '%'; 433 $search_terms_space = '%' . $search_terms . '%'; 434 } 435 436 $matched_user_ids = $wpdb->get_col( $wpdb->prepare( 437 "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 )", 438 $search_terms_nospace, 439 $search_terms_space, 440 $search_terms_nospace, 441 $search_terms_space 442 ) ); 443 444 $match_in_clause = empty( $matched_user_ids) ? 'NULL' : implode( ',', $matched_user_ids ); 445 $sql['where']['search'] = "u.{$this->uid_name} IN ({$match_in_clause})"; 446 } 447 448 // Only use $member_type__in if $member_type is not set. 449 if ( empty( $member_type ) && ! empty( $member_type__in ) ) { 450 $member_type = $member_type__in; 451 } 452 453 // Member types to exclude. Note that this takes precedence over inclusions. 454 if ( ! empty( $member_type__not_in ) ) { 455 $member_type_clause = $this->get_sql_clause_for_member_types( $member_type__not_in, 'NOT IN' ); 456 457 // Member types to include. 458 } elseif ( ! empty( $member_type ) ) { 459 $member_type_clause = $this->get_sql_clause_for_member_types( $member_type, 'IN' ); 460 } 461 462 if ( ! empty( $member_type_clause ) ) { 463 $sql['where']['member_type'] = $member_type_clause; 464 } 465 466 // 'meta_key', 'meta_value' allow usermeta search 467 // To avoid global joins, do a separate query. 468 if ( false !== $meta_key ) { 469 $meta_sql = $wpdb->prepare( "SELECT user_id FROM {$wpdb->usermeta} WHERE meta_key = %s", $meta_key ); 470 471 if ( false !== $meta_value ) { 472 $meta_sql .= $wpdb->prepare( " AND meta_value = %s", $meta_value ); 473 } 474 475 $found_user_ids = $wpdb->get_col( $meta_sql ); 476 477 if ( ! empty( $found_user_ids ) ) { 478 $sql['where'][] = "u.{$this->uid_name} IN (" . implode( ',', wp_parse_id_list( $found_user_ids ) ) . ")"; 479 } else { 480 $sql['where'][] = '1 = 0'; 481 } 482 } 483 484 // 'per_page', 'page' - handles LIMIT. 485 if ( !empty( $per_page ) && !empty( $page ) ) { 486 $sql['limit'] = $wpdb->prepare( "LIMIT %d, %d", intval( ( $page - 1 ) * $per_page ), intval( $per_page ) ); 487 } else { 488 $sql['limit'] = ''; 489 } 490 491 /** 492 * Filters the clauses for the user query. 493 * 494 * @since 2.0.0 495 * 496 * @param array $sql Array of SQL clauses to be used in the query. 497 * @param BP_User_Query $this Current BP_User_Query instance. 498 */ 499 $sql = apply_filters_ref_array( 'bp_user_query_uid_clauses', array( $sql, &$this ) ); 500 501 // Assemble the query chunks. 502 $this->uid_clauses['select'] = $sql['select']; 503 $this->uid_clauses['where'] = ! empty( $sql['where'] ) ? 'WHERE ' . implode( ' AND ', $sql['where'] ) : ''; 504 $this->uid_clauses['orderby'] = $sql['orderby']; 505 $this->uid_clauses['order'] = $sql['order']; 506 $this->uid_clauses['limit'] = $sql['limit']; 507 508 /** 509 * Fires before the BP_User_Query query is made. 510 * 511 * @since 1.7.0 512 * 513 * @param BP_User_Query $this Current BP_User_Query instance. Passed by reference. 514 */ 515 do_action_ref_array( 'bp_pre_user_query', array( &$this ) ); 516 } 517 518 /** 519 * Query for IDs of users that match the query parameters. 520 * 521 * Perform a database query to specifically get only user IDs, using 522 * existing query variables set previously in the constructor. 523 * 524 * Also used to quickly perform user total counts. 525 * 526 * @since 1.7.0 527 */ 528 public function do_user_ids_query() { 529 global $wpdb; 530 531 // If counting using SQL_CALC_FOUND_ROWS, set it up here. 532 if ( 'sql_calc_found_rows' == $this->query_vars['count_total'] ) { 533 $this->uid_clauses['select'] = str_replace( 'SELECT', 'SELECT SQL_CALC_FOUND_ROWS', $this->uid_clauses['select'] ); 534 } 535 536 // Get the specific user ids. 537 $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']}" ); 538 539 // Get the total user count. 540 if ( 'sql_calc_found_rows' == $this->query_vars['count_total'] ) { 541 542 /** 543 * Filters the found user SQL statements before query. 544 * 545 * If "sql_calc_found_rows" is the provided count_total query var 546 * then the value will be "SELECT FOUND_ROWS()". Otherwise it will 547 * use a "SELECT COUNT()" query statement. 548 * 549 * @since 1.7.0 550 * 551 * @param string $value SQL statement to select FOUND_ROWS(). 552 * @param BP_User_Query $this Current BP_User_Query instance. 553 */ 554 $this->total_users = $wpdb->get_var( apply_filters( 'bp_found_user_query', "SELECT FOUND_ROWS()", $this ) ); 555 } elseif ( 'count_query' == $this->query_vars['count_total'] ) { 556 $count_select = preg_replace( '/^SELECT.*?FROM (\S+) u/', "SELECT COUNT(u.{$this->uid_name}) FROM $1 u", $this->uid_clauses['select'] ); 557 558 /** This filter is documented in bp-core/classes/class-bp-user-query.php */ 559 $this->total_users = $wpdb->get_var( apply_filters( 'bp_found_user_query', "{$count_select} {$this->uid_clauses['where']}", $this ) ); 560 } 561 } 562 563 /** 564 * Use WP_User_Query() to pull data for the user IDs retrieved in the main query. 565 * 566 * @since 1.7.0 567 */ 568 public function do_wp_user_query() { 569 $fields = array( 'ID', 'user_login', 'user_pass', 'user_nicename', 'user_email', 'user_url', 'user_registered', 'user_activation_key', 'user_status', 'display_name' ); 570 571 if ( is_multisite() ) { 572 $fields[] = 'spam'; 573 $fields[] = 'deleted'; 574 } 575 576 /** 577 * Filters the WP User Query arguments before passing into the class. 578 * 579 * @since 1.7.0 580 * 581 * @param array $value Array of arguments for the user query. 582 * @param BP_User_Query $this Current BP_User_Query instance. 583 */ 584 $wp_user_query = new WP_User_Query( apply_filters( 'bp_wp_user_query_args', array( 585 586 // Relevant. 587 'fields' => $fields, 588 'include' => $this->user_ids, 589 590 // Overrides 591 'blog_id' => 0, // BP does not require blog roles. 592 'count_total' => false // We already have a count. 593 594 ), $this ) ); 595 596 /* 597 * We calculate total_users using a standalone query, except 598 * when a list of specific user_ids is passed to the constructor. 599 * This clause covers the latter situation, and ensures that 600 * pagination works when querying by $user_ids. 601 */ 602 if ( empty( $this->total_users ) ) { 603 $this->total_users = count( $wp_user_query->results ); 604 } 605 606 // Reindex for easier matching. 607 $r = array(); 608 foreach ( $wp_user_query->results as $u ) { 609 $r[ $u->ID ] = $u; 610 } 611 612 // Match up to the user ids from the main query. 613 foreach ( $this->user_ids as $key => $uid ) { 614 if ( isset( $r[ $uid ] ) ) { 615 $r[ $uid ]->ID = (int) $uid; 616 $r[ $uid ]->user_status = (int) $r[ $uid ]->user_status; 617 618 $this->results[ $uid ] = $r[ $uid ]; 619 620 // The BP template functions expect an 'id' 621 // (as opposed to 'ID') property. 622 $this->results[ $uid ]->id = (int) $uid; 623 624 // Remove user ID from original user_ids property. 625 } else { 626 unset( $this->user_ids[ $key ] ); 627 } 628 } 629 } 630 631 /** 632 * Fetch the IDs of users to put in the IN clause of the main query. 633 * 634 * By default, returns the value passed to it 635 * ($this->query_vars['include']). Having this abstracted into a 636 * standalone method means that extending classes can override the 637 * logic, parsing together their own user_id limits with the 'include' 638 * ids passed to the class constructor. See {@link BP_Group_Member_Query} 639 * for an example. 640 * 641 * @since 1.8.0 642 * 643 * @param array $include Sanitized array of user IDs, as passed to the 'include' 644 * parameter of the class constructor. 645 * @return array The list of users to which the main query should be 646 * limited. 647 */ 648 public function get_include_ids( $include = array() ) { 649 return $include; 650 } 651 652 /** 653 * Perform a database query to populate any extra metadata we might need. 654 * 655 * Different components will hook into the 'bp_user_query_populate_extras' 656 * action to loop in the things they want. 657 * 658 * @since 1.7.0 659 * 660 * @global WPDB $wpdb Global WordPress database access object. 661 */ 662 public function populate_extras() { 663 global $wpdb; 664 665 // Bail if no users. 666 if ( empty( $this->user_ids ) || empty( $this->results ) ) { 667 return; 668 } 669 670 // Bail if the populate_extras flag is set to false 671 // In the case of the 'popular' sort type, we force 672 // populate_extras to true, because we need the friend counts. 673 if ( 'popular' == $this->query_vars['type'] ) { 674 $this->query_vars['populate_extras'] = 1; 675 } 676 677 if ( ! (bool) $this->query_vars['populate_extras'] ) { 678 return; 679 } 680 681 // Turn user ID's into a query-usable, comma separated value. 682 $user_ids_sql = implode( ',', wp_parse_id_list( $this->user_ids ) ); 683 684 /** 685 * Allows users to independently populate custom extras. 686 * 687 * Note that anything you add here should query using $user_ids_sql, to 688 * avoid running multiple queries per user in the loop. 689 * 690 * Two BuddyPress components currently do this: 691 * - XProfile: To override display names. 692 * - Friends: To set whether or not a user is the current users friend. 693 * 694 * @see bp_xprofile_filter_user_query_populate_extras() 695 * @see bp_friends_filter_user_query_populate_extras() 696 * 697 * @since 1.7.0 698 * 699 * @param BP_User_Query $this Current BP_User_Query instance. 700 * @param string $user_ids_sql Comma-separated string of user IDs. 701 */ 702 do_action_ref_array( 'bp_user_query_populate_extras', array( $this, $user_ids_sql ) ); 703 704 // Fetch last_active data from the activity table. 705 $last_activities = BP_Core_User::get_last_activity( $this->user_ids ); 706 707 // Set a last_activity value for each user, even if it's empty. 708 foreach ( $this->results as $user_id => $user ) { 709 $user_last_activity = isset( $last_activities[ $user_id ]['date_recorded'] ) ? $last_activities[ $user_id ]['date_recorded'] : ''; 710 $this->results[ $user_id ]->last_activity = $user_last_activity; 711 } 712 713 // Fetch usermeta data 714 // We want the three following pieces of info from usermeta: 715 // - friend count 716 // - latest update. 717 $total_friend_count_key = bp_get_user_meta_key( 'total_friend_count' ); 718 $bp_latest_update_key = bp_get_user_meta_key( 'bp_latest_update' ); 719 720 // Total_friend_count must be set for each user, even if its 721 // value is 0. 722 foreach ( $this->results as $uindex => $user ) { 723 $this->results[$uindex]->total_friend_count = 0; 724 } 725 726 // Create, prepare, and run the separate usermeta query. 727 $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 ) ); 728 729 // The $members_template global expects the index key to be different 730 // from the meta_key in some cases, so we rejig things here. 731 foreach ( $user_metas as $user_meta ) { 732 switch ( $user_meta->meta_key ) { 733 case $total_friend_count_key : 734 $key = 'total_friend_count'; 735 break; 736 737 case $bp_latest_update_key : 738 $key = 'latest_update'; 739 break; 740 } 741 742 if ( isset( $this->results[ $user_meta->user_id ] ) ) { 743 $this->results[ $user_meta->user_id ]->{$key} = $user_meta->meta_value; 744 } 745 } 746 747 // When meta_key or meta_value have been passed to the query, 748 // fetch the resulting values for use in the template functions. 749 if ( ! empty( $this->query_vars['meta_key'] ) ) { 750 $meta_sql = array( 751 'select' => "SELECT user_id, meta_key, meta_value", 752 'from' => "FROM $wpdb->usermeta", 753 'where' => $wpdb->prepare( "WHERE meta_key = %s", $this->query_vars['meta_key'] ) 754 ); 755 756 if ( false !== $this->query_vars['meta_value'] ) { 757 $meta_sql['where'] .= $wpdb->prepare( " AND meta_value = %s", $this->query_vars['meta_value'] ); 758 } 759 760 $metas = $wpdb->get_results( "{$meta_sql['select']} {$meta_sql['from']} {$meta_sql['where']}" ); 761 762 if ( ! empty( $metas ) ) { 763 foreach ( $metas as $meta ) { 764 if ( isset( $this->results[ $meta->user_id ] ) ) { 765 $this->results[ $meta->user_id ]->meta_key = $meta->meta_key; 766 767 if ( ! empty( $meta->meta_value ) ) { 768 $this->results[ $meta->user_id ]->meta_value = $meta->meta_value; 769 } 770 } 771 } 772 } 773 } 774 } 775 776 /** 777 * Get a SQL clause representing member_type include/exclusion. 778 * 779 * @since 2.4.0 780 * 781 * @param string|array $member_types Array or comma-separated list of member types. 782 * @param string $operator 'IN' or 'NOT IN'. 783 * @return string 784 */ 785 protected function get_sql_clause_for_member_types( $member_types, $operator ) { 786 global $wpdb; 787 788 // Sanitize. 789 if ( 'NOT IN' !== $operator ) { 790 $operator = 'IN'; 791 } 792 793 // Parse and sanitize types. 794 if ( ! is_array( $member_types ) ) { 795 $member_types = preg_split( '/[,\s+]/', $member_types ); 796 } 797 798 $types = array(); 799 foreach ( $member_types as $mt ) { 800 if ( bp_get_member_type_object( $mt ) ) { 801 $types[] = $mt; 802 } 803 } 804 805 $tax_query = new WP_Tax_Query( array( 806 array( 807 'taxonomy' => bp_get_member_type_tax_name(), 808 'field' => 'name', 809 'operator' => $operator, 810 'terms' => $types, 811 ), 812 ) ); 813 814 // Switch to the root blog, where member type taxonomies live. 815 $site_id = bp_get_taxonomy_term_site_id( bp_get_member_type_tax_name() ); 816 $switched = false; 817 if ( $site_id !== get_current_blog_id() ) { 818 switch_to_blog( $site_id ); 819 $switched = true; 820 } 821 822 $sql_clauses = $tax_query->get_sql( 'u', $this->uid_name ); 823 824 $clause = ''; 825 826 // The no_results clauses are the same between IN and NOT IN. 827 if ( false !== strpos( $sql_clauses['where'], '0 = 1' ) ) { 828 $clause = $this->no_results['where']; 829 830 // The tax_query clause generated for NOT IN can be used almost as-is. We just trim the leading 'AND'. 831 } elseif ( 'NOT IN' === $operator ) { 832 $clause = preg_replace( '/^\s*AND\s*/', '', $sql_clauses['where'] ); 833 834 // IN clauses must be converted to a subquery. 835 } elseif ( preg_match( '/' . $wpdb->term_relationships . '\.term_taxonomy_id IN \([0-9, ]+\)/', $sql_clauses['where'], $matches ) ) { 836 $clause = "u.{$this->uid_name} IN ( SELECT object_id FROM $wpdb->term_relationships WHERE {$matches[0]} )"; 837 } 838 839 if ( $switched ) { 840 restore_current_blog(); 841 } 842 843 return $clause; 844 } 845 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
Generated: Thu Nov 21 01:00:57 2024 | Cross-referenced by PHPXref 0.7.1 |