Ticket #5839: 5839.4.patch
| File 5839.4.patch, 38.5 KB (added by , 11 years ago) |
|---|
-
src/bp-core/bp-core-adminbar.php
diff --git src/bp-core/bp-core-adminbar.php src/bp-core/bp-core-adminbar.php index a875c49..eade1b6 100644
function bp_core_enqueue_admin_bar_css() { 127 127 128 128 // Enqueue the additional adminbar css 129 129 wp_enqueue_style( 'bp-admin-bar' ); 130 } 131 No newline at end of file 130 } -
src/bp-core/bp-core-classes.php
diff --git src/bp-core/bp-core-classes.php src/bp-core/bp-core-classes.php index 1244cdf..c99c031 100644
if ( !defined( 'ABSPATH' ) ) exit; 48 48 * @type string|bool $meta_value When used with $meta_key, limits results 49 49 * to users whose usermeta value associated with $meta_key matches 50 50 * $meta_value. Default: false. 51 * @type array $xprofile_query Filter results by xprofile data. Requires 52 * the xprofile component. See {@link BP_XProfile_Query} for details. 51 53 * @type bool $populate_extras True if you want to fetch extra metadata 52 54 * about returned users, such as total group and friend counts. 53 55 * @type string $count_total Determines how BP_User_Query will do a count … … class BP_User_Query { 115 117 public $uid_clauses = array(); 116 118 117 119 /** 120 * SQL table where the user ID is being fetched from. 121 * 122 * @since BuddyPress (2.1.0) 123 * @access public 124 * @var string 125 */ 126 public $uid_table = ''; 127 128 /** 118 129 * SQL database column name to order by. 119 130 * 120 131 * @since BuddyPress (1.7.0) … … class BP_User_Query { 162 173 'user_ids' => false, 163 174 'meta_key' => false, 164 175 'meta_value' => false, 176 'xprofile_query' => false, 165 177 'populate_extras' => true, 166 178 'count_total' => 'count_query' 167 179 ) ); … … class BP_User_Query { 249 261 // number of minutes used as an interval 250 262 case 'online' : 251 263 $this->uid_name = 'user_id'; 252 $sql['select'] = "SELECT u.{$this->uid_name} as id FROM {$bp->members->table_name_last_activity} u"; 264 $this->uid_table = $bp->members->table_name_last_activity; 265 $sql['select'] = "SELECT u.{$this->uid_name} as id FROM {$this->uid_table} u"; 253 266 $sql['where'][] = $wpdb->prepare( "u.component = %s AND u.type = 'last_activity'", buddypress()->members->id ); 254 267 $sql['where'][] = $wpdb->prepare( "u.date_recorded >= DATE_SUB( UTC_TIMESTAMP(), INTERVAL %d MINUTE )", apply_filters( 'bp_user_query_online_interval', 15 ) ); 255 268 $sql['orderby'] = "ORDER BY u.date_recorded"; … … class BP_User_Query { 263 276 case 'newest' : 264 277 case 'random' : 265 278 $this->uid_name = 'user_id'; 266 $sql['select'] = "SELECT u.{$this->uid_name} as id FROM {$bp->members->table_name_last_activity} u"; 279 $this->uid_table = $bp->members->table_name_last_activity; 280 $sql['select'] = "SELECT u.{$this->uid_name} as id FROM {$this->uid_table} u"; 267 281 $sql['where'][] = $wpdb->prepare( "u.component = %s AND u.type = 'last_activity'", buddypress()->members->id ); 268 282 269 283 if ( 'newest' == $type ) { … … class BP_User_Query { 281 295 // 'popular' sorts by the 'total_friend_count' usermeta 282 296 case 'popular' : 283 297 $this->uid_name = 'user_id'; 284 $sql['select'] = "SELECT u.{$this->uid_name} as id FROM {$wpdb->usermeta} u"; 298 $this->uid_table = $wpdb->usermeta; 299 $sql['select'] = "SELECT u.{$this->uid_name} as id FROM {$this->uid_table} u"; 285 300 $sql['where'][] = $wpdb->prepare( "u.meta_key = %s", bp_get_user_meta_key( 'total_friend_count' ) ); 286 301 $sql['orderby'] = "ORDER BY CONVERT(u.meta_value, SIGNED)"; 287 302 $sql['order'] = "DESC"; … … class BP_User_Query { 298 313 // @todo remove need for bp_is_active() check 299 314 if ( ! bp_disable_profile_sync() || ! bp_is_active( 'xprofile' ) ) { 300 315 $this->uid_name = 'ID'; 301 $sql['select'] = "SELECT u.{$this->uid_name} as id FROM {$wpdb->users} u"; 316 $this->uid_table = $wpdb->users; 317 $sql['select'] = "SELECT u.{$this->uid_name} as id FROM {$this->uid_table} u"; 302 318 $sql['orderby'] = "ORDER BY u.display_name"; 303 319 $sql['order'] = "ASC"; 304 320 … … class BP_User_Query { 306 322 // the xprofile table 307 323 } else { 308 324 $this->uid_name = 'user_id'; 309 $sql['select'] = "SELECT u.{$this->uid_name} as id FROM {$bp->profile->table_name_data} u"; 325 $this->uid_table = $bp->profile->table_name_data; 326 $sql['select'] = "SELECT u.{$this->uid_name} as id FROM {$this->uid_table} u"; 310 327 $sql['where'][] = $wpdb->prepare( "u.field_id = %d", bp_xprofile_fullname_field_id() ); 311 328 $sql['orderby'] = "ORDER BY u.value"; 312 329 $sql['order'] = "ASC"; … … class BP_User_Query { 322 339 // Any other 'type' falls through 323 340 default : 324 341 $this->uid_name = 'ID'; 325 $sql['select'] = "SELECT u.{$this->uid_name} as id FROM {$wpdb->users} u"; 342 $this->uid_table = $wpdb->users; 343 $sql['select'] = "SELECT u.{$this->uid_name} as id FROM {$this->uid_table} u"; 326 344 327 345 // In this case, we assume that a plugin is 328 346 // handling order, so we leave those clauses -
src/bp-xprofile/bp-xprofile-classes.php
diff --git src/bp-xprofile/bp-xprofile-classes.php src/bp-xprofile/bp-xprofile-classes.php index cbb1b8b4..ed08b9b 100644
abstract class BP_XProfile_Field_Type { 3262 3262 return $html; 3263 3263 } 3264 3264 } 3265 3266 /** 3267 * Class for generating SQL clauses to filter a user query by xprofile data. 3268 * 3269 * @since BuddyPress (2.2.0) 3270 */ 3271 class BP_XProfile_Query { 3272 /** 3273 * Array of xprofile queries. 3274 * 3275 * See {@see WP_XProfile_Query::__construct()} for information on parameters. 3276 * 3277 * @since BuddyPress (2.2.0) 3278 * @access public 3279 * @var array 3280 */ 3281 public $queries = array(); 3282 3283 /** 3284 * Database table that where the metadata's objects are stored (eg $wpdb->users). 3285 * 3286 * @since BuddyPress (2.2.0) 3287 * @access public 3288 * @var string 3289 */ 3290 public $primary_table; 3291 3292 /** 3293 * Column in primary_table that represents the ID of the object. 3294 * 3295 * @since BuddyPress (2.2.0) 3296 * @access public 3297 * @var string 3298 */ 3299 public $primary_id_column; 3300 3301 /** 3302 * A flat list of table aliases used in JOIN clauses. 3303 * 3304 * @since BuddyPress (2.2.0) 3305 * @access protected 3306 * @var array 3307 */ 3308 protected $table_aliases = array(); 3309 public function __construct( $xprofile_query ) { 3310 if ( empty( $xprofile_query ) ) { 3311 return; 3312 } 3313 3314 $this->queries = $this->sanitize_query( $xprofile_query ); 3315 } 3316 3317 /** 3318 * Ensure the `xprofile_query` argument passed to the class constructor is well-formed. 3319 * 3320 * Eliminates empty items and ensures that a 'relation' is set. 3321 * 3322 * @since BuddyPress (2.2.0) 3323 * @access public 3324 * 3325 * @param array $queries Array of query clauses. 3326 * @return array Sanitized array of query clauses. 3327 */ 3328 public function sanitize_query( $queries ) { 3329 $clean_queries = array(); 3330 3331 if ( ! is_array( $queries ) ) { 3332 return $clean_queries; 3333 } 3334 3335 foreach ( $queries as $key => $query ) { 3336 if ( 'relation' === $key ) { 3337 $relation = $query; 3338 3339 } else if ( ! is_array( $query ) ) { 3340 continue; 3341 3342 // First-order clause. 3343 } else if ( $this->is_first_order_clause( $query ) ) { 3344 if ( isset( $query['value'] ) && array() === $query['value'] ) { 3345 unset( $query['value'] ); 3346 } 3347 3348 $clean_queries[] = $query; 3349 3350 // Otherwise, it's a nested query, so we recurse. 3351 } else { 3352 $cleaned_query = $this->sanitize_query( $query ); 3353 3354 if ( ! empty( $cleaned_query ) ) { 3355 $clean_queries[] = $cleaned_query; 3356 } 3357 } 3358 } 3359 3360 if ( empty( $clean_queries ) ) { 3361 return $clean_queries; 3362 } 3363 3364 // Sanitize the 'relation' key provided in the query. 3365 if ( isset( $relation ) && 'OR' === strtoupper( $relation ) ) { 3366 $clean_queries['relation'] = 'OR'; 3367 3368 /* 3369 * If there is only a single clause, call the relation 'OR'. 3370 * This value will not actually be used to join clauses, but it 3371 * simplifies the logic around combining key-only queries. 3372 */ 3373 } else if ( 1 === count( $clean_queries ) ) { 3374 $clean_queries['relation'] = 'OR'; 3375 3376 // Default to AND. 3377 } else { 3378 $clean_queries['relation'] = 'AND'; 3379 } 3380 3381 return $clean_queries; 3382 } 3383 3384 /** 3385 * Determine whether a query clause is first-order. 3386 * 3387 * A first-order meta query clause is one that has either a 'key' or 3388 * a 'value' array key. 3389 * 3390 * @since BuddyPress (2.2.0) 3391 * @access protected 3392 * 3393 * @param array $query Meta query arguments. 3394 * @return bool Whether the query clause is a first-order clause. 3395 */ 3396 protected function is_first_order_clause( $query ) { 3397 return isset( $query['field'] ) || isset( $query['value'] ); 3398 } 3399 3400 /** 3401 * Return the appropriate alias for the given meta type if applicable. 3402 * 3403 * @since BuddyPress (2.2.0) 3404 * @access public 3405 * 3406 * @param string $type MySQL type to cast meta_value. 3407 * @return string MySQL type. 3408 */ 3409 public function get_cast_for_type( $type = '' ) { 3410 if ( empty( $type ) ) 3411 return 'CHAR'; 3412 3413 $meta_type = strtoupper( $type ); 3414 3415 if ( ! preg_match( '/^(?:BINARY|CHAR|DATE|DATETIME|SIGNED|UNSIGNED|TIME|NUMERIC(?:\(\d+(?:,\s?\d+)?\))?|DECIMAL(?:\(\d+(?:,\s?\d+)?\))?)$/', $meta_type ) ) 3416 return 'CHAR'; 3417 3418 if ( 'NUMERIC' == $meta_type ) 3419 $meta_type = 'SIGNED'; 3420 3421 return $meta_type; 3422 } 3423 3424 /** 3425 * Generate SQL clauses to be appended to a main query. 3426 * 3427 * Called by the public {@see WP_Meta_Query::get_sql()}, this method 3428 * is abstracted out to maintain parity with the other Query classes. 3429 * 3430 * @since BuddyPress (2.2.0) 3431 * @access protected 3432 * 3433 * @return array { 3434 * Array containing JOIN and WHERE SQL clauses to append to the main query. 3435 * 3436 * @type string $join SQL fragment to append to the main JOIN clause. 3437 * @type string $where SQL fragment to append to the main WHERE clause. 3438 * } 3439 */ 3440 protected function get_sql_clauses() { 3441 /* 3442 * $queries are passed by reference to get_sql_for_query() for recursion. 3443 * To keep $this->queries unaltered, pass a copy. 3444 */ 3445 $queries = $this->queries; 3446 $sql = $this->get_sql_for_query( $queries ); 3447 3448 if ( ! empty( $sql['where'] ) ) { 3449 $sql['where'] = ' AND ' . $sql['where']; 3450 } 3451 3452 return $sql; 3453 } 3454 3455 /** 3456 * Generate SQL clauses for a single query array. 3457 * 3458 * If nested subqueries are found, this method recurses the tree to 3459 * produce the properly nested SQL. 3460 * 3461 * @since BuddyPress (2.2.0) 3462 * @access protected 3463 * 3464 * @param array $query Query to parse. 3465 * @param int $depth Optional. Number of tree levels deep we currently are. 3466 * Used to calculate indentation. 3467 * @return array { 3468 * Array containing JOIN and WHERE SQL clauses to append to a single query array. 3469 * 3470 * @type string $join SQL fragment to append to the main JOIN clause. 3471 * @type string $where SQL fragment to append to the main WHERE clause. 3472 * } 3473 */ 3474 protected function get_sql_for_query( &$query, $depth = 0 ) { 3475 $sql_chunks = array( 3476 'join' => array(), 3477 'where' => array(), 3478 ); 3479 3480 $sql = array( 3481 'join' => '', 3482 'where' => '', 3483 ); 3484 3485 $indent = ''; 3486 for ( $i = 0; $i < $depth; $i++ ) { 3487 $indent .= " "; 3488 } 3489 3490 foreach ( $query as $key => &$clause ) { 3491 if ( 'relation' === $key ) { 3492 $relation = $query['relation']; 3493 } else if ( is_array( $clause ) ) { 3494 3495 // This is a first-order clause. 3496 if ( $this->is_first_order_clause( $clause ) ) { 3497 $clause_sql = $this->get_sql_for_clause( $clause, $query ); 3498 3499 $where_count = count( $clause_sql['where'] ); 3500 if ( ! $where_count ) { 3501 $sql_chunks['where'][] = ''; 3502 } else if ( 1 === $where_count ) { 3503 $sql_chunks['where'][] = $clause_sql['where'][0]; 3504 } else { 3505 $sql_chunks['where'][] = '( ' . implode( ' AND ', $clause_sql['where'] ) . ' )'; 3506 } 3507 3508 $sql_chunks['join'] = array_merge( $sql_chunks['join'], $clause_sql['join'] ); 3509 // This is a subquery, so we recurse. 3510 } else { 3511 $clause_sql = $this->get_sql_for_query( $clause, $depth + 1 ); 3512 3513 $sql_chunks['where'][] = $clause_sql['where']; 3514 $sql_chunks['join'][] = $clause_sql['join']; 3515 } 3516 } 3517 } 3518 3519 // Filter to remove empties. 3520 $sql_chunks['join'] = array_filter( $sql_chunks['join'] ); 3521 $sql_chunks['where'] = array_filter( $sql_chunks['where'] ); 3522 3523 if ( empty( $relation ) ) { 3524 $relation = 'AND'; 3525 } 3526 3527 // Filter duplicate JOIN clauses and combine into a single string. 3528 if ( ! empty( $sql_chunks['join'] ) ) { 3529 $sql['join'] = implode( ' ', array_unique( $sql_chunks['join'] ) ); 3530 } 3531 3532 // Generate a single WHERE clause with proper brackets and indentation. 3533 if ( ! empty( $sql_chunks['where'] ) ) { 3534 $sql['where'] = '( ' . "\n " . $indent . implode( ' ' . "\n " . $indent . $relation . ' ' . "\n " . $indent, $sql_chunks['where'] ) . "\n" . $indent . ')'; 3535 } 3536 3537 return $sql; 3538 } 3539 3540 /** 3541 * Generates SQL clauses to be appended to a main query. 3542 * 3543 * @since BuddyPress (2.2.0) 3544 * @access public 3545 * 3546 * @param string $primary_table Database table where the object being filtered is stored (eg wp_users). 3547 * @param string $primary_id_column ID column for the filtered object in $primary_table. 3548 * @return array { 3549 * Array containing JOIN and WHERE SQL clauses to append to the main query. 3550 * 3551 * @type string $join SQL fragment to append to the main JOIN clause. 3552 * @type string $where SQL fragment to append to the main WHERE clause. 3553 * } 3554 */ 3555 public function get_sql( $primary_table, $primary_id_column, $context = null ) { 3556 global $wpdb; 3557 3558 $this->primary_table = $primary_table; 3559 $this->primary_id_column = $primary_id_column; 3560 3561 $sql = $this->get_sql_clauses(); 3562 3563 /* 3564 * If any JOINs are LEFT JOINs (as in the case of NOT EXISTS), then all JOINs should 3565 * be LEFT. Otherwise posts with no metadata will be excluded from results. 3566 */ 3567 if ( false !== strpos( $sql['join'], 'LEFT JOIN' ) ) { 3568 $sql['join'] = str_replace( 'INNER JOIN', 'LEFT JOIN', $sql['join'] ); 3569 } 3570 3571 /** 3572 * Filter the meta query's generated SQL. 3573 * 3574 * @since 2.1.0 3575 * 3576 * @param array $args { 3577 * An array of meta query SQL arguments. 3578 * 3579 * @type array $clauses Array containing the query's JOIN and WHERE clauses. 3580 * @type array $queries Array of meta queries. 3581 * @type string $primary_table Primary table. 3582 * @type string $primary_id_column Primary column ID. 3583 * } 3584 */ 3585 return apply_filters_ref_array( 'get_meta_sql', array( $sql, $this->queries, $primary_table, $primary_id_column ) ); 3586 } 3587 3588 /** 3589 * Generate SQL JOIN and WHERE clauses for a first-order query clause. 3590 * 3591 * "First-order" means that it's an array with a 'key' or 'value'. 3592 * 3593 * @since BuddyPress (2.2.0) 3594 * @access public 3595 * 3596 * @param array $clause Query clause. 3597 * @param array $parent_query Parent query array. 3598 * @return array { 3599 * Array containing JOIN and WHERE SQL clauses to append to a first-order query. 3600 * 3601 * @type string $join SQL fragment to append to the main JOIN clause. 3602 * @type string $where SQL fragment to append to the main WHERE clause. 3603 * } 3604 */ 3605 public function get_sql_for_clause( &$clause, $parent_query ) { 3606 global $wpdb; 3607 3608 $sql_chunks = array( 3609 'where' => array(), 3610 'join' => array(), 3611 ); 3612 3613 if ( isset( $clause['compare'] ) ) { 3614 $clause['compare'] = strtoupper( $clause['compare'] ); 3615 } else { 3616 $clause['compare'] = isset( $clause['value'] ) && is_array( $clause['value'] ) ? 'IN' : '='; 3617 } 3618 3619 if ( ! in_array( $clause['compare'], array( 3620 '=', '!=', '>', '>=', '<', '<=', 3621 'LIKE', 'NOT LIKE', 3622 'IN', 'NOT IN', 3623 'BETWEEN', 'NOT BETWEEN', 3624 'EXISTS', 'NOT EXISTS', 3625 'REGEXP', 'NOT REGEXP', 'RLIKE' 3626 ) ) ) { 3627 $clause['compare'] = '='; 3628 } 3629 3630 $field_compare = $clause['compare']; 3631 3632 // First build the JOIN clause, if one is required. 3633 $join = ''; 3634 3635 $data_table = buddypress()->profile->table_name_data; 3636 3637 // We prefer to avoid joins if possible. Look for an existing join compatible with this clause. 3638 $alias = $this->find_compatible_table_alias( $clause, $parent_query ); 3639 if ( false === $alias ) { 3640 $i = count( $this->table_aliases ); 3641 $alias = $i ? 'xpq' . $i : $data_table; 3642 3643 // JOIN clauses for NOT EXISTS have their own syntax. 3644 if ( 'NOT EXISTS' === $field_compare ) { 3645 $join .= " LEFT JOIN $data_table"; 3646 $join .= $i ? " AS $alias" : ''; 3647 $join .= $wpdb->prepare( " ON ($this->primary_table.$this->primary_id_column = $alias.user_id AND $alias.field_id = %d )", $clause['field'] ); 3648 3649 // All other JOIN clauses. 3650 } else { 3651 $join .= " INNER JOIN $data_table"; 3652 $join .= $i ? " AS $alias" : ''; 3653 $join .= " ON ( $this->primary_table.$this->primary_id_column = $alias.user_id )"; 3654 } 3655 3656 $this->table_aliases[] = $alias; 3657 $sql_chunks['join'][] = $join; 3658 } 3659 3660 // Save the alias to this clause, for future siblings to find. 3661 $clause['alias'] = $alias; 3662 3663 // Next, build the WHERE clause. 3664 $where = ''; 3665 3666 // field_id. 3667 if ( array_key_exists( 'field', $clause ) ) { 3668 // Convert field name to ID if necessary. 3669 if ( ! is_numeric( $clause['field'] ) ) { 3670 $clause['field'] = xprofile_get_field_id_from_name( $clause['field'] ); 3671 } 3672 3673 // NOT EXISTS has its own syntax. 3674 if ( 'NOT EXISTS' === $field_compare ) { 3675 $sql_chunks['where'][] = $alias . '.user_id IS NULL'; 3676 } else { 3677 $sql_chunks['where'][] = $wpdb->prepare( "$alias.field_id = %d", $clause['field'] ); 3678 } 3679 } 3680 3681 // value. 3682 if ( array_key_exists( 'value', $clause ) ) { 3683 $field_value = $clause['value']; 3684 $field_type = $this->get_cast_for_type( isset( $clause['type'] ) ? $clause['type'] : '' ); 3685 3686 if ( in_array( $field_compare, array( 'IN', 'NOT IN', 'BETWEEN', 'NOT BETWEEN' ) ) ) { 3687 if ( ! is_array( $field_value ) ) { 3688 $field_value = preg_split( '/[,\s]+/', $field_value ); 3689 } 3690 } else { 3691 $field_value = trim( $field_value ); 3692 } 3693 3694 switch ( $field_compare ) { 3695 case 'IN' : 3696 case 'NOT IN' : 3697 $field_compare_string = '(' . substr( str_repeat( ',%s', count( $field_value ) ), 1 ) . ')'; 3698 $where = $wpdb->prepare( $field_compare_string, $field_value ); 3699 break; 3700 3701 case 'BETWEEN' : 3702 case 'NOT BETWEEN' : 3703 $field_value = array_slice( $field_value, 0, 2 ); 3704 $where = $wpdb->prepare( '%s AND %s', $field_value ); 3705 break; 3706 3707 case 'LIKE' : 3708 case 'NOT LIKE' : 3709 $field_value = '%' . $wpdb->esc_like( $field_value ) . '%'; 3710 $where = $wpdb->prepare( '%s', $field_value ); 3711 break; 3712 3713 default : 3714 $where = $wpdb->prepare( '%s', $field_value ); 3715 break; 3716 3717 } 3718 3719 if ( $where ) { 3720 $sql_chunks['where'][] = "CAST($alias.value AS {$field_type}) {$field_compare} {$where}"; 3721 } 3722 } 3723 3724 /* 3725 * Multiple WHERE clauses (for meta_key and meta_value) should 3726 * be joined in parentheses. 3727 */ 3728 if ( 1 < count( $sql_chunks['where'] ) ) { 3729 $sql_chunks['where'] = array( '( ' . implode( ' AND ', $sql_chunks['where'] ) . ' )' ); 3730 } 3731 3732 return $sql_chunks; 3733 } 3734 3735 /** 3736 * Identify an existing table alias that is compatible with the current query clause. 3737 * 3738 * We avoid unnecessary table joins by allowing each clause to look for 3739 * an existing table alias that is compatible with the query that it 3740 * needs to perform. An existing alias is compatible if (a) it is a 3741 * sibling of $clause (ie, it's under the scope of the same relation), 3742 * and (b) the combination of operator and relation between the clauses 3743 * allows for a shared table join. In the case of WP_Meta_Query, this 3744 * only applies to IN clauses that are connected by the relation OR. 3745 * 3746 * @since BuddyPress (2.2.0) 3747 * @access protected 3748 * 3749 * @param array $clause Query clause. 3750 * @param array $parent_query Parent query of $clause. 3751 * @return string|bool Table alias if found, otherwise false. 3752 */ 3753 protected function find_compatible_table_alias( $clause, $parent_query ) { 3754 $alias = false; 3755 3756 foreach ( $parent_query as $sibling ) { 3757 // If the sibling has no alias yet, there's nothing to check. 3758 if ( empty( $sibling['alias'] ) ) { 3759 continue; 3760 } 3761 3762 // We're only interested in siblings that are first-order clauses. 3763 if ( ! is_array( $sibling ) || ! $this->is_first_order_clause( $sibling ) ) { 3764 continue; 3765 } 3766 3767 $compatible_compares = array(); 3768 3769 // Clauses connected by OR can share joins as long as they have "positive" operators. 3770 if ( 'OR' === $parent_query['relation'] ) { 3771 $compatible_compares = array( '=', 'IN', 'BETWEEN', 'LIKE', 'REGEXP', 'RLIKE', '>', '>=', '<', '<=' ); 3772 3773 // Clauses joined by AND with "negative" operators share a join only if they also share a key. 3774 } else if ( isset( $sibling['field_id'] ) && isset( $clause['field_id'] ) && $sibling['field_id'] === $clause['field_id'] ) { 3775 $compatible_compares = array( '!=', 'NOT IN', 'NOT LIKE' ); 3776 } 3777 3778 $clause_compare = strtoupper( $clause['compare'] ); 3779 $sibling_compare = strtoupper( $sibling['compare'] ); 3780 if ( in_array( $clause_compare, $compatible_compares ) && in_array( $sibling_compare, $compatible_compares ) ) { 3781 $alias = $sibling['alias']; 3782 break; 3783 } 3784 } 3785 3786 return $alias; 3787 } 3788 } -
src/bp-xprofile/bp-xprofile-filters.php
diff --git src/bp-xprofile/bp-xprofile-filters.php src/bp-xprofile/bp-xprofile-filters.php index 21eaa3d..411a8d4 100644
function xprofile_filter_pre_validate_value_by_field_type( $value, $field, $fiel 200 200 /** 201 201 * Filter an Extended Profile field value, and attempt to make clickable links 202 202 * to members search results out of them. 203 * 203 * 204 204 * - Not run on datebox field types 205 205 * - Not run on values without commas with less than 5 words 206 206 * - URL's are made clickable … … function bp_xprofile_filter_user_query_populate_extras( BP_User_Query $user_quer 323 323 add_filter( 'bp_user_query_populate_extras', 'bp_xprofile_filter_user_query_populate_extras', 2, 2 ); 324 324 325 325 /** 326 * Parse 'xprofile_query' argument passed to BP_User_Query. 327 * 328 * @since BuddyPress (2.2.0) 329 * 330 * @param BP_User_Query User query object. 331 */ 332 function bp_xprofile_add_xprofile_query_to_user_query( BP_User_Query $q ) { 333 global $wpdb; 334 335 if ( empty( $q->query_vars['xprofile_query'] ) ) { 336 return; 337 } 338 339 $xprofile_query = new BP_XProfile_Query( $q->query_vars['xprofile_query'] ); 340 $sql = $xprofile_query->get_sql( 'u', $q->uid_name ); 341 342 if ( ! empty( $sql['join'] ) ) { 343 $q->uid_clauses['select'] .= $sql['join']; 344 $q->uid_clauses['where'] .= $sql['where']; 345 } 346 } 347 add_action( 'bp_pre_user_query', 'bp_xprofile_add_xprofile_query_to_user_query' ); 348 349 /** 326 350 * Filter meta queries to modify for the xprofile data schema. 327 351 * 328 352 * @since BuddyPress (2.0.0) -
new file tests/phpunit/testcases/xprofile/class-bp-xprofile-query.php
diff --git tests/phpunit/testcases/xprofile/class-bp-xprofile-query.php tests/phpunit/testcases/xprofile/class-bp-xprofile-query.php new file mode 100644 index 0000000..2f3f751
- + 1 <?php 2 3 /** 4 * @group xprofile 5 * @group BP_XProfile_Query 6 */ 7 class BP_Tests_BP_XProfile_Query extends BP_UnitTestCase { 8 protected $group; 9 protected $fields = array(); 10 protected $users = array(); 11 12 public function tearDown() { 13 parent::tearDown(); 14 $this->group = ''; 15 $this->fields = array(); 16 } 17 18 public function test_no_field() { 19 $this->create_fields( 2 ); 20 $this->create_users( 3 ); 21 22 xprofile_set_field_data( $this->fields[0], $this->users[0], 'foo' ); 23 xprofile_set_field_data( $this->fields[0], $this->users[1], 'bar' ); 24 xprofile_set_field_data( $this->fields[1], $this->users[2], 'foo'); 25 26 $q = new BP_User_Query( array( 27 'xprofile_query' => array( 28 array( 29 'value' => 'foo', 30 ), 31 ), 32 ) ); 33 34 $expected = array( $this->users[0], $this->users[2] ); 35 $this->assertEqualSets( $expected, array_keys( $q->results ) ); 36 } 37 38 public function test_no_value() { 39 $this->create_fields( 2 ); 40 $this->create_users( 3 ); 41 42 xprofile_set_field_data( $this->fields[0], $this->users[0], 'foo' ); 43 xprofile_set_field_data( $this->fields[0], $this->users[1], 'bar' ); 44 xprofile_set_field_data( $this->fields[1], $this->users[2], 'foo'); 45 46 $q = new BP_User_Query( array( 47 'xprofile_query' => array( 48 array( 49 'field' => $this->fields[1], 50 ), 51 ), 52 ) ); 53 54 $expected = array( $this->users[2] ); 55 $this->assertEqualSets( $expected, array_keys( $q->results ) ); 56 } 57 58 public function test_translate_field_name_to_field_id() { 59 $this->create_fields( 0 ); 60 $f = $this->factory->xprofile_field->create( array( 61 'field_group_id' => $this->group, 62 'type' => 'textbox', 63 'name' => 'Foo Field', 64 ) ); 65 $this->create_users( 2 ); 66 67 xprofile_set_field_data( $f, $this->users[0], 'foo' ); 68 69 $q = new BP_User_Query( array( 70 'xprofile_query' => array( 71 array( 72 'field' => 'Foo Field', 73 ), 74 ), 75 ) ); 76 77 $expected = array( $this->users[0] ); 78 $this->assertEqualSets( $expected, array_keys( $q->results ) ); 79 } 80 81 public function test_single_clause_compare_default() { 82 $this->create_fields( 2 ); 83 $this->create_users( 3 ); 84 85 xprofile_set_field_data( $this->fields[0], $this->users[0], 'foo' ); 86 xprofile_set_field_data( $this->fields[0], $this->users[1], 'bar' ); 87 xprofile_set_field_data( $this->fields[1], $this->users[2], 'foo'); 88 89 $q = new BP_User_Query( array( 90 'xprofile_query' => array( 91 array( 92 'field' => $this->fields[0], 93 'value' => 'foo', 94 ), 95 ), 96 ) ); 97 98 $expected = array( $this->users[0] ); 99 $this->assertEquals( $expected, array_keys( $q->results ) ); 100 } 101 102 public function test_single_clause_compare_equals() { 103 $this->create_fields( 2 ); 104 $this->create_users( 3 ); 105 106 xprofile_set_field_data( $this->fields[0], $this->users[0], 'foo' ); 107 xprofile_set_field_data( $this->fields[0], $this->users[1], 'bar' ); 108 xprofile_set_field_data( $this->fields[1], $this->users[2], 'foo'); 109 110 $q = new BP_User_Query( array( 111 'xprofile_query' => array( 112 array( 113 'field' => $this->fields[0], 114 'value' => 'foo', 115 'compare' => '=', 116 ), 117 ), 118 ) ); 119 120 $expected = array( $this->users[0] ); 121 $this->assertEquals( $expected, array_keys( $q->results ) ); 122 } 123 124 public function test_single_clause_compare_not_equals() { 125 $this->create_fields( 1 ); 126 $this->create_users( 2 ); 127 128 xprofile_set_field_data( $this->fields[0], $this->users[0], 'foo' ); 129 xprofile_set_field_data( $this->fields[0], $this->users[1], 'bar' ); 130 131 $q = new BP_User_Query( array( 132 'xprofile_query' => array( 133 array( 134 'field' => $this->fields[0], 135 'value' => 'foo', 136 'compare' => '!=', 137 ), 138 ), 139 ) ); 140 141 $expected = array( $this->users[1] ); 142 $this->assertEquals( $expected, array_keys( $q->results ) ); 143 } 144 145 public function test_single_clause_compare_arithmetic_comparisons() { 146 $this->create_fields( 1 ); 147 $this->create_users( 3 ); 148 149 xprofile_set_field_data( $this->fields[0], $this->users[0], '1' ); 150 xprofile_set_field_data( $this->fields[0], $this->users[1], '2' ); 151 xprofile_set_field_data( $this->fields[0], $this->users[2], '3' ); 152 153 // < 154 $q = new BP_User_Query( array( 155 'xprofile_query' => array( 156 array( 157 'field' => $this->fields[0], 158 'value' => 2, 159 'compare' => '<', 160 ), 161 ), 162 ) ); 163 164 $expected = array( $this->users[0] ); 165 $this->assertEqualSets( $expected, array_keys( $q->results ) ); 166 167 // <= 168 $q = new BP_User_Query( array( 169 'xprofile_query' => array( 170 array( 171 'field' => $this->fields[0], 172 'value' => 2, 173 'compare' => '<=', 174 ), 175 ), 176 ) ); 177 178 $expected = array( $this->users[0], $this->users[1] ); 179 $this->assertEqualSets( $expected, array_keys( $q->results ) ); 180 181 // >= 182 $q = new BP_User_Query( array( 183 'xprofile_query' => array( 184 array( 185 'field' => $this->fields[0], 186 'value' => 2, 187 'compare' => '>=', 188 ), 189 ), 190 ) ); 191 192 $expected = array( $this->users[1], $this->users[2] ); 193 $this->assertEqualSets( $expected, array_keys( $q->results ) ); 194 195 // > 196 $q = new BP_User_Query( array( 197 'xprofile_query' => array( 198 array( 199 'field' => $this->fields[0], 200 'value' => 2, 201 'compare' => '>', 202 ), 203 ), 204 ) ); 205 206 $expected = array( $this->users[2] ); 207 $this->assertEqualSets( $expected, array_keys( $q->results ) ); 208 } 209 210 public function test_single_clause_compare_like() { 211 $this->create_fields( 1 ); 212 $this->create_users( 2 ); 213 214 xprofile_set_field_data( $this->fields[0], $this->users[0], 'bar' ); 215 216 $q = new BP_User_Query( array( 217 'xprofile_query' => array( 218 array( 219 'field' => $this->fields[0], 220 'value' => 'ba', 221 'compare' => 'LIKE', 222 ), 223 ), 224 ) ); 225 226 $expected = array( $this->users[0] ); 227 $this->assertEqualSets( $expected, array_keys( $q->results ) ); 228 } 229 230 public function test_single_clause_compare_not_like() { 231 $this->create_fields( 1 ); 232 $this->create_users( 3 ); 233 234 xprofile_set_field_data( $this->fields[0], $this->users[0], 'bar' ); 235 xprofile_set_field_data( $this->fields[0], $this->users[1], 'rab' ); 236 237 $q = new BP_User_Query( array( 238 'xprofile_query' => array( 239 array( 240 'field' => $this->fields[0], 241 'value' => 'ba', 242 'compare' => 'NOT LIKE', 243 ), 244 ), 245 ) ); 246 247 $expected = array( $this->users[1] ); 248 $this->assertEqualSets( $expected, array_keys( $q->results ) ); 249 } 250 251 public function test_single_clause_compare_between_not_between() { 252 $this->create_fields( 1 ); 253 $this->create_users( 3 ); 254 255 xprofile_set_field_data( $this->fields[0], $this->users[0], '1' ); 256 xprofile_set_field_data( $this->fields[0], $this->users[1], '10' ); 257 xprofile_set_field_data( $this->fields[0], $this->users[2], '100' ); 258 259 $q = new BP_User_Query( array( 260 'xprofile_query' => array( 261 array( 262 'field' => $this->fields[0], 263 'value' => array( 9, 12 ), 264 'compare' => 'BETWEEN', 265 'type' => 'NUMERIC', 266 ), 267 ), 268 ) ); 269 270 $expected = array( $this->users[1] ); 271 $this->assertEqualSets( $expected, array_keys( $q->results ) ); 272 273 $q = new BP_User_Query( array( 274 'xprofile_query' => array( 275 array( 276 'field' => $this->fields[0], 277 'value' => array( 9, 12 ), 278 'compare' => 'NOT BETWEEN', 279 'type' => 'NUMERIC', 280 ), 281 ), 282 ) ); 283 284 $expected = array( $this->users[0], $this->users[2] ); 285 $this->assertEqualSets( $expected, array_keys( $q->results ) ); 286 } 287 288 public function test_single_clause_compare_regexp_rlike() { 289 $this->create_fields( 1 ); 290 $this->create_users( 3 ); 291 292 xprofile_set_field_data( $this->fields[0], $this->users[0], 'bar' ); 293 xprofile_set_field_data( $this->fields[0], $this->users[1], 'baz' ); 294 295 $q = new BP_User_Query( array( 296 'xprofile_query' => array( 297 array( 298 'field' => $this->fields[0], 299 'value' => 'z$', 300 'compare' => 'REGEXP', 301 ), 302 ), 303 ) ); 304 305 $expected = array( $this->users[1] ); 306 $this->assertEqualSets( $expected, array_keys( $q->results ) ); 307 308 // RLIKE is a synonym for REGEXP. 309 $q = new BP_User_Query( array( 310 'xprofile_query' => array( 311 array( 312 'field' => $this->fields[0], 313 'value' => 'z$', 314 'compare' => 'RLIKE', 315 ), 316 ), 317 ) ); 318 319 $expected = array( $this->users[1] ); 320 $this->assertEqualSets( $expected, array_keys( $q->results ) ); 321 } 322 323 public function test_single_clause_compare_not_regexp() { 324 $this->create_fields( 1 ); 325 $this->create_users( 3 ); 326 327 xprofile_set_field_data( $this->fields[0], $this->users[0], 'bar' ); 328 xprofile_set_field_data( $this->fields[0], $this->users[1], 'baz' ); 329 330 $q = new BP_User_Query( array( 331 'xprofile_query' => array( 332 array( 333 'field' => $this->fields[0], 334 'value' => 'z$', 335 'compare' => 'NOT REGEXP', 336 ), 337 ), 338 ) ); 339 340 $expected = array( $this->users[0] ); 341 $this->assertEqualSets( $expected, array_keys( $q->results ) ); 342 } 343 344 public function test_single_clause_compare_not_exists() { 345 $this->create_fields( 2 ); 346 $this->create_users( 2 ); 347 348 xprofile_set_field_data( $this->fields[0], $this->users[0], 'bar' ); 349 xprofile_set_field_data( $this->fields[1], $this->users[1], 'bar' ); 350 351 $q = new BP_User_Query( array( 352 'xprofile_query' => array( 353 array( 354 'field' => $this->fields[0], 355 'compare' => 'NOT EXISTS', 356 ), 357 ), 358 ) ); 359 360 $expected = array( $this->users[1] ); 361 $this->assertEqualSets( $expected, array_keys( $q->results ) ); 362 } 363 364 public function test_relation_default_to_and() { 365 $this->create_fields( 2 ); 366 $this->create_users( 4 ); 367 368 xprofile_set_field_data( $this->fields[0], $this->users[0], 'foo' ); 369 xprofile_set_field_data( $this->fields[0], $this->users[1], 'bar' ); 370 xprofile_set_field_data( $this->fields[1], $this->users[2], 'foo' ); 371 xprofile_set_field_data( $this->fields[1], $this->users[3], 'bar' ); 372 xprofile_set_field_data( $this->fields[0], $this->users[3], 'foo' ); 373 374 $q = new BP_User_Query( array( 375 'xprofile_query' => array( 376 array( 377 'field' => $this->fields[0], 378 'value' => 'foo', 379 ), 380 array( 381 'field' => $this->fields[1], 382 'value' => 'bar', 383 ), 384 ), 385 ) ); 386 387 $expected = array( $this->users[3] ); 388 $this->assertEqualSets( $expected, array_keys( $q->results ) ); 389 } 390 391 public function test_relation_and() { 392 $this->create_fields( 2 ); 393 $this->create_users( 4 ); 394 395 xprofile_set_field_data( $this->fields[0], $this->users[0], 'foo' ); 396 xprofile_set_field_data( $this->fields[0], $this->users[1], 'bar' ); 397 xprofile_set_field_data( $this->fields[1], $this->users[2], 'foo' ); 398 xprofile_set_field_data( $this->fields[1], $this->users[3], 'bar' ); 399 xprofile_set_field_data( $this->fields[0], $this->users[3], 'foo' ); 400 401 $q = new BP_User_Query( array( 402 'xprofile_query' => array( 403 'relation' => 'AND', 404 array( 405 'field' => $this->fields[0], 406 'value' => 'foo', 407 ), 408 array( 409 'field' => $this->fields[1], 410 'value' => 'bar', 411 ), 412 ), 413 ) ); 414 415 $expected = array( $this->users[3] ); 416 $this->assertEqualSets( $expected, array_keys( $q->results ) ); 417 } 418 419 public function test_relation_or() { 420 $this->create_fields( 2 ); 421 $this->create_users( 4 ); 422 423 xprofile_set_field_data( $this->fields[0], $this->users[0], 'foo' ); 424 xprofile_set_field_data( $this->fields[0], $this->users[1], 'bar' ); 425 xprofile_set_field_data( $this->fields[1], $this->users[2], 'foo' ); 426 xprofile_set_field_data( $this->fields[1], $this->users[3], 'bar' ); 427 xprofile_set_field_data( $this->fields[0], $this->users[3], 'foo' ); 428 429 $q = new BP_User_Query( array( 430 'xprofile_query' => array( 431 'relation' => 'OR', 432 array( 433 'field' => $this->fields[0], 434 'value' => 'foo', 435 ), 436 array( 437 'field' => $this->fields[1], 438 'value' => 'bar', 439 ), 440 ), 441 ) ); 442 443 $expected = array( $this->users[0], $this->users[3] ); 444 $this->assertEqualSets( $expected, array_keys( $q->results ) ); 445 } 446 447 public function test_relation_and_with_compare_not_exists() { 448 $this->create_fields( 2 ); 449 $this->create_users( 4 ); 450 451 xprofile_set_field_data( $this->fields[0], $this->users[0], 'foo' ); 452 xprofile_set_field_data( $this->fields[0], $this->users[1], 'bar' ); 453 xprofile_set_field_data( $this->fields[1], $this->users[2], 'foo' ); 454 xprofile_set_field_data( $this->fields[1], $this->users[3], 'bar' ); 455 456 $q = new BP_User_Query( array( 457 'xprofile_query' => array( 458 'relation' => 'AND', 459 array( 460 'field' => $this->fields[0], 461 'compare' => 'NOT EXISTS', 462 ), 463 array( 464 'field' => $this->fields[1], 465 'value' => 'bar', 466 ), 467 ), 468 ) ); 469 470 $expected = array( $this->users[3] ); 471 $this->assertEqualSets( $expected, array_keys( $q->results ) ); 472 } 473 474 public function test_relation_or_with_compare_not_exists() { 475 $this->create_fields( 2 ); 476 $this->create_users( 4 ); 477 478 xprofile_set_field_data( $this->fields[0], $this->users[0], 'foo' ); 479 xprofile_set_field_data( $this->fields[0], $this->users[1], 'bar' ); 480 xprofile_set_field_data( $this->fields[1], $this->users[2], 'foo' ); 481 xprofile_set_field_data( $this->fields[1], $this->users[3], 'bar' ); 482 xprofile_set_field_data( $this->fields[0], $this->users[3], 'bar' ); 483 484 $q = new BP_User_Query( array( 485 'xprofile_query' => array( 486 'relation' => 'OR', 487 array( 488 'field' => $this->fields[0], 489 'compare' => 'NOT EXISTS', 490 ), 491 array( 492 'field' => $this->fields[1], 493 'value' => 'bar', 494 ), 495 ), 496 ) ); 497 498 $expected = array( $this->users[2], $this->users[3] ); 499 $this->assertEqualSets( $expected, array_keys( $q->results ) ); 500 } 501 502 /** 503 * Tests for table join logic. 504 */ 505 public function test_relation_or_compare_equals_and_like() { 506 $this->create_fields( 2 ); 507 $this->create_users( 4 ); 508 509 xprofile_set_field_data( $this->fields[0], $this->users[0], 'foo' ); 510 xprofile_set_field_data( $this->fields[0], $this->users[1], 'bar' ); 511 xprofile_set_field_data( $this->fields[1], $this->users[2], 'foo' ); 512 xprofile_set_field_data( $this->fields[1], $this->users[3], 'barry' ); 513 514 $q = new BP_User_Query( array( 515 'xprofile_query' => array( 516 'relation' => 'OR', 517 array( 518 'field' => $this->fields[0], 519 'compare' => '=', 520 'value' => 'foo', 521 ), 522 array( 523 'field' => $this->fields[1], 524 'value' => 'bar', 525 'compare' => 'LIKE', 526 ), 527 ), 528 ) ); 529 530 $expected = array( $this->users[0], $this->users[3] ); 531 $this->assertEqualSets( $expected, array_keys( $q->results ) ); 532 } 533 534 public function test_nested() { 535 $this->create_fields( 2 ); 536 $this->create_users( 3 ); 537 538 xprofile_set_field_data( $this->fields[0], $this->users[0], 'foo' ); 539 xprofile_set_field_data( $this->fields[0], $this->users[1], 'bar' ); 540 xprofile_set_field_data( $this->fields[1], $this->users[2], 'foo' ); 541 xprofile_set_field_data( $this->fields[1], $this->users[1], 'foo' ); 542 543 $q = new BP_User_Query( array( 544 'xprofile_query' => array( 545 'relation' => 'OR', 546 array( 547 'field' => $this->fields[0], 548 'compare' => '=', 549 'value' => 'foo', 550 ), 551 array( 552 'relation' => 'AND', 553 array( 554 'field' => $this->fields[0], 555 'value' => 'bar', 556 ), 557 array( 558 'field' => $this->fields[1], 559 'value' => 'foo', 560 ), 561 ), 562 ), 563 ) ); 564 565 $expected = array( $this->users[0], $this->users[1] ); 566 $this->assertEqualSets( $expected, array_keys( $q->results ) ); 567 } 568 569 /** Helpers **********************************************************/ 570 571 protected function create_fields( $count ) { 572 $this->group = $this->factory->xprofile_group->create(); 573 for ( $i = 0; $i < $count; $i++ ) { 574 $this->fields[] = $this->factory->xprofile_field->create( array( 575 'field_group_id' => $this->group, 576 'type' => 'textbox', 577 ) ); 578 } 579 } 580 581 protected function create_users( $count ) { 582 for ( $i = 0; $i < $count; $i++ ) { 583 $this->users[] = $this->create_user(); 584 } 585 } 586 }