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