CVE-2020-6859 (PHP, published 2020-01-13T17:15Z)
Description: value: Multiple Insecure Direct Object Reference vulnerabilities in includes/core/class-files.php in the Ultimate Member plugin through 2.1.2 for WordPress allow remote attackers to change other users' profiles and cover photos via a modified user_id parameter. This is related to ajax_image_upload and ajax_resize_image.
Severity: MEDIUM (CVSS v3 base score: 5.3)
Weakness: CWE-639 \u2014 Authorization Bypass Through User-Controlled Key
The system's authorization functionality does not prevent one user from gaining access to another user's data or record by modifying the key value identifying the data.
Repository: https://github.com/ultimatemember/ultimatemember
Files changed:
includes/admin/core/class-admin-settings.php
includes/core/class-files.php
includes/core/class-member-directory-meta.php
includes/core/class-member-directory.php
templates/members.php
Commit message: - fixed vulnerability with uploading cover/profile photo for other user ID;
- re-written member directory meta queries;
- fixed search line additional slashes;


Vulnerable code:
		var $sql_where = '';
		var $meta_iteration = 1;
						$this->sql_where .= " AND u.ID IN ( '" . implode( "','", $users_array ) . "' )";
				$this->joins[] = "LEFT JOIN {$wpdb->prefix}um_metadata umm_general ON umm_general.user_id = u.ID";

				$this->sql_where .= " AND ( umm_general.um_key = 'um_member_directory_data' AND 
					$this->joins[] = "LEFT JOIN {$wpdb->prefix}um_metadata umm_general ON umm_general.user_id = u.ID";

					$this->sql_where .= " AND ( umm_general.um_key = 'um_member_directory_data'{$profile_photo_where}{$cover_photo_where} )";
			$roles = array();
				$roles = array_merge( $roles, maybe_unserialize( $view_roles ) );
				if ( ! empty( $roles ) ) {
					$roles = array_intersect( $roles, maybe_unserialize( $directory_data['roles'] ) );
					$roles = array_merge( $roles, maybe_unserialize( $directory_data['roles'] ) );
			if ( ! empty( $roles ) ) {
				foreach ( $roles as $role ) {
					$roles_clauses[] = "umm_roles.um_value LIKE '%\"" . $role . "\"%'";
				$roles_search = implode( ' OR ', $roles_clauses );

				$this->sql_where .= " AND ( {$roles_search} )";
					$this->sql_where .= " AND umm_roles.um_value IS NOT NULL ";
					$searches[] = $wpdb->prepare( "u.{$field} LIKE %s", '%' . trim( $_POST['search'] ) . '%' );
				$this->sql_where .= " AND ( umm_search.um_value = '" . trim( $_POST['search'] ) . "' OR umm_search.um_value LIKE '%" . trim( $_POST['search'] ) . "%' OR umm_search.um_value LIKE '%" . trim( serialize( strval( $_POST['search'] ) ) ) . "%' OR {$core_search})";
					switch ( $field ) {
						default:

							$filter_type = $this->filter_types[ $field ];

							/**
							 * UM hook
							 *
							 * @type filter
							 * @title um_query_args_{$field}__filter
							 * @description Change field's query for search at Members Directory
							 * @input_vars
							 * [{"var":"$field_query","type":"array","desc":"Field query"}]
							 * @change_log
							 * ["Since: 2.0"]
							 * @usage
							 * <?php add_filter( 'um_query_args_{$field}__filter', 'function_name', 10, 1 ); ?>
							 * @example
							 * <?php
							 * add_filter( 'um_query_args_{$field}__filter', 'my_query_args_filter', 10, 1 );
							 * function my_query_args_filter( $field_query ) {
							 *     // your code here
							 *     return $field_query;
							 * }
							 * ?>
							 */
							$field_query = apply_filters( "um_query_args_{$field}__filter", false, $field, $value, $filter_type );

							if ( ! $field_query ) {

								switch ( $filter_type ) {
									default:

										$field_query = apply_filters( "um_query_args_{$field}_{$filter_type}__filter", false, $field, $value, $filter_type );

										break;
									case 'text':

										$this->joins[] = "LEFT JOIN {$wpdb->prefix}um_metadata umm" . $i . " ON umm" . $i . ".user_id = u.ID";

										$value = trim( stripslashes( $value ) );

										$this->sql_where .= " AND ( umm" . $i . ".um_key = '{$field}' AND umm" . $i . ".um_value = '{$value}' )";

										$this->custom_filters_in_query[ $field ] = $value;

										break;

									case 'select':
										if ( is_array( $value ) ) {

											$this->joins[] = "LEFT JOIN {$wpdb->prefix}um_metadata umm" . $i . " ON umm" . $i . ".user_id = u.ID";

											$values_array = array();
											foreach ( $value as $single_val ) {
												$single_val = stripslashes( $single_val );

												$values_array[] = "umm" . $i . ".um_value LIKE '%\"" . trim( $single_val ) . "\"%'";
												$values_array[] = "umm" . $i . ".um_value LIKE '%" . serialize( strval( trim( $single_val ) ) ) . "%'";
												$values_array[] = "umm" . $i . ".um_value = '" . trim( $single_val ) . "'";

												if ( is_numeric( $single_val ) ) {
													$values_array[] = "umm" . $i . ".um_value LIKE '%" . serialize( intval( trim( $single_val ) ) ) . "%'";
												}
											}

											$values = implode( ' OR ', $values_array );

											$this->sql_where .= " AND ( umm" . $i . ".um_key = '{$field}' AND ( {$values} ) )";
										}

										$this->custom_filters_in_query[ $field ] = $value;

										break;
									case 'slider':

										$this->joins[] = "LEFT JOIN {$wpdb->prefix}um_metadata umm" . $i . " ON umm" . $i . ".user_id = u.ID";

										$min = min( $value );
										$max = max( $value );

										$this->sql_where .= " AND ( umm" . $i . ".um_key = '{$field}' AND umm" . $i . ".um_value BETWEEN {$min} AND {$max} )";

										$this->custom_filters_in_query[ $field ] = $value;

										break;
									case 'datepicker':

										$offset = 0;
										if ( isset( $_POST['gmt_offset'] ) && is_numeric( $_POST['gmt_offset'] ) ) {
											$offset = (int) $_POST['gmt_offset'];
										}

										$from_date = (int) min( $value ) + ( $offset * HOUR_IN_SECONDS ); // client time zone offset
										$to_date   = (int) max( $value ) + ( $offset * HOUR_IN_SECONDS ) + DAY_IN_SECONDS - 1; // time 23:59
										$from_date = date( 'Y/m/d', $from_date );
										$to_date = date( 'Y/m/d', $to_date );


										$this->joins[] = "LEFT JOIN {$wpdb->prefix}um_metadata umm" . $i . " ON umm" . $i . ".user_id = u.ID";

										$this->sql_where .= " AND ( umm" . $i . ".um_key = '{$field}' AND umm" . $i . ".um_value BETWEEN {$from_date} AND {$to_date} )";

										$this->custom_filters_in_query[ $field ] = array( $from_date, $to_date );

										break;
									case 'timepicker':

										if ( $value[0] == $value[1] ) {
											$this->joins[] = "LEFT JOIN {$wpdb->prefix}um_metadata umm" . $i . " ON umm" . $i . ".user_id = u.ID";
											$this->sql_where .= " AND ( umm" . $i . ".um_key = '{$field}' AND umm" . $i . ".um_value = '{$value[0]}' )";
										} else {
											$this->joins[] = "LEFT JOIN {$wpdb->prefix}um_metadata umm" . $i . " ON umm" . $i . ".user_id = u.ID";
											$this->sql_where .= " AND ( umm" . $i . ".um_key = '{$field}' AND CAST( umm" . $i . ".um_value AS TIME ) BETWEEN {$value[0]} AND {$value[1]} )";
										}

										$this->custom_filters_in_query[ $field ] = $value;

										break;
								}

							}

							if ( ! empty( $field_query ) && $field_query !== true ) {
								$this->query_args['meta_query'] = array_merge( $this->query_args['meta_query'], array( $field_query ) );
							}

							break;
						case 'role':
							$value = array_map( 'strtolower', $value );

							if ( ! empty( $roles ) || is_multisite() ) {
								$roles_clauses = array();
								foreach ( $value as $role ) {
									$roles_clauses[] = "umm_roles.um_value LIKE '%\"" . $role . "\"%'";
								}

								$roles_search = implode( ' OR ', $roles_clauses );

								$this->sql_where .= " AND ( {$roles_search} )";
							} else {
								$this->joins[] = "LEFT JOIN {$wpdb->prefix}um_metadata umm_roles ON ( umm_roles.user_id = u.ID AND umm_roles.um_key = '" . $wpdb->get_blog_prefix( $blog_id ) . "capabilities' )";

								$roles = $value;

								$roles_clauses = array();
								foreach ( $value as $role ) {
									$roles_clauses[] = "umm_roles.um_value LIKE '%\"" . $role . "\"%'";
								}

								$roles_search = implode( ' OR ', $roles_clauses );

								$this->sql_where .= " AND ( {$roles_search} )";
							}

							$this->custom_filters_in_query[ $field ] = $value;

							break;
						case 'birth_date':

							$from_date = date( 'Y/m/d', mktime( 0,0,0, date( 'm', time() ), date( 'd', time() ), date( 'Y', time() - min( $value ) * YEAR_IN_SECONDS ) ) );
							$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 ) ) );

							$this->joins[] = "LEFT JOIN {$wpdb->prefix}um_metadata umm" . $i . " ON umm" . $i . ".user_id = u.ID";

							$this->sql_where .= " AND ( umm" . $i . ".um_key = 'birth_date' AND umm" . $i . ".um_value BETWEEN {$from_date} AND {$to_date} )";

							$this->custom_filters_in_query[ $field ] = array( $to_date, $from_date );

							break;
						case 'user_registered':

							$offset = 0;
							if ( isset( $_POST['gmt_offset'] ) && is_numeric( $_POST['gmt_offset'] ) ) {
								$offset = (int) $_POST['gmt_offset'];
							}

							$from_date = date( 'Y-m-d H:s:i', strtotime( date( 'Y-m-d H:s:i', min( $value ) ) . "+$offset hours" ) );
							$to_date = date( 'Y-m-d H:s:i', strtotime( date( 'Y-m-d H:s:i', max( $value ) ) . "+$offset hours" ) );

							$this->sql_where .= " AND ( u.user_registered BETWEEN {$from_date} AND {$to_date} )";

							$this->custom_filters_in_query[ $field ] = $value;

							break;
						case 'last_login':

							$offset = 0;
							if ( isset( $_POST['gmt_offset'] ) && is_numeric( $_POST['gmt_offset'] ) ) {
								$offset = (int) $_POST['gmt_offset'];
							}

							$from_date = (int) min( $value ) + ( $offset * HOUR_IN_SECONDS ); // client time zone offset
							$to_date   = (int) max( $value ) + ( $offset * HOUR_IN_SECONDS ) + DAY_IN_SECONDS - 1; // time 23:59

							$this->joins[] = "LEFT JOIN {$wpdb->prefix}um_metadata umm" . $i . " ON umm" . $i . ".user_id = u.ID";

							$this->sql_where .= " AND ( umm" . $i . ".um_key = '_um_last_login' AND umm" . $i . ".um_value BETWEEN {$from_date} AND {$to_date} )";

							$this->custom_filters_in_query[ $field ] = $value;

							break;
					}
				$gmt_offset = get_post_meta( $directory_data['form_id'], '_um_search_filters_gmt', true );

						//unable default filter in case if we select other value in frontend filters
		//				if ( in_array( $field, array_keys( $this->custom_filters_in_query ) ) ) {
		//					continue;
		//				}

						switch ( $field ) {
							default:

								$filter_type = $this->filter_types[ $field ];

								/**
								 * UM hook
								 *
								 * @type filter
								 * @title um_query_args_{$field}__filter
								 * @description Change field's query for search at Members Directory
								 * @input_vars
								 * [{"var":"$field_query","type":"array","desc":"Field query"}]
								 * @change_log
								 * ["Since: 2.0"]
								 * @usage
								 * <?php add_filter( 'um_query_args_{$field}__filter', 'function_name', 10, 1 ); ?>
								 * @example
								 * <?php
								 * add_filter( 'um_query_args_{$field}__filter', 'my_query_args_filter', 10, 1 );
								 * function my_query_args_filter( $field_query ) {
								 *     // your code here
								 *     return $field_query;
								 * }
								 * ?>
								 */
								$field_query = apply_filters( "um_query_args_{$field}__filter", false, $field, $value, $filter_type );

								if ( ! $field_query ) {

									switch ( $filter_type ) {
										default:

											$field_query = apply_filters( "um_query_args_{$field}_{$filter_type}__filter", false, $field, $value, $filter_type );

											break;
										case 'text':

											$this->joins[] = "LEFT JOIN {$wpdb->prefix}um_metadata ummd" . $i . " ON ummd" . $i . ".user_id = u.ID";

											$value = trim( stripslashes( $value ) );

											$this->sql_where .= " AND ( ummd" . $i . ".um_key = '{$field}' AND ummd" . $i . ".um_value = '{$value}' )";

											$this->custom_filters_in_query[ $field ] = $value;

											break;
										case 'select':
											if ( ! is_array( $value ) ) {
												$value = array( $value );
											}

											$this->joins[] = "LEFT JOIN {$wpdb->prefix}um_metadata ummd" . $i . " ON ummd" . $i . ".user_id = u.ID";

											$values_array = array();
											foreach ( $value as $single_val ) {
												$single_val = stripslashes( $single_val );

												$values_array[] = "ummd" . $i . ".um_value LIKE '%\"" . trim( $single_val ) . "\"%'";
												$values_array[] = "ummd" . $i . ".um_value LIKE '%" . serialize( strval( trim( $single_val ) ) ) . "%'";
												$values_array[] = "ummd" . $i . ".um_value = '" . trim( $single_val ) . "'";

												if ( is_numeric( $single_val ) ) {
													$values_array[] = "ummd" . $i . ".um_value LIKE '%" . serialize( intval( trim( $single_val ) ) ) . "%'";
												}
											}

											$values = implode( ' OR ', $values_array );

											$this->sql_where .= " AND ( ummd" . $i . ".um_key = '{$field}' AND ( {$values} ) )";

											break;
										case 'slider':

											$this->joins[] = "LEFT JOIN {$wpdb->prefix}um_metadata ummd" . $i . " ON ummd" . $i . ".user_id = u.ID";

											$min = min( $value );
											$max = max( $value );

											$this->sql_where .= " AND ( ummd" . $i . ".um_key = '{$field}' AND ummd" . $i . ".um_value BETWEEN {$min} AND {$max} )";

											break;
										case 'datepicker':

											$offset = 0;
											if ( is_numeric( $gmt_offset ) ) {
												$offset = $gmt_offset;
											}

											$from_date = (int) min( $value ) + ( $offset * HOUR_IN_SECONDS ); // client time zone offset
											$to_date   = (int) max( $value ) + ( $offset * HOUR_IN_SECONDS ) + DAY_IN_SECONDS - 1; // time 23:59
											$from_date = date( 'Y/m/d', $from_date );
											$to_date = date( 'Y/m/d', $to_date );


											$this->joins[] = "LEFT JOIN {$wpdb->prefix}um_metadata ummd" . $i . " ON ummd" . $i . ".user_id = u.ID";

											$this->sql_where .= " AND ( ummd" . $i . ".um_key = '{$field}' AND ummd" . $i . ".um_value BETWEEN {$from_date} AND {$to_date} )";

											break;
										case 'timepicker':

											if ( $value[0] == $value[1] ) {
												$this->joins[] = "LEFT JOIN {$wpdb->prefix}um_metadata ummd" . $i . " ON ummd" . $i . ".user_id = u.ID";
												$this->sql_where .= " AND ( ummd" . $i . ".um_key = '{$field}' AND ummd" . $i . ".um_value = '{$value[0]}' )";
											} else {
												$this->joins[] = "LEFT JOIN {$wpdb->prefix}um_metadata ummd" . $i . " ON ummd" . $i . ".user_id = u.ID";
												$this->sql_where .= " AND ( ummd" . $i . ".um_key = '{$field}' AND CAST( ummd" . $i . ".um_value AS TIME ) BETWEEN {$value[0]} AND {$value[1]} )";
											}

											break;
									}

								}

								break;
							case 'role':
//								$value = explode( '||', $value );
//								$value = array_map( 'strtolower', $value );

								$value = array_map( 'strtolower', $value );

								if ( ! empty( $roles ) || is_multisite() ) {
									$roles_clauses = array();
									foreach ( $value as $role ) {
										$roles_clauses[] = "umm_roles.um_value LIKE '%\"" . $role . "\"%'";
									}

									$roles_search = implode( ' OR ', $roles_clauses );

									$this->sql_where .= " AND ( {$roles_search} )";
								} else {
									$this->joins[] = "LEFT JOIN {$wpdb->prefix}um_metadata umm_roles ON ( umm_roles.user_id = u.ID AND umm_roles.um_key = '" . $wpdb->get_blog_prefix( $blog_id ) . "capabilities' )";

									$roles = $value;

									$roles_clauses = array();
									foreach ( $value as $role ) {
										$roles_clauses[] = "umm_roles.um_value LIKE '%\"" . $role . "\"%'";
									}

									$roles_search = implode( ' OR ', $roles_clauses );

									$this->sql_where .= " AND ( {$roles_search} )";
								}

								break;
							case 'birth_date':
								$from_date = date( 'Y/m/d', mktime( 0,0,0, date( 'm', time() ), date( 'd', time() ), date( 'Y', time() - min( $value ) * YEAR_IN_SECONDS ) ) );
								$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 ) ) );

								$this->joins[] = "LEFT JOIN {$wpdb->prefix}um_metadata ummd" . $i . " ON ummd" . $i . ".user_id = u.ID";

								$this->sql_where .= " AND ( ummd" . $i . ".um_key = 'birth_date' AND ummd" . $i . ".um_value BETWEEN {$from_date} AND {$to_date} )";

								break;
							case 'user_registered':
								$offset = 0;
								if ( is_numeric( $gmt_offset ) ) {
									$offset = $gmt_offset;
								}

								$from_date = date( 'Y-m-d H:s:i', strtotime( date( 'Y-m-d H:s:i', min( $value ) ) . "+$offset hours" ) );
								$to_date = date( 'Y-m-d H:s:i', strtotime( date( 'Y-m-d H:s:i', max( $value ) ) . "+$offset hours" ) );

								$this->sql_where .= " AND ( u.user_registered BETWEEN {$from_date} AND {$to_date} )";

								break;
							case 'last_login':
								$offset = 0;
								if ( is_numeric( $gmt_offset ) ) {
									$offset = $gmt_offset;
								}

								$from_date = (int) min( $value ) + ( $offset * HOUR_IN_SECONDS ); // client time zone offset
								$to_date   = (int) max( $value ) + ( $offset * HOUR_IN_SECONDS ) + DAY_IN_SECONDS - 1; // time 23:59

								$this->joins[] = "LEFT JOIN {$wpdb->prefix}um_metadata ummd" . $i . " ON ummd" . $i . ".user_id = u.ID";

								$this->sql_where .= " AND ( ummd" . $i . ".um_key = '_um_last_login' AND ummd" . $i . ".um_value BETWEEN {$from_date} AND {$to_date} )";
								break;
						}
			$this->sql_order = apply_filters( 'um_modify_sortby_parameter', $this->sql_order, $sortby );
			$sql_join = implode( ' ', $this->joins );

				WHERE 1=1 {$this->sql_where}


		/**
		 * Get data array for pagination
		 *
		 *
		 * @param array $directory_data
		 * @param int $total_users
		 *
		 * @return array
		 */
		function calculate_pagination( $directory_data, $total_users ) {

			$current_page = ! empty( $_POST['page'] ) ? $_POST['page'] : 1;

			$total_users = ( ! empty( $directory_data['max_users'] ) && $directory_data['max_users'] <= $total_users ) ? $directory_data['max_users'] : $total_users;

			// number of profiles for mobile
			$profiles_per_page = $directory_data['profiles_per_page'];
			if ( UM()->mobile()->isMobile() && isset( $directory_data['profiles_per_page_mobile'] ) ) {
				$profiles_per_page = $directory_data['profiles_per_page_mobile'];
			}

			$total_pages = 1;
			if ( ! empty( $profiles_per_page ) ) {
				$total_pages = ceil( $total_users / $profiles_per_page );
			}

			if ( ! empty( $total_pages ) ) {
				$index1 = 0 - ( $current_page - 2 ) + 1;
				$to = $current_page + 2;
				if ( $index1 > 0 ) {
					$to += $index1;
				}

				$index2 = $total_pages - ( $current_page + 2 );
				$from = $current_page - 2;
				if ( $index2 < 0 ) {
					$from += $index2;
				}

				$pages_to_show = range(
					( $from > 0 ) ? $from : 1,
					( $to <= $total_pages ) ? $to : $total_pages
				);
			}


			$pagination_data = array(
				'pages_to_show' => ( ! empty( $pages_to_show ) && count( $pages_to_show ) > 1 ) ? array_values( $pages_to_show ) : array(),
				'current_page'  => $current_page,
				'total_pages'   => $total_pages,
				'total_users'   => $total_users,
			);

			$pagination_data['header'] = $this->convert_tags( $directory_data['header'], $pagination_data );
			$pagination_data['header_single'] = $this->convert_tags( $directory_data['header_single'], $pagination_data );

			return $pagination_data;
		}
						'value'     => trim( $_POST['search'] ),
						'value'     => trim( $_POST['search'] ),
						'value'     => trim( serialize( strval( $_POST['search'] ) ) ),
				$meta_query = apply_filters( 'um_member_directory_general_search_meta_query', $meta_query, $_POST['search'] );
				$search = trim( $_POST['search'] );
		 * @param \WP_User_Query $result
		function calculate_pagination( $directory_data, $result ) {
			$total_users = ( ! empty( $directory_data['max_users'] ) && $directory_data['max_users'] <= $result->total_users ) ? $directory_data['max_users'] : $result->total_users;
			$pagination_data = $this->calculate_pagination( $directory_data, $user_query );
	$search_from_url = ! empty( $_GET[ 'search_' . $unique_hash ] ) ? $_GET[ 'search_' . $unique_hash ] : '';

Fixed code:
				$metakeys[] = '_um_verified';

			if ( ! UM()->roles()->um_current_user_can( 'edit', $user_id ) ) {
				$ret['error'] = esc_js( __( 'You haven\'t ability to edit this user', 'ultimate-member' ) );
				wp_send_json_error( $ret );
			}

			if ( ! UM()->roles()->um_current_user_can( 'edit', $user_id ) ) {
				$ret['error'] = __( 'You haven\'t ability to edit this user', 'ultimate-member' );
				wp_send_json_error( $ret );
			}

		var $where_clauses = array();

		var $roles = array();
		var $general_meta_joined = false;

		/**
		 * @param $directory_data
		 * @param $field
		 * @param $value
		 * @param $i
		 * @param bool $is_default
		 */
		function handle_filter_query( $directory_data, $field, $value, $i, $is_default = false ) {
			global $wpdb;

			$blog_id = get_current_blog_id();

			switch ( $field ) {
				default:

					$filter_type = $this->filter_types[ $field ];

					/**
					 * UM hook
					 *
					 * @type filter
					 * @title um_query_args_{$field}__filter
					 * @description Change field's query for search at Members Directory
					 * @input_vars
					 * [{"var":"$field_query","type":"array","desc":"Field query"}]
					 * @change_log
					 * ["Since: 2.0"]
					 * @usage
					 * <?php add_filter( 'um_query_args_{$field}__filter_meta', 'function_name', 10, 4 ); ?>
					 * @example
					 * <?php
					 * add_filter( 'um_query_args_{$field}__filter_meta', 'my_query_args_filter', 10, 4 );
					 * function my_query_args_filter( $field_query ) {
					 *     // your code here
					 *     return $field_query;
					 * }
					 * ?>
					 */
					$skip_default = apply_filters( "um_query_args_{$field}__filter_meta", false, $this, $field, $value, $filter_type, $is_default );

					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':

								$this->joins[] = "LEFT JOIN {$wpdb->prefix}um_metadata umm{$i} ON umm{$i}.user_id = u.ID";

								$value = trim( stripslashes( $value ) );

								$this->where_clauses[] = $wpdb->prepare( "umm{$i}.um_key = %s AND umm{$i}.um_value = %s", $field, $value );

								if ( ! $is_default ) {
									$this->custom_filters_in_query[ $field ] = $value;
								}

								break;

							case 'select':
								if ( ! is_array( $value ) ) {
									$value = array( $value );
								}

								$this->joins[] = "LEFT JOIN {$wpdb->prefix}um_metadata umm{$i} ON umm{$i}.user_id = u.ID";

								$values_array = array();
								foreach ( $value as $single_val ) {
									$single_val = stripslashes( $single_val );

									$values_array[] = $wpdb->prepare( "umm{$i}.um_value LIKE %s", '%"' . trim( $single_val ) . '"%' );
									$values_array[] = $wpdb->prepare( "umm{$i}.um_value LIKE %s", '%' . serialize( strval( trim( $single_val ) ) ) . '%' );
									$values_array[] = $wpdb->prepare( "umm{$i}.um_value = %s", trim( $single_val ) );

									if ( is_numeric( $single_val ) ) {
										$values_array[] = $wpdb->prepare( "umm{$i}.um_value LIKE %s", '%' . serialize( intval( trim( $single_val ) ) ) . '%' );
									}
								}

								$values = implode( ' OR ', $values_array );

								$this->where_clauses[] = $wpdb->prepare( "( umm{$i}.um_key = %s AND ( {$values} ) )", $field );

								if ( ! $is_default ) {
									$this->custom_filters_in_query[ $field ] = $value;
								}

								break;
							case 'slider':

								$this->joins[] = "LEFT JOIN {$wpdb->prefix}um_metadata umm{$i} ON umm{$i}.user_id = u.ID";

								$min = min( $value );
								$max = max( $value );

								$this->where_clauses[] = $wpdb->prepare( "( umm{$i}.um_key = %s AND umm{$i}.um_value BETWEEN %d AND %d )", $field, $min, $max );

								if ( ! $is_default ) {
									$this->custom_filters_in_query[ $field ] = $value;
								}

								break;
							case 'datepicker':

								$offset = 0;
								if ( ! $is_default ) {
									if ( isset( $_POST['gmt_offset'] ) && is_numeric( $_POST['gmt_offset'] ) ) {
										$offset = (int) $_POST['gmt_offset'];
									}
								} else {
									$gmt_offset = get_post_meta( $directory_data['form_id'], '_um_search_filters_gmt', true );
									if ( is_numeric( $gmt_offset ) ) {
										$offset = $gmt_offset;
									}
								}

								$from_date = (int) min( $value ) + ( $offset * HOUR_IN_SECONDS ); // client time zone offset
								$to_date   = (int) max( $value ) + ( $offset * HOUR_IN_SECONDS ) + DAY_IN_SECONDS - 1; // time 23:59
								$from_date = date( 'Y/m/d', $from_date );
								$to_date = date( 'Y/m/d', $to_date );

								$this->joins[] = "LEFT JOIN {$wpdb->prefix}um_metadata umm{$i} ON umm{$i}.user_id = u.ID";

								$this->where_clauses[] = $wpdb->prepare( "( umm{$i}.um_key = %s AND umm{$i}.um_value BETWEEN %s AND %s )", $field, $from_date, $to_date );

								if ( ! $is_default ) {
									$this->custom_filters_in_query[ $field ] = array( $from_date, $to_date );
								}

								break;
							case 'timepicker':

								$this->joins[] = "LEFT JOIN {$wpdb->prefix}um_metadata umm{$i} ON umm{$i}.user_id = u.ID";
								if ( $value[0] == $value[1] ) {
									$this->where_clauses[] = $wpdb->prepare( "( umm{$i}.um_key = %s AND umm{$i}.um_value = %s )", $field, $value[0] );
								} else {
									$this->where_clauses[] = $wpdb->prepare( "( umm{$i}.um_key = %s AND CAST( umm{$i}.um_value AS TIME ) BETWEEN %s AND %s )", $field, $value[0], $value[1] );
								}

								if ( ! $is_default ) {
									$this->custom_filters_in_query[ $field ] = $value;
								}

								break;
						}

					}

					break;
				case 'role':
					$value = array_map( 'strtolower', $value );

					if ( empty( $this->roles ) && ! is_multisite() ) {
						$this->joins[] = "LEFT JOIN {$wpdb->prefix}um_metadata umm_roles ON ( umm_roles.user_id = u.ID AND umm_roles.um_key = '" . $wpdb->get_blog_prefix( $blog_id ) . "capabilities' )";
						$this->roles = $value;
					}

					$roles_clauses = array();
					foreach ( $value as $role ) {
						$roles_clauses[] = $wpdb->prepare( "umm_roles.um_value LIKE %s", '%"' . $role . '"%' );
					}

					$this->where_clauses[] = '( ' . implode( ' OR ', $roles_clauses ) . ' )';

					if ( ! $is_default ) {
						$this->custom_filters_in_query[ $field ] = $value;
					}

					break;
				case 'birth_date':

					$from_date = date( 'Y/m/d', mktime( 0,0,0, date( 'm', time() ), date( 'd', time() ), date( 'Y', time() - min( $value ) * YEAR_IN_SECONDS ) ) );
					$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 ) ) );

					$this->joins[] = "LEFT JOIN {$wpdb->prefix}um_metadata umm{$i} ON umm{$i}.user_id = u.ID";

					$this->where_clauses[] = $wpdb->prepare( "( umm{$i}.um_key = 'birth_date' AND umm{$i}.um_value BETWEEN %s AND %s )", $from_date, $to_date );

					if ( ! $is_default ) {
						$this->custom_filters_in_query[ $field ] = array( $to_date, $from_date );
					}

					break;
				case 'user_registered':

					$offset = 0;
					if ( ! $is_default ) {
						if ( isset( $_POST['gmt_offset'] ) && is_numeric( $_POST['gmt_offset'] ) ) {
							$offset = (int) $_POST['gmt_offset'];
						}
					} else {
						$gmt_offset = get_post_meta( $directory_data['form_id'], '_um_search_filters_gmt', true );
						if ( is_numeric( $gmt_offset ) ) {
							$offset = $gmt_offset;
						}
					}

					$from_date = date( 'Y-m-d H:s:i', strtotime( date( 'Y-m-d H:s:i', min( $value ) ) . "+$offset hours" ) );
					$to_date = date( 'Y-m-d H:s:i', strtotime( date( 'Y-m-d H:s:i', max( $value ) ) . "+$offset hours" ) );

					$this->where_clauses[] = $wpdb->prepare( "u.user_registered BETWEEN %s AND %s", $from_date, $to_date );

					if ( ! $is_default ) {
						$this->custom_filters_in_query[ $field ] = $value;
					}

					break;
				case 'last_login':

					$offset = 0;
					if ( ! $is_default ) {
						if ( isset( $_POST['gmt_offset'] ) && is_numeric( $_POST['gmt_offset'] ) ) {
							$offset = (int) $_POST['gmt_offset'];
						}
					} else {
						$gmt_offset = get_post_meta( $directory_data['form_id'], '_um_search_filters_gmt', true );
						if ( is_numeric( $gmt_offset ) ) {
							$offset = $gmt_offset;
						}
					}

					$from_date = (int) min( $value ) + ( $offset * HOUR_IN_SECONDS ); // client time zone offset
					$to_date   = (int) max( $value ) + ( $offset * HOUR_IN_SECONDS ) + DAY_IN_SECONDS - 1; // time 23:59

					$this->joins[] = "LEFT JOIN {$wpdb->prefix}um_metadata umm{$i} ON umm{$i}.user_id = u.ID";

					$this->where_clauses[] = $wpdb->prepare( "( umm{$i}.um_key = '_um_last_login' AND umm{$i}.um_value BETWEEN %s AND %s )", $from_date, $to_date );

					if ( ! $is_default ) {
						$this->custom_filters_in_query[ $field ] = $value;
					}

					break;
			}
		}


						$this->where_clauses[] = "u.ID IN ( '" . implode( "','", $users_array ) . "' )";
				if ( ! $this->general_meta_joined ) {
					$this->joins[] = "LEFT JOIN {$wpdb->prefix}um_metadata umm_general ON umm_general.user_id = u.ID";
					$this->general_meta_joined = true;
				}
				$this->where_clauses[] = "( umm_general.um_key = 'um_member_directory_data' AND 
					if ( ! $this->general_meta_joined ) {
						$this->joins[] = "LEFT JOIN {$wpdb->prefix}um_metadata umm_general ON umm_general.user_id = u.ID";
						$this->general_meta_joined = true;
					}
					$this->where_clauses[] = "( umm_general.um_key = 'um_member_directory_data'{$profile_photo_where}{$cover_photo_where} )";
			//$this->roles = array();
				$this->roles = array_merge( $this->roles, maybe_unserialize( $view_roles ) );
				if ( ! empty( $this->roles ) ) {
					$this->roles = array_intersect( $this->roles, maybe_unserialize( $directory_data['roles'] ) );
					$this->roles = array_merge( $this->roles, maybe_unserialize( $directory_data['roles'] ) );
			if ( ! empty( $this->roles ) ) {
				foreach ( $this->roles as $role ) {
					$roles_clauses[] = $wpdb->prepare( 'umm_roles.um_value LIKE %s', '%"' . $role . '"%' );
				$this->where_clauses[] = '( ' . implode( ' OR ', $roles_clauses ) . ' )';
					$this->where_clauses[] = "umm_roles.um_value IS NOT NULL";
				$search_line = trim( stripslashes( $_POST['search'] ) );

					$searches[] = $wpdb->prepare( "u.{$field} LIKE %s", '%' . $search_line . '%' );

				$this->where_clauses[] = $wpdb->prepare( "( umm_search.um_value = %s OR umm_search.um_value LIKE %s OR umm_search.um_value LIKE %s OR {$core_search})", $search_line, '%' . $search_line . '%', '%' . serialize( strval( $search_line ) ) . '%' );
					$this->handle_filter_query( $directory_data, $field, $value, $i );
						$this->handle_filter_query( $directory_data, $field, $value, $i, true );
			$this->sql_order = apply_filters( 'um_modify_sortby_parameter_meta', $this->sql_order, $sortby );
			$sql_join = implode( ' ', $this->joins );
			$sql_where = implode( ' AND ', $this->where_clauses );
			$sql_where = ! empty( $sql_where ) ? ' AND ' . $sql_where : '';


			/*var_dump( "SELECT SQL_CALC_FOUND_ROWS DISTINCT u.ID
				FROM {$wpdb->users} AS u
				{$sql_join}
				WHERE 1=1 {$sql_where}
				{$this->sql_order}
				{$this->sql_limit}" );*/

				WHERE 1=1 {$sql_where}
						'value'     => trim( stripslashes( $_POST['search'] ) ),
						'value'     => trim( stripslashes( $_POST['search'] ) ),
						'value'     => trim( serialize( strval( stripslashes( $_POST['search'] ) ) ) ),
				$meta_query = apply_filters( 'um_member_directory_general_search_meta_query', $meta_query, stripslashes( $_POST['search'] ) );
				$search = trim( stripslashes( $_POST['search'] ) );
		 * @param int $total_users
		function calculate_pagination( $directory_data, $total_users ) {
			$total_users = ( ! empty( $directory_data['max_users'] ) && $directory_data['max_users'] <= $total_users ) ? $directory_data['max_users'] : $total_users;
			$pagination_data = $this->calculate_pagination( $directory_data, $user_query->total_users );
	$search_from_url = ! empty( $_GET[ 'search_' . $unique_hash ] ) ? stripslashes( $_GET[ 'search_' . $unique_hash ] ) : '';