2019-12-20 15:06:58 +02:00
< ? php
namespace um\core ;
2023-06-12 13:20:00 +03:00
if ( ! defined ( 'ABSPATH' ) ) {
exit ;
}
2019-12-20 15:06:58 +02:00
if ( ! class_exists ( 'um\core\Member_Directory_Meta' ) ) {
/**
* Class Member_Directory_Meta
* @package um\core
*/
class Member_Directory_Meta extends Member_Directory {
2023-06-12 13:20:00 +03:00
/**
* @var array
*/
public $joins = array ();
2019-12-20 15:06:58 +02:00
/**
2023-06-12 13:20:00 +03:00
* @var array
2019-12-20 15:06:58 +02:00
*/
2023-06-12 13:20:00 +03:00
public $where_clauses = array ();
2020-01-08 15:19:24 +02:00
2023-06-12 13:20:00 +03:00
/**
* @var array
*/
public $roles = array ();
/**
* @var bool
*/
2024-02-02 02:19:15 +02:00
private $roles_in_query = false ;
2020-07-23 16:06:59 +03:00
2024-02-02 02:19:15 +02:00
/**
* @var bool
*/
public $general_meta_joined = false ;
2020-01-08 15:19:24 +02:00
2024-02-02 02:19:15 +02:00
/**
* @var string
*/
private $having = '' ;
2019-12-20 15:06:58 +02:00
2024-02-02 02:19:15 +02:00
/**
* @var string
*/
private $select = '' ;
/**
* @var string
*/
private $sql_limit = '' ;
2019-12-20 15:06:58 +02:00
2024-02-02 02:19:15 +02:00
/**
* @var string
*/
public $sql_order = '' ;
2019-12-20 15:06:58 +02:00
/**
* Member_Directory_Meta constructor.
*/
2023-06-12 13:20:00 +03:00
public function __construct () {
2019-12-20 15:06:58 +02:00
parent :: __construct ();
add_action ( 'updated_user_meta' , array ( & $this , 'on_update_usermeta' ), 10 , 4 );
add_action ( 'added_user_meta' , array ( & $this , 'on_update_usermeta' ), 10 , 4 );
2020-01-06 11:05:32 +02:00
add_action ( 'deleted_user_meta' , array ( & $this , 'on_delete_usermeta' ), 10 , 4 );
2020-04-10 17:32:32 +03:00
add_action ( 'um_add_new_field' , array ( & $this , 'on_new_field_added' ), 10 , 2 );
add_action ( 'um_delete_custom_field' , array ( & $this , 'on_delete_custom_field' ), 10 , 2 );
2020-01-06 11:05:32 +02:00
}
/**
* Delete custom field and metakey from UM usermeta table
*
* @param $metakey
2020-04-10 17:32:32 +03:00
* @param $args
2020-01-06 11:05:32 +02:00
*/
2024-02-02 02:19:15 +02:00
public function on_delete_custom_field ( $metakey , $args ) {
2020-01-06 11:05:32 +02:00
$metakeys = get_option ( 'um_usermeta_fields' , array () );
2020-04-10 17:32:32 +03:00
2024-02-02 02:19:15 +02:00
if ( in_array ( $metakey , $metakeys , true ) ) {
unset ( $metakeys [ array_search ( $metakey , $metakeys , true ) ] );
2021-03-12 12:24:31 +02:00
global $wpdb ;
$wpdb -> delete (
" { $wpdb -> prefix } um_metadata " ,
array (
2024-02-02 02:19:15 +02:00
'um_key' => $metakey ,
2021-03-12 12:24:31 +02:00
),
array (
2024-02-02 02:19:15 +02:00
'%s' ,
2021-03-12 12:24:31 +02:00
)
);
update_option ( 'um_usermeta_fields' , array_values ( $metakeys ) );
2020-01-06 11:05:32 +02:00
}
2021-03-12 12:24:31 +02:00
2021-04-02 13:24:48 +03:00
do_action ( 'um_metadata_on_delete_custom_field' , $metakeys , $metakey , $args );
2020-01-06 11:05:32 +02:00
}
/**
* Add metakey to usermeta fields
*
* @param $metakey
2020-04-10 17:32:32 +03:00
* @param $args
2020-01-06 11:05:32 +02:00
*/
2024-02-02 02:19:15 +02:00
public function on_new_field_added ( $metakey , $args ) {
2020-01-06 11:05:32 +02:00
$metakeys = get_option ( 'um_usermeta_fields' , array () );
2020-04-10 17:32:32 +03:00
2024-02-02 02:19:15 +02:00
if ( ! in_array ( $metakey , $metakeys , true ) ) {
2021-03-12 12:24:31 +02:00
$metakeys [] = $metakey ;
update_option ( 'um_usermeta_fields' , array_values ( $metakeys ) );
2020-01-06 11:05:32 +02:00
}
2021-03-12 12:24:31 +02:00
2021-04-02 13:24:48 +03:00
do_action ( 'um_metadata_on_new_field_added' , $metakeys , $metakey , $args );
2020-01-06 11:05:32 +02:00
}
/**
* When you delete usermeta - remove row from um_metadata
*
* @param int|array $meta_ids
* @param int $object_id
* @param string $meta_key
* @param mixed $_meta_value
*/
2024-02-02 02:19:15 +02:00
public function on_delete_usermeta ( $meta_ids , $object_id , $meta_key , $_meta_value ) {
2020-01-06 11:05:32 +02:00
$metakeys = get_option ( 'um_usermeta_fields' , array () );
2024-02-02 02:19:15 +02:00
if ( ! in_array ( $meta_key , $metakeys , true ) ) {
2020-01-06 11:05:32 +02:00
return ;
}
global $wpdb ;
$wpdb -> delete (
" { $wpdb -> prefix } um_metadata " ,
array (
2024-02-02 02:19:15 +02:00
'user_id' => $object_id ,
'um_key' => $meta_key ,
2020-01-06 11:05:32 +02:00
),
array (
'%d' ,
2024-02-02 02:19:15 +02:00
'%s' ,
2020-01-06 11:05:32 +02:00
)
);
2019-12-20 15:06:58 +02:00
}
2020-01-06 11:05:32 +02:00
/**
* When you add/update usermeta - add/update row from um_metadata
*
* @param int $meta_id
* @param int $object_id
* @param string $meta_key
* @param mixed $_meta_value
*/
2024-02-02 02:19:15 +02:00
public function on_update_usermeta ( $meta_id , $object_id , $meta_key , $_meta_value ) {
2019-12-20 15:06:58 +02:00
$metakeys = get_option ( 'um_usermeta_fields' , array () );
2024-02-02 02:19:15 +02:00
if ( ! in_array ( $meta_key , $metakeys , true ) ) {
2019-12-20 15:06:58 +02:00
return ;
}
global $wpdb ;
2024-02-02 02:19:15 +02:00
$result = $wpdb -> get_var (
$wpdb -> prepare (
" SELECT umeta_id
FROM { $wpdb -> prefix } um_metadata
WHERE user_id = %d AND
2025-02-03 16:17:37 +02:00
um_key = %s
2024-02-02 02:19:15 +02:00
LIMIT 1 " ,
$object_id ,
$meta_key
)
);
2019-12-20 15:06:58 +02:00
if ( empty ( $result ) ) {
$wpdb -> insert (
" { $wpdb -> prefix } um_metadata " ,
array (
2024-02-02 02:19:15 +02:00
'user_id' => $object_id ,
'um_key' => $meta_key ,
'um_value' => maybe_serialize ( $_meta_value ),
2019-12-20 15:06:58 +02:00
),
array (
'%d' ,
'%s' ,
'%s' ,
)
);
} else {
$wpdb -> update (
" { $wpdb -> prefix } um_metadata " ,
array (
2024-02-02 02:19:15 +02:00
'um_value' => maybe_serialize ( $_meta_value ),
2019-12-20 15:06:58 +02:00
),
array (
2024-02-02 02:19:15 +02:00
'umeta_id' => $result ,
2019-12-20 15:06:58 +02:00
),
array (
'%s' ,
),
array (
'%d' ,
)
);
}
}
2020-01-08 15:19:24 +02:00
/**
* @param $directory_data
* @param $field
* @param $value
2024-02-02 02:19:15 +02:00
* @param int $i
2020-01-08 15:19:24 +02:00
* @param bool $is_default
*/
2024-02-02 02:19:15 +02:00
protected function handle_filter_query ( $directory_data , $field , $value , $i , $is_default = false ) {
2020-01-08 15:19:24 +02:00
global $wpdb ;
2024-02-02 02:19:15 +02:00
$join_slug = $is_default ? 'ummd' : 'umm' ;
$join_alias = esc_sql ( $join_slug . $i );
2020-01-10 17:31:55 +02:00
2020-01-08 15:19:24 +02:00
$blog_id = get_current_blog_id ();
2024-03-28 12:24:29 +02:00
/**
* Filters member directory select-type filter relation in query.
*
* @param {string} $relation Relation `OR` or `AND`. `OR` by default.
* @param {string} $field Field key.
*
* @return {string} Relation.
*
* @since 2.8.5
* @hook um_members_directory_select_filter_relation
*
* @example <caption>Change relation to 'AND'.</caption>
* function my_um_members_directory_select_filter_relation( $relation, $field ) {
* // your code here
* $relation = 'AND';
* return $relation;
* }
* add_filter( 'um_members_directory_select_filter_relation', 'my_um_members_directory_select_filter_relation', 10, 2 );
*/
$relation = apply_filters ( 'um_members_directory_select_filter_relation' , 'OR' , $field );
2020-01-08 15:19:24 +02:00
switch ( $field ) {
default :
$filter_type = $this -> filter_types [ $field ];
/**
2024-02-02 02:19:15 +02:00
* Filters marker for skipping default filter handle in member directory queries.
* Hook handle filter queries for the custom usermeta table only.
* Note: $field is the field meta key.
*
* @since 2.1
* @hook um_query_args_{$field}__filter_meta
2020-01-08 15:19:24 +02:00
*
2024-02-02 02:19:15 +02:00
* @param {bool} $skip Skip default filter handler marker.
* @param {object} $member_directory_meta Member_Directory_Meta class instance.
* @param {string} $field Filter's field key.
* @param {mixed} $value Filter value.
* @param {string} $filter_type Filter type.
* @param {bool} $is_default If it's admin filtering option then `true`.
2020-01-08 15:19:24 +02:00
*
2024-02-02 02:19:15 +02:00
* @return {bool} Skip default filter handler marker.
*
* @example <caption>Skip filter by rating default handler and add 3rd-party handlers in callback.</caption>
* function um_custom_query_args_filter_rating__filter_meta( $skip, $member_directory_meta, $field, $value, $filter_type, $is_default ) {
* $skip = true;
* $member_directory_meta->joins[] = "LEFT JOIN {$wpdb->prefix}um_metadata ummreviews ON ( ummreviews.user_id = u.ID AND ummreviews.um_key = '_reviews_avg' )";
* return $skip;
2020-01-08 15:19:24 +02:00
* }
2024-02-02 02:19:15 +02:00
* add_filter( 'um_query_args_filter_rating__filter_meta', 'um_custom_query_args_filter_rating__filter_meta', 10, 6 );
2020-01-08 15:19:24 +02:00
*/
$skip_default = apply_filters ( " um_query_args_ { $field } __filter_meta " , false , $this , $field , $value , $filter_type , $is_default );
2024-02-02 02:19:15 +02:00
/**
* Filters marker for skipping default filter handle in member directory queries.
* Hook handle filter queries for the custom usermeta table only.
*
* @since 2.1
* @hook um_query_args_filter_global_meta
*
* @param {bool} $skip Skip default filter handler marker.
* @param {object} $member_directory_meta Member_Directory_Meta class instance.
* @param {string} $field Filter's field key.
* @param {mixed} $value Filter value.
* @param {string} $filter_type Filter type.
* @param {bool} $is_default If it's admin filtering option then `true`.
*
* @return {bool} Skip default filter handler marker.
*
* @example <caption>Skip filter by rating default handler and add 3rd-party handlers in callback.</caption>
* function um_custom_query_args_filter_global_meta( $skip, $member_directory_meta, $field, $value, $filter_type, $is_default ) {
* if ( 'filter_rating' === $field ) {
* $skip = true;
* $member_directory_meta->joins[] = "LEFT JOIN {$wpdb->prefix}um_metadata ummreviews ON ( ummreviews.user_id = u.ID AND ummreviews.um_key = '_reviews_avg' )";
* }
* return $skip;
* }
* add_filter( 'um_query_args_filter_global_meta', 'um_custom_query_args_filter_global_meta', 10, 6 );
*/
2020-02-21 09:01:18 +02:00
$skip_default = apply_filters ( 'um_query_args_filter_global_meta' , $skip_default , $this , $field , $value , $filter_type , $is_default );
2020-01-08 15:19:24 +02:00
if ( ! $skip_default ) {
switch ( $filter_type ) {
default :
do_action ( " um_query_args_ { $field } _ { $filter_type } __filter_meta " , $field , $value , $filter_type , $i , $is_default );
break ;
case 'text' :
2024-02-02 02:19:15 +02:00
// $join_alias is pre-escaped.
$this -> joins [] = " LEFT JOIN { $wpdb -> prefix } um_metadata { $join_alias } ON { $join_alias } .user_id = u.ID " ;
2020-01-08 15:19:24 +02:00
2024-02-02 02:19:15 +02:00
$value = trim ( stripslashes ( $value ) );
2021-03-12 12:24:31 +02:00
$compare = apply_filters ( 'um_members_directory_filter_text' , '=' , $field );
2024-02-02 02:19:15 +02:00
$compare = esc_sql ( $compare );
$value = apply_filters ( 'um_members_directory_filter_text_meta_value' , $value , $field );
2021-03-12 12:24:31 +02:00
2024-02-02 02:19:15 +02:00
// phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- $join_alias and $compare variables are pre-escaped.
$this -> where_clauses [] = $wpdb -> prepare ( " { $join_alias } .um_key = %s AND { $join_alias } .um_value { $compare } %s " , $field , $value );
2020-01-08 15:19:24 +02:00
if ( ! $is_default ) {
$this -> custom_filters_in_query [ $field ] = $value ;
}
break ;
case 'select' :
if ( ! is_array ( $value ) ) {
$value = array ( $value );
}
2024-02-02 02:19:15 +02:00
// $join_alias is pre-escaped.
$this -> joins [] = " LEFT JOIN { $wpdb -> prefix } um_metadata { $join_alias } ON { $join_alias } .user_id = u.ID " ;
2020-01-08 15:19:24 +02:00
$values_array = array ();
foreach ( $value as $single_val ) {
2020-11-24 12:55:22 +02:00
$single_val = trim ( stripslashes ( $single_val ) );
2020-01-08 15:19:24 +02:00
2024-02-02 02:19:15 +02:00
// phpcs:disable WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- $join_alias and $compare variables are pre-escaped.
$values_array [] = $wpdb -> prepare ( " { $join_alias } .um_value LIKE %s " , '%"' . $wpdb -> esc_like ( $single_val ) . '"%' );
2025-04-11 16:04:55 +03:00
$values_array [] = $wpdb -> prepare ( " { $join_alias } .um_value LIKE %s " , '%' . $wpdb -> esc_like ( serialize ( ( string ) $single_val ) ) . '%' );
2024-02-02 02:19:15 +02:00
$values_array [] = $wpdb -> prepare ( " { $join_alias } .um_value = %s " , $single_val );
2020-01-08 15:19:24 +02:00
if ( is_numeric ( $single_val ) ) {
2025-04-11 16:04:55 +03:00
$values_array [] = $wpdb -> prepare ( " { $join_alias } .um_value LIKE %s " , '%' . $wpdb -> esc_like ( serialize ( ( int ) $single_val ) ) . '%' );
2020-01-08 15:19:24 +02:00
}
2024-02-02 02:19:15 +02:00
// phpcs:enable WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- $join_alias is pre-escaped.
2020-01-08 15:19:24 +02:00
}
2024-03-28 12:24:29 +02:00
$values = implode ( ' ' . esc_sql ( $relation ) . ' ' , $values_array );
2020-01-08 15:19:24 +02:00
2024-02-02 02:19:15 +02:00
// phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- $join_alias and $values variables are pre-escaped or $wpdb->prepare.
$this -> where_clauses [] = $wpdb -> prepare ( " ( { $join_alias } .um_key = %s AND ( { $values } ) ) " , $field );
2020-01-08 15:19:24 +02:00
if ( ! $is_default ) {
$this -> custom_filters_in_query [ $field ] = $value ;
}
break ;
2024-02-02 02:19:15 +02:00
case 'slider' :
// $join_alias is pre-escaped.
$this -> joins [] = " LEFT JOIN { $wpdb -> prefix } um_metadata { $join_alias } ON { $join_alias } .user_id = u.ID " ;
2020-01-08 15:19:24 +02:00
$min = min ( $value );
$max = max ( $value );
2024-02-02 02:19:15 +02:00
// phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- $join_alias is pre-escaped.
$this -> where_clauses [] = $wpdb -> prepare ( " ( { $join_alias } .um_key = %s AND { $join_alias } .um_value BETWEEN %d AND %d ) " , $field , $min , $max );
2020-01-08 15:19:24 +02:00
if ( ! $is_default ) {
$this -> custom_filters_in_query [ $field ] = $value ;
}
break ;
2024-02-02 02:19:15 +02:00
case 'datepicker' :
2020-01-08 15:19:24 +02:00
$offset = 0 ;
if ( ! $is_default ) {
2024-02-02 02:19:15 +02:00
// phpcs:disable WordPress.Security.NonceVerification -- early verified in `ajax_get_members()`.
2020-01-08 15:19:24 +02:00
if ( isset ( $_POST [ 'gmt_offset' ] ) && is_numeric ( $_POST [ 'gmt_offset' ] ) ) {
$offset = ( int ) $_POST [ 'gmt_offset' ];
}
2024-02-02 02:19:15 +02:00
// phpcs:enable WordPress.Security.NonceVerification -- early verified in `ajax_get_members()`.
2020-01-08 15:19:24 +02:00
} else {
$gmt_offset = get_post_meta ( $directory_data [ 'form_id' ], '_um_search_filters_gmt' , true );
if ( is_numeric ( $gmt_offset ) ) {
$offset = $gmt_offset ;
}
}
2024-04-15 17:58:18 +03:00
if ( ! empty ( $value [ 0 ] ) ) {
$min = $value [ 0 ];
} else {
$range = $this -> datepicker_filters_range ( $field );
$min = strtotime ( gmdate ( 'Y/m/d' , $range [ 0 ] ) );
}
if ( ! empty ( $value [ 1 ] ) ) {
$max = $value [ 1 ];
} else {
$max = strtotime ( gmdate ( 'Y/m/d' ) );
}
$from_date = ( int ) $min + ( $offset * HOUR_IN_SECONDS ); // client time zone offset
$to_date = ( int ) $max + ( $offset * HOUR_IN_SECONDS ) + DAY_IN_SECONDS - 1 ; // time 23:59
2020-01-08 15:19:24 +02:00
2024-02-02 02:19:15 +02:00
// $join_alias is pre-escaped.
$this -> joins [] = " LEFT JOIN { $wpdb -> prefix } um_metadata { $join_alias } ON { $join_alias } .user_id = u.ID " ;
2020-01-08 15:19:24 +02:00
2024-02-02 02:19:15 +02:00
// phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- $join_alias is pre-escaped.
$this -> where_clauses [] = $wpdb -> prepare ( " ( { $join_alias } .um_key = %s AND { $join_alias } .um_value BETWEEN %s AND %s ) " , $field , $from_date , $to_date );
2020-01-08 15:19:24 +02:00
if ( ! $is_default ) {
$this -> custom_filters_in_query [ $field ] = array ( $from_date , $to_date );
}
break ;
2024-02-02 02:19:15 +02:00
case 'timepicker' :
2024-04-16 13:47:09 +03:00
if ( ! empty ( $value [ 0 ] ) ) {
$value [ 0 ] = $value [ 0 ] . ':00' ;
} else {
$range = $this -> timepicker_filters_range ( $field );
$value [ 0 ] = $range [ 0 ] . ':00' ;
}
if ( ! empty ( $value [ 1 ] ) ) {
$value [ 1 ] = $value [ 1 ] . ':00' ;
} else {
$range = $this -> timepicker_filters_range ( $field );
$value [ 1 ] = $range [ 1 ] . ':00' ;
}
2024-02-02 02:19:15 +02:00
// $join_alias is pre-escaped.
$this -> joins [] = " LEFT JOIN { $wpdb -> prefix } um_metadata { $join_alias } ON { $join_alias } .user_id = u.ID " ;
if ( $value [ 0 ] === $value [ 1 ] ) {
// phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- $join_alias is pre-escaped.
$this -> where_clauses [] = $wpdb -> prepare ( " ( { $join_alias } .um_key = %s AND { $join_alias } .um_value = %s ) " , $field , $value [ 0 ] );
2020-01-08 15:19:24 +02:00
} else {
2024-02-02 02:19:15 +02:00
// phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- $join_alias is pre-escaped.
$this -> where_clauses [] = $wpdb -> prepare ( " ( { $join_alias } .um_key = %s AND CAST( { $join_alias } .um_value AS TIME ) BETWEEN %s AND %s ) " , $field , $value [ 0 ], $value [ 1 ] );
2020-01-08 15:19:24 +02:00
}
if ( ! $is_default ) {
$this -> custom_filters_in_query [ $field ] = $value ;
}
break ;
2024-02-02 02:19:15 +02:00
}
2020-01-08 15:19:24 +02:00
}
break ;
2024-02-02 02:19:15 +02:00
2020-01-08 15:19:24 +02:00
case 'role' :
$value = array_map ( 'strtolower' , $value );
if ( empty ( $this -> roles ) && ! is_multisite () ) {
2024-02-02 02:19:15 +02:00
$this -> joins [] = $wpdb -> prepare ( " LEFT JOIN { $wpdb -> prefix } um_metadata umm_roles ON ( umm_roles.user_id = u.ID AND umm_roles.um_key = %s ) " , $wpdb -> get_blog_prefix ( $blog_id ) . 'capabilities' );
$this -> roles = $value ;
2020-07-23 16:06:59 +03:00
$this -> roles_in_query = true ;
2020-01-08 15:19:24 +02:00
}
$roles_clauses = array ();
foreach ( $value as $role ) {
2024-02-02 02:19:15 +02:00
$roles_clauses [] = $wpdb -> prepare ( 'umm_roles.um_value LIKE %s' , '%"' . $wpdb -> esc_like ( $role ) . '"%' );
2020-01-08 15:19:24 +02:00
}
2024-02-02 02:19:15 +02:00
// $roles_clauses is pre-prepared.
2024-03-28 12:24:29 +02:00
$this -> where_clauses [] = '( ' . implode ( ' ' . esc_sql ( $relation ) . ' ' , $roles_clauses ) . ' )' ;
2020-01-08 15:19:24 +02:00
if ( ! $is_default ) {
$this -> custom_filters_in_query [ $field ] = $value ;
}
break ;
2024-02-02 02:19:15 +02:00
case 'birth_date' :
// @todo: rewrite date() in WP5.3 standards.
2020-01-08 15:19:24 +02:00
$from_date = date ( 'Y/m/d' , mktime ( 0 , 0 , 0 , date ( 'm' , time () ), date ( 'd' , time () ), date ( 'Y' , time () - min ( $value ) * YEAR_IN_SECONDS ) ) );
2024-02-02 02:19:15 +02:00
$to_date = date ( 'Y/m/d' , mktime ( 0 , 0 , 0 , date ( 'm' , time () ), date ( 'd' , time () ) + 1 , date ( 'Y' , time () - ( max ( $value ) + 1 ) * YEAR_IN_SECONDS ) ) );
2020-01-08 15:19:24 +02:00
2024-02-02 02:19:15 +02:00
// $join_alias is pre-escaped.
$this -> joins [] = " LEFT JOIN { $wpdb -> prefix } um_metadata { $join_alias } ON { $join_alias } .user_id = u.ID " ;
// phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- $join_alias is pre-escaped.
$this -> where_clauses [] = $wpdb -> prepare ( " ( { $join_alias } .um_key = 'birth_date' AND { $join_alias } .um_value BETWEEN %s AND %s ) " , $to_date , $from_date );
2020-01-08 15:19:24 +02:00
if ( ! $is_default ) {
$this -> custom_filters_in_query [ $field ] = array ( $to_date , $from_date );
}
break ;
2024-02-02 02:19:15 +02:00
case 'user_registered' :
2020-01-08 15:19:24 +02:00
$offset = 0 ;
if ( ! $is_default ) {
2024-02-02 02:19:15 +02:00
// phpcs:disable WordPress.Security.NonceVerification -- early verified in `ajax_get_members()`.
2020-01-08 15:19:24 +02:00
if ( isset ( $_POST [ 'gmt_offset' ] ) && is_numeric ( $_POST [ 'gmt_offset' ] ) ) {
$offset = ( int ) $_POST [ 'gmt_offset' ];
}
2024-02-02 02:19:15 +02:00
// phpcs:enable WordPress.Security.NonceVerification -- early verified in `ajax_get_members()`.
2020-01-08 15:19:24 +02:00
} else {
$gmt_offset = get_post_meta ( $directory_data [ 'form_id' ], '_um_search_filters_gmt' , true );
if ( is_numeric ( $gmt_offset ) ) {
2020-08-06 15:12:50 +03:00
$offset = ( int ) $gmt_offset ;
2020-01-08 15:19:24 +02:00
}
}
2024-02-02 02:19:15 +02:00
// @todo: rewrite date() in WP5.3 standards.
2023-12-01 02:17:37 +02:00
$from_date = date ( 'Y-m-d H:i:s' , strtotime ( min ( $value ) ) + $offset * HOUR_IN_SECONDS ); // client time zone offset
2024-02-02 02:19:15 +02:00
$to_date = date ( 'Y-m-d H:i:s' , strtotime ( max ( $value ) ) + $offset * HOUR_IN_SECONDS + DAY_IN_SECONDS - 1 ); // time 23:59
2020-01-08 15:19:24 +02:00
2024-02-02 02:19:15 +02:00
$this -> where_clauses [] = $wpdb -> prepare ( 'u.user_registered BETWEEN %s AND %s' , $from_date , $to_date );
2020-01-08 15:19:24 +02:00
if ( ! $is_default ) {
$this -> custom_filters_in_query [ $field ] = $value ;
}
break ;
2024-02-02 02:19:15 +02:00
2020-01-08 15:19:24 +02:00
case 'last_login' :
$offset = 0 ;
if ( ! $is_default ) {
2024-02-02 02:19:15 +02:00
// phpcs:disable WordPress.Security.NonceVerification -- early verified in `ajax_get_members()`.
2020-01-08 15:19:24 +02:00
if ( isset ( $_POST [ 'gmt_offset' ] ) && is_numeric ( $_POST [ 'gmt_offset' ] ) ) {
$offset = ( int ) $_POST [ 'gmt_offset' ];
}
2024-02-02 02:19:15 +02:00
// phpcs:enable WordPress.Security.NonceVerification -- early verified in `ajax_get_members()`.
2020-01-08 15:19:24 +02:00
} else {
$gmt_offset = get_post_meta ( $directory_data [ 'form_id' ], '_um_search_filters_gmt' , true );
if ( is_numeric ( $gmt_offset ) ) {
$offset = $gmt_offset ;
}
}
2024-04-02 17:28:54 +03:00
$value = array_map (
function ( $date ) {
return is_numeric ( $date ) ? $date : strtotime ( $date );
},
$value
);
2024-04-15 17:58:18 +03:00
if ( ! empty ( $value [ 0 ] ) ) {
$min = $value [ 0 ];
} else {
$range = $this -> datepicker_filters_range ( 'last_login' );
$min = strtotime ( gmdate ( 'Y/m/d' , $range [ 0 ] ) );
}
if ( ! empty ( $value [ 1 ] ) ) {
$max = $value [ 1 ];
} else {
$max = strtotime ( gmdate ( 'Y/m/d' ) );
}
$from_date = gmdate ( 'Y-m-d H:i:s' , ( int ) $min + ( $offset * HOUR_IN_SECONDS ) ); // client time zone offset
$to_date = gmdate ( 'Y-m-d H:i:s' , ( int ) $max + ( $offset * HOUR_IN_SECONDS ) + DAY_IN_SECONDS - 1 ); // time 23:59
2020-01-08 15:19:24 +02:00
2024-02-02 02:19:15 +02:00
// $join_alias is pre-escaped.
$this -> joins [] = " LEFT JOIN { $wpdb -> prefix } um_metadata { $join_alias } ON { $join_alias } .user_id = u.ID " ;
2024-04-02 17:28:54 +03:00
$join_alias_ll = $join_alias . '_show_las_login' ;
$this -> joins [] = " LEFT JOIN { $wpdb -> prefix } um_metadata { $join_alias_ll } ON { $join_alias_ll } .user_id = u.ID AND { $join_alias_ll } .um_key = 'um_show_last_login' " ;
2024-02-02 02:19:15 +02:00
// phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- $join_alias is pre-escaped.
2024-04-02 17:28:54 +03:00
$this -> where_clauses [] = $wpdb -> prepare ( " ( { $join_alias } .um_key = '_um_last_login' AND { $join_alias } .um_value BETWEEN %s AND %s AND ( { $join_alias_ll } .um_value IS NULL OR { $join_alias_ll } .um_value != %s ) ) " , $from_date , $to_date , 'a:1:{i:0;s:2:"no";}' );
2020-01-08 15:19:24 +02:00
if ( ! $is_default ) {
$this -> custom_filters_in_query [ $field ] = $value ;
}
break ;
2024-02-02 02:19:15 +02:00
2024-03-26 00:47:40 +02:00
case 'gender' :
if ( ! is_array ( $value ) ) {
$value = array ( $value );
}
// $join_alias is pre-escaped.
$this -> joins [] = " LEFT JOIN { $wpdb -> prefix } um_metadata { $join_alias } ON { $join_alias } .user_id = u.ID " ;
$values_array = array ();
foreach ( $value as $single_val ) {
$single_val = trim ( stripslashes ( $single_val ) );
// phpcs:disable WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- $join_alias and $compare variables are pre-escaped.
$values_array [] = $wpdb -> prepare ( " { $join_alias } .um_value LIKE %s " , '%"' . $wpdb -> esc_like ( $single_val ) . '"%' );
$values_array [] = $wpdb -> prepare ( " { $join_alias } .um_value = %s " , $single_val );
// phpcs:enable WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- $join_alias is pre-escaped.
}
2024-03-28 12:24:29 +02:00
$values = implode ( ' ' . esc_sql ( $relation ) . ' ' , $values_array );
2024-03-26 00:47:40 +02:00
// phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- $join_alias and $values variables are pre-escaped or $wpdb->prepare.
$this -> where_clauses [] = $wpdb -> prepare ( " ( { $join_alias } .um_key = %s AND ( { $values } ) ) " , $field );
2020-01-08 15:19:24 +02:00
2024-03-26 00:47:40 +02:00
if ( ! $is_default ) {
$this -> custom_filters_in_query [ $field ] = $value ;
}
2020-01-08 15:19:24 +02:00
break ;
}
}
2019-12-20 15:06:58 +02:00
/**
* Main Query function for getting members via AJAX
*/
2024-02-02 02:19:15 +02:00
public function ajax_get_members () {
2019-12-20 15:06:58 +02:00
UM () -> check_ajax_nonce ();
2025-12-11 17:36:42 +02:00
if ( UM () -> is_rate_limited ( 'member_directory' ) ) {
wp_send_json_error ( __ ( 'Too many requests' , 'ultimate-member' ) );
}
2019-12-20 15:06:58 +02:00
global $wpdb ;
$blog_id = get_current_blog_id ();
2024-02-02 02:19:15 +02:00
// phpcs:disable WordPress.Security.NonceVerification -- verified via `UM()->check_ajax_nonce();`.
2022-07-07 14:30:10 +03:00
if ( empty ( $_POST [ 'directory_id' ] ) ) {
wp_send_json_error ( __ ( 'Wrong member directory data' , 'ultimate-member' ) );
}
$directory_id = $this -> get_directory_by_hash ( sanitize_key ( $_POST [ 'directory_id' ] ) );
if ( empty ( $directory_id ) ) {
wp_send_json_error ( __ ( 'Wrong member directory data' , 'ultimate-member' ) );
}
2024-02-02 02:19:15 +02:00
// phpcs:enable WordPress.Security.NonceVerification -- verified via `UM()->check_ajax_nonce();`.
2022-07-07 14:30:10 +03:00
2025-12-11 17:36:42 +02:00
if ( ! $this -> can_view_directory ( $directory_id ) ) {
wp_send_json_error ( __ ( 'You cannot see this member directory' , 'ultimate-member' ) );
}
2019-12-20 15:06:58 +02:00
$directory_data = UM () -> query () -> post_data ( $directory_id );
2024-02-02 02:19:15 +02:00
// Predefined result for user without capabilities to see other members.
2019-12-20 15:06:58 +02:00
$this -> predefined_no_caps ( $directory_data );
do_action ( 'um_member_directory_before_query' );
// Prepare for BIG SELECT query
$wpdb -> query ( 'SET SQL_BIG_SELECTS=1' );
if ( ! empty ( $directory_data [ 'show_these_users' ] ) ) {
$show_these_users = maybe_unserialize ( $directory_data [ 'show_these_users' ] );
if ( is_array ( $show_these_users ) && ! empty ( $show_these_users ) ) {
$users_array = array ();
foreach ( $show_these_users as $username ) {
if ( false !== ( $exists_id = username_exists ( $username ) ) ) {
2024-02-02 02:19:15 +02:00
$users_array [] = absint ( $exists_id );
2019-12-20 15:06:58 +02:00
}
}
if ( ! empty ( $users_array ) ) {
2020-01-08 15:19:24 +02:00
$this -> where_clauses [] = " u.ID IN ( ' " . implode ( " ',' " , $users_array ) . " ' ) " ;
2019-12-20 15:06:58 +02:00
}
}
}
2020-05-25 17:51:48 +03:00
if ( ! empty ( $directory_data [ 'exclude_these_users' ] ) ) {
$exclude_these_users = maybe_unserialize ( $directory_data [ 'exclude_these_users' ] );
if ( is_array ( $exclude_these_users ) && ! empty ( $exclude_these_users ) ) {
$users_array = array ();
foreach ( $exclude_these_users as $username ) {
if ( false !== ( $exists_id = username_exists ( $username ) ) ) {
2024-02-02 02:19:15 +02:00
$users_array [] = absint ( $exists_id );
2020-05-25 17:51:48 +03:00
}
}
if ( ! empty ( $users_array ) ) {
$this -> where_clauses [] = " u.ID NOT IN ( ' " . implode ( " ',' " , $users_array ) . " ' ) " ;
}
}
}
2019-12-20 15:06:58 +02:00
$profile_photo_where = '' ;
if ( $directory_data [ 'has_profile_photo' ] == 1 ) {
$profile_photo_where = " AND umm_general.um_value LIKE '%s:13: \" profile_photo \" ;b:1;%' " ;
}
$cover_photo_where = '' ;
if ( $directory_data [ 'has_cover_photo' ] == 1 ) {
$cover_photo_where = " AND umm_general.um_value LIKE '%s:11: \" cover_photo \" ;b:1;%' " ;
}
if ( ! UM () -> roles () -> um_user_can ( 'can_edit_everyone' ) ) {
2020-01-08 15:19:24 +02:00
if ( ! $this -> general_meta_joined ) {
$this -> joins [] = " LEFT JOIN { $wpdb -> prefix } um_metadata umm_general ON umm_general.user_id = u.ID " ;
2024-02-02 02:19:15 +02:00
2020-01-08 15:19:24 +02:00
$this -> general_meta_joined = true ;
}
2024-02-02 02:19:15 +02:00
// $profile_photo_where and $cover_photo_where are static in code.
2023-05-24 11:04:50 +03:00
$this -> where_clauses [] = " ( umm_general.um_key = 'um_member_directory_data' AND
2019-12-20 15:06:58 +02:00
umm_general.um_value LIKE '%s:14: \" account_status \" ;s:8: \" approved \" ;%' AND umm_general.um_value LIKE '%s:15: \" hide_in_members \" ;b:0;%' { $profile_photo_where } { $cover_photo_where } ) " ;
} else {
if ( ! empty ( $cover_photo_where ) || ! empty ( $profile_photo_where ) ) {
2020-01-08 15:19:24 +02:00
if ( ! $this -> general_meta_joined ) {
$this -> joins [] = " LEFT JOIN { $wpdb -> prefix } um_metadata umm_general ON umm_general.user_id = u.ID " ;
2024-02-02 02:19:15 +02:00
2020-01-08 15:19:24 +02:00
$this -> general_meta_joined = true ;
}
2024-02-02 02:19:15 +02:00
// $profile_photo_where and $cover_photo_where are static in code.
2020-01-08 15:19:24 +02:00
$this -> where_clauses [] = " ( umm_general.um_key = 'um_member_directory_data' { $profile_photo_where } { $cover_photo_where } ) " ;
2019-12-20 15:06:58 +02:00
}
}
if ( UM () -> roles () -> um_user_can ( 'can_view_all' ) ) {
$view_roles = um_user ( 'can_view_roles' );
if ( ! $view_roles ) {
$view_roles = array ();
2020-08-10 11:36:11 +03:00
} else {
$this -> roles_in_query = true ;
2019-12-20 15:06:58 +02:00
}
2020-01-08 15:19:24 +02:00
$this -> roles = array_merge ( $this -> roles , maybe_unserialize ( $view_roles ) );
2019-12-20 15:06:58 +02:00
}
if ( ! empty ( $directory_data [ 'roles' ] ) ) {
2020-01-08 15:19:24 +02:00
if ( ! empty ( $this -> roles ) ) {
$this -> roles = array_intersect ( $this -> roles , maybe_unserialize ( $directory_data [ 'roles' ] ) );
2019-12-20 15:06:58 +02:00
} else {
2020-01-08 15:19:24 +02:00
$this -> roles = array_merge ( $this -> roles , maybe_unserialize ( $directory_data [ 'roles' ] ) );
2019-12-20 15:06:58 +02:00
}
2020-07-23 16:06:59 +03:00
$this -> roles_in_query = true ;
2019-12-20 15:06:58 +02:00
}
2020-01-08 15:19:24 +02:00
if ( ! empty ( $this -> roles ) ) {
2024-02-02 02:19:15 +02:00
$this -> joins [] = $wpdb -> prepare ( " LEFT JOIN { $wpdb -> prefix } um_metadata umm_roles ON ( umm_roles.user_id = u.ID AND umm_roles.um_key = %s ) " , $wpdb -> get_blog_prefix ( $blog_id ) . 'capabilities' );
2019-12-20 15:06:58 +02:00
$roles_clauses = array ();
2020-01-08 15:19:24 +02:00
foreach ( $this -> roles as $role ) {
2024-02-02 02:19:15 +02:00
$roles_clauses [] = $wpdb -> prepare ( 'umm_roles.um_value LIKE %s' , '%"' . $wpdb -> esc_like ( $role ) . '"%' );
2019-12-20 15:06:58 +02:00
}
2024-02-02 02:19:15 +02:00
// $roles_clauses is pre-prepared.
2020-01-08 15:19:24 +02:00
$this -> where_clauses [] = '( ' . implode ( ' OR ' , $roles_clauses ) . ' )' ;
2019-12-20 15:06:58 +02:00
} else {
2020-07-23 16:06:59 +03:00
if ( ! $this -> roles_in_query && is_multisite () ) {
2019-12-20 15:06:58 +02:00
// select users who have capabilities for current blog
2024-02-02 02:19:15 +02:00
$this -> joins [] = $wpdb -> prepare ( " LEFT JOIN { $wpdb -> prefix } um_metadata umm_roles ON ( umm_roles.user_id = u.ID AND umm_roles.um_key = %s ) " , $wpdb -> get_blog_prefix ( $blog_id ) . 'capabilities' );
$this -> where_clauses [] = 'umm_roles.um_value IS NOT NULL' ;
2020-07-23 16:09:56 +03:00
} elseif ( $this -> roles_in_query ) {
2024-02-02 02:19:15 +02:00
$member_directory_response = array (
'pagination' => $this -> calculate_pagination ( $directory_data , 0 ),
'users' => array (),
'is_search' => $this -> is_search ,
);
$member_directory_response = apply_filters ( 'um_ajax_get_members_response' , $member_directory_response , $directory_data );
2020-07-23 16:06:59 +03:00
wp_send_json_success ( $member_directory_response );
2019-12-20 15:06:58 +02:00
}
}
2024-02-02 02:19:15 +02:00
// phpcs:disable WordPress.Security.NonceVerification -- verified via `UM()->check_ajax_nonce();`.
2019-12-20 15:06:58 +02:00
if ( ! empty ( $_POST [ 'search' ] ) ) {
2023-09-20 21:31:50 +03:00
$search_line = $this -> prepare_search ( $_POST [ 'search' ] );
2024-02-02 02:19:15 +02:00
// phpcs:enable WordPress.Security.NonceVerification -- verified via `UM()->check_ajax_nonce();`.
2023-09-20 21:31:50 +03:00
if ( ! empty ( $search_line ) ) {
$searches = array ();
2024-04-09 13:55:39 +03:00
$exclude_fields = get_post_meta ( $directory_id , '_um_search_exclude_fields' , true );
$include_fields = get_post_meta ( $directory_id , '_um_search_include_fields' , true );
$core_search = $this -> get_core_search_fields ();
if ( ! empty ( $include_fields ) ) {
$core_search = array_intersect ( $core_search , $include_fields );
}
if ( ! empty ( $exclude_fields ) ) {
$core_search = array_diff ( $core_search , $exclude_fields );
}
if ( ! empty ( $core_search ) ) {
foreach ( $core_search as $field ) {
$field = esc_sql ( $field );
// phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- $field is pre-escaped.
$searches [] = $wpdb -> prepare ( " u. { $field } LIKE %s " , '%' . $wpdb -> esc_like ( $search_line ) . '%' );
}
2023-09-20 21:31:50 +03:00
}
2019-12-20 15:06:58 +02:00
2023-09-20 21:31:50 +03:00
$core_search = implode ( ' OR ' , $searches );
2024-04-09 13:55:39 +03:00
if ( ! empty ( $core_search ) ) {
$core_search = ' OR ' . $core_search ;
}
2019-12-20 15:06:58 +02:00
2023-09-20 21:31:50 +03:00
$this -> joins [] = " LEFT JOIN { $wpdb -> prefix } um_metadata umm_search ON umm_search.user_id = u.ID " ;
2020-01-08 15:19:24 +02:00
2024-02-02 02:19:15 +02:00
$additional_search = apply_filters ( 'um_member_directory_meta_general_search_meta_query' , '' , $search_line );
2020-01-10 12:53:30 +02:00
2024-02-02 02:19:15 +02:00
$search_like_string = apply_filters ( 'um_member_directory_meta_search_like_type' , '%' . $wpdb -> esc_like ( $search_line ) . '%' , $search_line );
2020-01-29 15:53:39 +02:00
2024-04-09 13:55:39 +03:00
$custom_fields_sql = '' ;
2024-01-30 12:56:31 +02:00
if ( ! empty ( $exclude_fields ) ) {
2024-04-09 13:55:39 +03:00
$custom_fields_sql = " AND umm_search.um_key NOT IN (' " . implode ( " ',' " , $exclude_fields ) . " ') " ;
2024-01-30 12:56:31 +02:00
}
2024-02-07 12:07:48 +02:00
if ( ! empty ( $include_fields ) ) {
2024-04-09 13:55:39 +03:00
$custom_fields_sql = " AND umm_search.um_key IN (' " . implode ( " ',' " , $include_fields ) . " ') " ;
2024-02-07 12:07:48 +02:00
}
2024-04-09 13:55:39 +03:00
2024-02-02 02:19:15 +02:00
// phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- $core_search and $additional_search are pre-prepared.
2024-04-09 13:55:39 +03:00
$this -> where_clauses [] = $wpdb -> prepare ( " ( umm_search.um_value = %s OR umm_search.um_value LIKE %s OR umm_search.um_value LIKE %s { $core_search } { $additional_search } ) { $custom_fields_sql } " , $search_line , $search_like_string , '%' . $wpdb -> esc_like ( maybe_serialize ( ( string ) $search_line ) ) . '%' );
2019-12-20 15:06:58 +02:00
2023-09-20 21:31:50 +03:00
$this -> is_search = true ;
}
2019-12-20 15:06:58 +02:00
}
2024-02-02 02:19:15 +02:00
// Filters
2019-12-20 15:06:58 +02:00
$filter_query = array ();
if ( ! empty ( $directory_data [ 'search_fields' ] ) ) {
$search_filters = maybe_unserialize ( $directory_data [ 'search_fields' ] );
if ( ! empty ( $search_filters ) && is_array ( $search_filters ) ) {
2024-02-02 02:19:15 +02:00
// phpcs:ignore WordPress.Security.NonceVerification -- verified via `UM()->check_ajax_nonce();`.
2019-12-20 15:06:58 +02:00
$filter_query = array_intersect_key ( $_POST , array_flip ( $search_filters ) );
}
}
// added for user tags extension integration on individual tag page
$ignore_empty_filters = apply_filters ( 'um_member_directory_ignore_empty_filters' , false );
if ( ! empty ( $filter_query ) || $ignore_empty_filters ) {
$this -> is_search = true ;
$i = 1 ;
foreach ( $filter_query as $field => $value ) {
2021-06-29 02:51:54 +03:00
$field = sanitize_text_field ( $field );
2021-07-02 17:36:01 +03:00
if ( is_array ( $value ) ) {
$value = array_map ( 'sanitize_text_field' , $value );
} else {
$value = sanitize_text_field ( $value );
}
2021-06-29 02:51:54 +03:00
2019-12-20 15:06:58 +02:00
$attrs = UM () -> fields () -> get_field ( $field );
// skip private invisible fields
if ( ! um_can_view_field ( $attrs ) ) {
continue ;
}
2020-01-08 15:19:24 +02:00
$this -> handle_filter_query ( $directory_data , $field , $value , $i );
2019-12-20 15:06:58 +02:00
$i ++ ;
}
}
//unable default filter in case if we select other filters in frontend filters
2020-01-10 17:31:55 +02:00
//if ( empty( $this->custom_filters_in_query ) ) {
$default_filters = array ();
if ( ! empty ( $directory_data [ 'search_filters' ] ) ) {
$default_filters = maybe_unserialize ( $directory_data [ 'search_filters' ] );
}
2019-12-20 15:06:58 +02:00
2020-01-10 17:31:55 +02:00
if ( ! empty ( $default_filters ) ) {
$i = 1 ;
foreach ( $default_filters as $field => $value ) {
2019-12-20 15:06:58 +02:00
2020-01-10 17:31:55 +02:00
$this -> handle_filter_query ( $directory_data , $field , $value , $i , true );
2019-12-20 15:06:58 +02:00
2020-01-10 17:31:55 +02:00
$i ++ ;
2019-12-20 15:06:58 +02:00
}
}
2020-01-10 17:31:55 +02:00
//}
2019-12-20 15:06:58 +02:00
$order = 'ASC' ;
2024-02-02 02:19:15 +02:00
// phpcs:ignore WordPress.Security.NonceVerification -- verified via `UM()->check_ajax_nonce();`.
2021-06-29 02:51:54 +03:00
$sortby = ! empty ( $_POST [ 'sorting' ] ) ? sanitize_text_field ( $_POST [ 'sorting' ] ) : $directory_data [ 'sortby' ];
2024-02-02 02:19:15 +02:00
$sortby = ( 'other' === $sortby ) ? $directory_data [ 'sortby_custom' ] : $sortby ;
2019-12-20 15:06:58 +02:00
2020-01-13 10:18:30 +02:00
$custom_sort = array ();
2020-01-29 13:22:03 +02:00
if ( ! empty ( $directory_data [ 'sorting_fields' ] ) ) {
$sorting_fields = maybe_unserialize ( $directory_data [ 'sorting_fields' ] );
foreach ( $sorting_fields as $field ) {
if ( is_array ( $field ) ) {
2024-02-02 02:19:15 +02:00
$field_keys = array_keys ( $field );
2020-01-29 13:22:03 +02:00
$custom_sort [] = $field_keys [ 0 ];
}
2020-01-13 10:18:30 +02:00
}
}
2022-06-09 17:17:15 +03:00
$numeric_sorting_keys = array ();
if ( ! empty ( UM () -> builtin () -> saved_fields ) ) {
foreach ( UM () -> builtin () -> saved_fields as $key => $data ) {
2023-06-12 13:20:00 +03:00
if ( '_um_last_login' === $key ) {
2022-06-09 17:17:15 +03:00
continue ;
}
if ( isset ( $data [ 'type' ] ) && 'number' === $data [ 'type' ] ) {
if ( array_key_exists ( $key . '_desc' , $this -> sort_fields ) ) {
$numeric_sorting_keys [] = $key . '_desc' ;
}
if ( array_key_exists ( $key . '_asc' , $this -> sort_fields ) ) {
$numeric_sorting_keys [] = $key . '_asc' ;
}
}
}
}
2019-12-20 15:06:58 +02:00
// handle sorting options
// sort members by
2024-02-02 02:19:15 +02:00
if ( $sortby === $directory_data [ 'sortby_custom' ] || in_array ( $sortby , $custom_sort , true ) ) {
2023-06-12 13:20:00 +03:00
$custom_sort_order = ! empty ( $directory_data [ 'sortby_custom_order' ] ) ? $directory_data [ 'sortby_custom_order' ] : 'ASC' ;
2019-12-20 15:06:58 +02:00
2024-02-02 02:19:15 +02:00
$this -> joins [] = $wpdb -> prepare ( " LEFT JOIN { $wpdb -> prefix } um_metadata umm_sort ON ( umm_sort.user_id = u.ID AND umm_sort.um_key = %s ) " , $sortby );
2019-12-20 15:06:58 +02:00
2023-06-12 13:20:00 +03:00
$meta_query = new \WP_Meta_Query ();
$custom_sort_type = ! empty ( $directory_data [ 'sortby_custom_type' ] ) ? $meta_query -> get_cast_for_type ( $directory_data [ 'sortby_custom_type' ] ) : 'CHAR' ;
2023-05-24 15:45:27 +03:00
if ( ! empty ( $directory_data [ 'sorting_fields' ] ) ) {
2023-06-12 13:20:00 +03:00
// phpcs:ignore WordPress.Security.NonceVerification -- already verified here
2023-05-24 15:45:27 +03:00
$sorting = sanitize_text_field ( $_POST [ 'sorting' ] );
2023-06-12 13:20:00 +03:00
$sorting_fields = maybe_unserialize ( $directory_data [ 'sorting_fields' ] );
2023-05-24 15:45:27 +03:00
foreach ( $sorting_fields as $field ) {
if ( isset ( $field [ $sorting ] ) ) {
2023-06-12 13:20:00 +03:00
$custom_sort_type = ! empty ( $field [ 'type' ] ) ? $meta_query -> get_cast_for_type ( $field [ 'type' ] ) : 'CHAR' ;
2023-05-24 15:45:27 +03:00
$custom_sort_order = $field [ 'order' ];
}
}
}
2023-06-12 13:20:00 +03:00
/** This filter is documented in includes/core/class-member-directory.php */
2023-05-24 11:04:50 +03:00
$custom_sort_type = apply_filters ( 'um_member_directory_custom_sorting_type' , $custom_sort_type , $sortby , $directory_data );
2024-02-05 10:54:54 +02:00
$custom_sort_type = esc_sql ( $custom_sort_type );
$custom_sort_type = in_array ( strtoupper ( $custom_sort_type ), $this -> sort_data_types , true ) ? $custom_sort_type : 'CHAR' ;
2023-05-24 11:04:50 +03:00
2024-02-02 02:19:15 +02:00
$custom_sort_order = esc_sql ( $custom_sort_order );
2024-02-05 10:54:54 +02:00
$custom_sort_order = in_array ( strtoupper ( $custom_sort_order ), array ( 'ASC' , 'DESC' ), true ) ? $custom_sort_order : 'ASC' ;
2024-02-02 02:19:15 +02:00
$this -> sql_order = " ORDER BY CAST( umm_sort.um_value AS { $custom_sort_type } ) { $custom_sort_order } " ;
2019-12-20 15:06:58 +02:00
2024-02-02 02:19:15 +02:00
} elseif ( count ( $numeric_sorting_keys ) && in_array ( $sortby , $numeric_sorting_keys , true ) ) {
2022-06-09 17:17:15 +03:00
2024-02-02 02:19:15 +02:00
if ( false !== strpos ( $sortby , '_desc' ) ) {
2022-06-09 17:17:15 +03:00
$sortby = str_replace ( '_desc' , '' , $sortby );
2024-02-02 02:19:15 +02:00
$order = 'DESC' ;
2022-06-09 17:17:15 +03:00
}
2024-02-02 02:19:15 +02:00
if ( false !== strpos ( $sortby , '_asc' ) ) {
2022-06-09 17:17:15 +03:00
$sortby = str_replace ( '_asc' , '' , $sortby );
2024-02-02 02:19:15 +02:00
$order = 'ASC' ;
2022-06-09 17:17:15 +03:00
}
2024-02-02 02:19:15 +02:00
$order = esc_sql ( $order );
2024-02-05 10:54:54 +02:00
$order = in_array ( strtoupper ( $order ), array ( 'ASC' , 'DESC' ), true ) ? $order : 'ASC' ;
2024-02-02 02:19:15 +02:00
$this -> joins [] = $wpdb -> prepare ( " LEFT JOIN { $wpdb -> prefix } um_metadata umm_sort ON ( umm_sort.user_id = u.ID AND umm_sort.um_key = %s ) " , $sortby );
2022-06-09 17:17:15 +03:00
$this -> sql_order = " ORDER BY CAST( umm_sort.um_value AS SIGNED ) { $order } , u.user_registered DESC " ;
2024-02-02 02:19:15 +02:00
} elseif ( 'username' === $sortby ) {
2021-07-02 17:36:01 +03:00
2024-02-02 02:19:15 +02:00
$order = esc_sql ( $order );
2024-02-05 10:54:54 +02:00
$order = in_array ( strtoupper ( $order ), array ( 'ASC' , 'DESC' ), true ) ? $order : 'ASC' ;
2021-07-02 17:36:01 +03:00
$this -> sql_order = " ORDER BY u.user_login { $order } " ;
2024-02-02 02:19:15 +02:00
} elseif ( 'display_name' === $sortby ) {
2019-12-20 15:06:58 +02:00
$display_name = UM () -> options () -> get ( 'display_name' );
2024-02-02 02:19:15 +02:00
if ( 'username' === $display_name ) {
2019-12-20 15:06:58 +02:00
2024-02-02 02:19:15 +02:00
$order = esc_sql ( $order );
2024-02-05 10:54:54 +02:00
$order = in_array ( strtoupper ( $order ), array ( 'ASC' , 'DESC' ), true ) ? $order : 'ASC' ;
2019-12-20 15:06:58 +02:00
$this -> sql_order = " ORDER BY u.user_login { $order } " ;
} else {
$this -> joins [] = " LEFT JOIN { $wpdb -> prefix } um_metadata umm_sort ON ( umm_sort.user_id = u.ID AND umm_sort.um_key = 'full_name' ) " ;
2024-02-02 02:19:15 +02:00
$order = esc_sql ( $order );
2024-02-05 10:54:54 +02:00
$order = in_array ( strtoupper ( $order ), array ( 'ASC' , 'DESC' ), true ) ? $order : 'ASC' ;
2019-12-20 15:06:58 +02:00
$this -> sql_order = " ORDER BY CAST( umm_sort.um_value AS CHAR ) { $order } , u.display_name { $order } " ;
}
2024-02-02 02:19:15 +02:00
} elseif ( in_array ( $sortby , array ( 'last_name' , 'first_name' , 'nickname' ), true ) ) {
2019-12-20 15:06:58 +02:00
2024-02-02 02:19:15 +02:00
$this -> joins [] = $wpdb -> prepare ( " LEFT JOIN { $wpdb -> prefix } um_metadata umm_sort ON ( umm_sort.user_id = u.ID AND umm_sort.um_key = %s ) " , $sortby );
2019-12-20 15:06:58 +02:00
2024-02-02 02:19:15 +02:00
$order = esc_sql ( $order );
2024-02-05 10:54:54 +02:00
$order = in_array ( strtoupper ( $order ), array ( 'ASC' , 'DESC' ), true ) ? $order : 'ASC' ;
2019-12-20 15:06:58 +02:00
$this -> sql_order = " ORDER BY CAST( umm_sort.um_value AS CHAR ) { $order } " ;
2023-12-01 02:17:37 +02:00
} elseif ( 'last_login' === $sortby ) {
2019-12-20 15:06:58 +02:00
2023-12-01 02:17:37 +02:00
$this -> joins [] = " LEFT JOIN { $wpdb -> prefix } um_metadata umm_sort ON ( umm_sort.user_id = u.ID AND umm_sort.um_key = '_um_last_login' ) " ;
2024-04-24 02:42:44 +03:00
$this -> joins [] = " LEFT JOIN { $wpdb -> prefix } um_metadata umm_show_login ON ( umm_show_login.user_id = u.ID AND umm_show_login.um_key = 'um_show_last_login' ) " ;
$this -> sql_order = $wpdb -> prepare ( ' ORDER BY CASE ISNULL(NULLIF(umm_show_login.um_value,%s)) WHEN 0 THEN %s ELSE CAST( umm_sort.um_value AS DATETIME ) END DESC ' , 'a:1:{i:0;s:3:"yes";}' , '1970-01-01 00:00:00' );
2019-12-20 15:06:58 +02:00
2024-02-02 02:19:15 +02:00
} elseif ( 'last_first_name' === $sortby ) {
2019-12-20 15:06:58 +02:00
$this -> joins [] = " LEFT JOIN { $wpdb -> prefix } um_metadata umm_sort ON ( umm_sort.user_id = u.ID AND umm_sort.um_key = 'last_name' ) " ;
$this -> joins [] = " LEFT JOIN { $wpdb -> prefix } um_metadata umm_sort2 ON ( umm_sort2.user_id = u.ID AND umm_sort2.um_key = 'first_name' ) " ;
2024-02-02 02:19:15 +02:00
$this -> sql_order = ' ORDER BY CAST( umm_sort.um_value AS CHAR ) ASC, CAST( umm_sort2.um_value AS CHAR ) ASC ' ;
2019-12-20 15:06:58 +02:00
2024-02-02 02:19:15 +02:00
} elseif ( 'random' === $sortby ) {
2019-12-20 15:06:58 +02:00
if ( um_is_session_started () === false ) {
2024-02-02 02:19:15 +02:00
// phpcs:ignore WordPress.PHP.NoSilencedErrors.Discouraged
2019-12-20 15:06:58 +02:00
@ session_start ();
}
// Reset seed on load of initial
2024-02-02 02:19:15 +02:00
// phpcs:ignore WordPress.Security.NonceVerification -- verified via `UM()->check_ajax_nonce();`.
2019-12-20 15:06:58 +02:00
if ( empty ( $_REQUEST [ 'directory_id' ] ) && isset ( $_SESSION [ 'um_member_directory_seed' ] ) ) {
unset ( $_SESSION [ 'um_member_directory_seed' ] );
}
// Get seed from session variable if it exists
$seed = false ;
if ( isset ( $_SESSION [ 'um_member_directory_seed' ] ) ) {
2024-02-05 10:54:54 +02:00
$seed = ( int ) $_SESSION [ 'um_member_directory_seed' ];
2019-12-20 15:06:58 +02:00
}
// Set new seed if none exists
if ( ! $seed ) {
2024-02-02 02:19:15 +02:00
$seed = wp_rand ();
2019-12-20 15:06:58 +02:00
$_SESSION [ 'um_member_directory_seed' ] = $seed ;
}
2024-02-02 02:19:15 +02:00
$seed = esc_sql ( $seed );
2020-01-14 22:22:11 +02:00
$this -> sql_order = 'ORDER BY RAND(' . $seed . ')' ;
2019-12-20 15:06:58 +02:00
} else {
2024-02-02 02:19:15 +02:00
if ( false !== strpos ( $sortby , '_desc' ) ) {
2019-12-20 15:06:58 +02:00
$sortby = str_replace ( '_desc' , '' , $sortby );
2024-02-02 02:19:15 +02:00
$order = 'DESC' ;
2019-12-20 15:06:58 +02:00
}
2024-02-02 02:19:15 +02:00
if ( false !== strpos ( $sortby , '_asc' ) ) {
2019-12-20 15:06:58 +02:00
$sortby = str_replace ( '_asc' , '' , $sortby );
2024-02-02 02:19:15 +02:00
$order = 'ASC' ;
2019-12-20 15:06:58 +02:00
}
$metakeys = get_option ( 'um_usermeta_fields' , array () );
2024-02-19 13:29:49 +02:00
if ( in_array ( $sortby , $this -> core_users_fields , true ) ) {
2024-02-02 02:19:15 +02:00
$sortby = esc_sql ( $sortby );
$order = esc_sql ( $order );
2024-02-05 10:54:54 +02:00
$order = in_array ( strtoupper ( $order ), array ( 'ASC' , 'DESC' ), true ) ? $order : 'ASC' ;
2019-12-20 15:06:58 +02:00
$this -> sql_order = " ORDER BY u. { $sortby } { $order } " ;
2024-02-19 13:29:49 +02:00
} elseif ( in_array ( $sortby , $metakeys , true ) ) {
$this -> joins [] = $wpdb -> prepare ( " LEFT JOIN { $wpdb -> prefix } um_metadata umm_sort ON ( umm_sort.user_id = u.ID AND umm_sort.um_key = %s ) " , $sortby );
$order = esc_sql ( $order );
$order = in_array ( strtoupper ( $order ), array ( 'ASC' , 'DESC' ), true ) ? $order : 'ASC' ;
$this -> sql_order = " ORDER BY CAST( umm_sort.um_value AS CHAR ) { $order } " ;
2019-12-20 15:06:58 +02:00
}
}
2020-01-08 15:19:24 +02:00
$this -> sql_order = apply_filters ( 'um_modify_sortby_parameter_meta' , $this -> sql_order , $sortby );
2019-12-20 15:06:58 +02:00
$profiles_per_page = $directory_data [ 'profiles_per_page' ];
2024-12-17 21:53:18 +02:00
if ( wp_is_mobile () && isset ( $directory_data [ 'profiles_per_page_mobile' ] ) ) {
2019-12-20 15:06:58 +02:00
$profiles_per_page = $directory_data [ 'profiles_per_page_mobile' ];
}
$query_number = ( ! empty ( $directory_data [ 'max_users' ] ) && $directory_data [ 'max_users' ] <= $profiles_per_page ) ? $directory_data [ 'max_users' ] : $profiles_per_page ;
2024-02-02 02:19:15 +02:00
$query_paged = ! empty ( $_POST [ 'page' ] ) ? absint ( $_POST [ 'page' ] ) : 1 ; // phpcs:ignore WordPress.Security.NonceVerification -- verified via `UM()->check_ajax_nonce();`.
2019-12-20 15:06:58 +02:00
$number = $query_number ;
2024-02-02 02:19:15 +02:00
if ( ! empty ( $directory_data [ 'max_users' ] ) && $query_paged * $query_number > $directory_data [ 'max_users' ] ) {
$number = ( $query_paged * $query_number - ( $query_paged * $query_number - $directory_data [ 'max_users' ] ) ) % $query_number ;
2019-12-20 15:06:58 +02:00
}
// limit
if ( isset ( $query_number ) && $query_number > 0 ) {
$this -> sql_limit .= $wpdb -> prepare ( 'LIMIT %d, %d' , $query_number * ( $query_paged - 1 ), $number );
}
do_action ( 'um_pre_users_query' , $this , $directory_data , $sortby );
2024-02-02 02:19:15 +02:00
$sql_select = esc_sql ( $this -> select );
$sql_having = esc_sql ( $this -> having );
$sql_join = implode ( ' ' , $this -> joins );
$sql_where = implode ( ' AND ' , $this -> where_clauses );
$sql_where = ! empty ( $sql_where ) ? 'AND ' . $sql_where : '' ;
2020-01-08 15:19:24 +02:00
2024-04-18 14:09:34 +03:00
$query = array (
'select' => $this -> select ,
'sql_where' => $sql_where ,
'having' => $this -> having ,
'sql_limit' => $this -> sql_limit ,
);
/** This filter is documented in includes/core/class-member-directory.php */
do_action ( 'um_user_before_query' , $query , $this );
2021-01-20 15:26:01 +02:00
/*
*
* SQL_CALC_FOUND_ROWS is deprecated as of MySQL 8.0.17
* https://core.trac.wordpress.org/ticket/47280
*
* */
2019-12-20 15:06:58 +02:00
$user_ids = $wpdb -> get_col (
2020-01-29 13:22:03 +02:00
" SELECT SQL_CALC_FOUND_ROWS DISTINCT u.ID
2024-02-02 02:19:15 +02:00
{ $sql_select }
2019-12-20 15:06:58 +02:00
FROM { $wpdb -> users } AS u
{ $sql_join }
2020-01-08 15:19:24 +02:00
WHERE 1=1 { $sql_where }
2024-02-02 02:19:15 +02:00
{ $sql_having }
2019-12-20 15:06:58 +02:00
{ $this -> sql_order }
{ $this -> sql_limit } "
);
$total_users = ( int ) $wpdb -> get_var ( 'SELECT FOUND_ROWS()' );
2021-01-19 13:54:31 +02:00
/**
2023-06-12 15:22:35 +03:00
* Filters the member directory query result when um_usermeta table is used.
2021-01-19 13:54:31 +02:00
*
2023-06-12 15:22:35 +03:00
* @since 2.1.3
* @hook um_prepare_user_results_array_meta
*
* @param {array} $user_ids Members Query Result.
* @param {array} $query_args Query arguments.
*
* @return {array} Query result.
*
* @example <caption>Remove some users where ID equals 10 and 12 from query.</caption>
* function my_custom_um_prepare_user_results_array_meta( $user_ids, $query_args ) {
* $user_ids = array_diff( $user_ids, array( 10, 12 ) );
2021-01-19 13:54:31 +02:00
* return $user_ids;
* }
2023-06-12 15:22:35 +03:00
* add_filter( 'um_prepare_user_results_array_meta', 'my_custom_um_prepare_user_results_array', 10, 2 );
2021-01-19 13:54:31 +02:00
*/
$user_ids = apply_filters ( 'um_prepare_user_results_array_meta' , $user_ids , $query );
2019-12-20 15:06:58 +02:00
$pagination_data = $this -> calculate_pagination ( $directory_data , $total_users );
$sizes = UM () -> options () -> get ( 'cover_thumb_sizes' );
2025-09-06 14:42:15 +03:00
// Ensure we have valid sizes array and handle case when only one size is defined
if ( ! is_array ( $sizes ) || empty ( $sizes ) ) {
$sizes = array ( 300 ); // fallback to default
}
2019-12-20 15:06:58 +02:00
2025-09-06 14:42:15 +03:00
// For mobile, use second size if available, otherwise use first size
$available_mobile = isset ( $sizes [ 1 ] ) ? $sizes [ 1 ] : $sizes [ 0 ];
$this -> cover_size = wp_is_mobile () ? $available_mobile : end ( $sizes );
2019-12-20 15:06:58 +02:00
2024-02-02 02:19:15 +02:00
$avatar_size = UM () -> options () -> get ( 'profile_photosize' );
2019-12-20 15:06:58 +02:00
$this -> avatar_size = str_replace ( 'px' , '' , $avatar_size );
$users = array ();
foreach ( $user_ids as $user_id ) {
$users [] = $this -> build_user_card_data ( $user_id , $directory_data );
}
um_reset_user ();
// end of user card
2024-02-02 02:19:15 +02:00
$member_directory_response = array (
'pagination' => $pagination_data ,
'users' => $users ,
'is_search' => $this -> is_search ,
);
$member_directory_response = apply_filters ( 'um_ajax_get_members_response' , $member_directory_response , $directory_data );
2020-01-24 14:59:47 +02:00
wp_send_json_success ( $member_directory_response );
2019-12-20 15:06:58 +02:00
}
}
2021-06-29 02:51:54 +03:00
}