CVE-2020-6859 (published 2020-01-13T17:15Z)

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 v2 base score: 5.0 (AV:N/AC:L/Au:N/C:N/I:P/A:N)
CVSS v3 base score: 5.3, MEDIUM (CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:L/A:N)

Weakness information:
cwe_id: CWE-639
cwe_name: Authorization Bypass Through User-Controlled Key
description: 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.
extended_description: 
            Retrieval of a user record occurs in the system based on some key value that is under user control. The key would typically identify a user-related record stored in the system and would be used to lookup that record for presentation to the user. It is likely that an attacker would have to be an authenticated user in the system. However, the authorization process would not properly check the data access operation to ensure that the authenticated user performing the operation has sufficient entitlements to perform the requested data access, hence bypassing any other authorization checks present in the system.
            For example, attackers can look at places where user specific data is retrieved (e.g. search screens) and determine whether the key for the item being looked up is controllable externally. The key may be a hidden field in the HTML form field, might be passed as a URL parameter or as an unencrypted cookie variable, then in each of these cases it will be possible to tamper with the key value.
            One manifestation of this weakness is when a system uses sequential or otherwise easily-guessable session IDs that would allow one user to easily switch to another user's session and read/modify their data.
         
         

Fix information:
repo_url: https://github.com/ultimatemember/ultimatemember
commit_details: repo_url: https://github.com/ultimatemember/ultimatemember
author: nikitasinelnikov
author_date: 2020-01-08 15:19:24+02:00
msg: - fixed vulnerability with uploading cover/profile photo for other user ID;
- re-written member directory meta queries;
- fixed search line additional slashes;
repository: repo_url: https://github.com/ultimatemember/ultimatemember
repo_name: ultimatemember/ultimatemember
description: The easiest way to create powerful online communities and beautiful user profiles with WordPress
repo_language: PHP
owner: ultimatemember
file_changes: filename: class-admin-settings.php
change_type: ModificationType.MODIFY
diff: @@ -120,6 +120,7 @@ if ( ! class_exists( 'um\admin\core\Admin_Settings' ) ) {
 
 				//member directory data
 				$metakeys[] = 'um_member_directory_data';
+				$metakeys[] = '_um_verified';
 
 				$skip_fields = UM()->builtin()->get_fields_without_metakey();
 				$skip_fields = array_merge( $skip_fields, UM()->member_directory()->core_search_fields );

programming_language: PHP
filename: class-files.php
change_type: ModificationType.MODIFY
diff: @@ -287,6 +287,12 @@ if ( ! class_exists( 'um\core\Files' ) ) {
 			}
 
 			$user_id = empty( $_REQUEST['user_id'] ) ? get_current_user_id() : $_REQUEST['user_id'];
+
+			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 );
+			}
+
 			$image_path = um_is_file_owner( $src, $user_id, true );
 			if ( ! $image_path ) {
 				wp_send_json_error( esc_js( __( 'Invalid file ownership', 'ultimate-member' ) ) );
@@ -319,6 +325,11 @@ if ( ! class_exists( 'um\core\Files' ) ) {
 			UM()->fields()->set_id = $_POST['set_id'];
 			UM()->fields()->set_mode = $_POST['set_mode'];
 
+			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 );
+			}
+
 			/**
 			 * UM hook
 			 *

programming_language: PHP
filename: class-member-directory-meta.php
change_type: ModificationType.MODIFY
diff: @@ -18,9 +18,12 @@ if ( ! class_exists( 'um\core\Member_Directory_Meta' ) ) {
 		/**
 		 * @var string
 		 */
-		var $sql_where = '';
-		var $meta_iteration = 1;
 		var $joins = array();
+		var $where_clauses = array();
+
+		var $roles = array();
+		var $general_meta_joined = false;
+
 		var $sql_limit = '';
 		var $sql_order = '';
 
@@ -172,6 +175,246 @@ if ( ! class_exists( 'um\core\Member_Directory_Meta' ) ) {
 		}
 
 
+		/**
+		 * @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;
+			}
+		}
+
+
 		/**
 		 * Main Query function for getting members via AJAX
 		 */
@@ -206,7 +449,7 @@ if ( ! class_exists( 'um\core\Member_Directory_Meta' ) ) {
 					}
 
 					if ( ! empty( $users_array ) ) {
-						$this->sql_where .= " AND u.ID IN ( '" . implode( "','", $users_array ) . "' )";
+						$this->where_clauses[] = "u.ID IN ( '" . implode( "','", $users_array ) . "' )";
 					}
 				}
 			}
@@ -223,19 +466,23 @@ if ( ! class_exists( 'um\core\Member_Directory_Meta' ) ) {
 			}
 
 			if ( ! UM()->roles()->um_user_can( 'can_edit_everyone' ) ) {
-				$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 
+				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 
 				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 ) ) {
-					$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} )";
+					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} )";
 				}
 			}
 
-			$roles = array();
+			//$this->roles = array();
 			if ( UM()->roles()->um_user_can( 'can_view_all' ) ) {
 				$view_roles = um_user( 'can_view_roles' );
 
@@ -243,47 +490,48 @@ if ( ! class_exists( 'um\core\Member_Directory_Meta' ) ) {
 					$view_roles = array();
 				}
 
-				$roles = array_merge( $roles, maybe_unserialize( $view_roles ) );
+				$this->roles = array_merge( $this->roles, maybe_unserialize( $view_roles ) );
 			}
 
 			if ( ! empty( $directory_data['roles'] ) ) {
-				if ( ! empty( $roles ) ) {
-					$roles = array_intersect( $roles, maybe_unserialize( $directory_data['roles'] ) );
+				if ( ! empty( $this->roles ) ) {
+					$this->roles = array_intersect( $this->roles, maybe_unserialize( $directory_data['roles'] ) );
 				} else {
-					$roles = array_merge( $roles, maybe_unserialize( $directory_data['roles'] ) );
+					$this->roles = array_merge( $this->roles, maybe_unserialize( $directory_data['roles'] ) );
 				}
 			}
 
-			if ( ! empty( $roles ) ) {
+			if ( ! empty( $this->roles ) ) {
 				$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_clauses = array();
-				foreach ( $roles as $role ) {
-					$roles_clauses[] = "umm_roles.um_value LIKE '%\"" . $role . "\"%'";
+				foreach ( $this->roles as $role ) {
+					$roles_clauses[] = $wpdb->prepare( 'umm_roles.um_value LIKE %s', '%"' . $role . '"%' );
 				}
 
-				$roles_search = implode( ' OR ', $roles_clauses );
-
-				$this->sql_where .= " AND ( {$roles_search} )";
+				$this->where_clauses[] = '( ' . implode( ' OR ', $roles_clauses ) . ' )';
 			} else {
 				if ( is_multisite() ) {
 					// select users who have capabilities for current blog
 					$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->sql_where .= " AND umm_roles.um_value IS NOT NULL ";
+					$this->where_clauses[] = "umm_roles.um_value IS NOT NULL";
 				}
 			}
 
 
 			if ( ! empty( $_POST['search'] ) ) {
+				$search_line = trim( stripslashes( $_POST['search'] ) );
+
 				$searches = array();
 				foreach ( $this->core_search_fields as $field ) {
-					$searches[] = $wpdb->prepare( "u.{$field} LIKE %s", '%' . trim( $_POST['search'] ) . '%' );
+					$searches[] = $wpdb->prepare( "u.{$field} LIKE %s", '%' . $search_line . '%' );
 				}
 
 				$core_search = implode( ' OR ', $searches );
 
 				$this->joins[] = "LEFT JOIN {$wpdb->prefix}um_metadata umm_search ON umm_search.user_id = u.ID";
-				$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})";
+
+				$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->is_search = true;
 			}
@@ -313,209 +561,7 @@ if ( ! class_exists( 'um\core\Member_Directory_Meta' ) ) {
 						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 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;
-					}
+					$this->handle_filter_query( $directory_data, $field, $value, $i );
 
 					$i++;
 				}
@@ -529,199 +575,11 @@ if ( ! class_exists( 'um\core\Member_Directory_Meta' ) ) {
 					$default_filters = maybe_unserialize( $directory_data['search_filters'] );
 				}
 
-				$gmt_offset = get_post_meta( $directory_data['form_id'], '_um_search_filters_gmt', true );
-
 				if ( ! empty( $default_filters ) ) {
 					$i = 1;
 					foreach ( $default_filters as $field => $value ) {
-						//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->handle_filter_query( $directory_data, $field, $value, $i, true );
 
 						$i++;
 					}
@@ -820,7 +678,7 @@ if ( ! class_exists( 'um\core\Member_Directory_Meta' ) ) {
 				}
 			}
 
-			$this->sql_order = apply_filters( 'um_modify_sortby_parameter', $this->sql_order, $sortby );
+			$this->sql_order = apply_filters( 'um_modify_sortby_parameter_meta', $this->sql_order, $sortby );
 
 
 			$profiles_per_page = $directory_data['profiles_per_page'];
@@ -841,16 +699,26 @@ if ( ! class_exists( 'um\core\Member_Directory_Meta' ) ) {
 				$this->sql_limit .= $wpdb->prepare( 'LIMIT %d, %d', $query_number * ( $query_paged - 1 ), $number );
 			}
 
-			$sql_join = implode( ' ', $this->joins );
-
 			do_action( 'um_pre_users_query', $this, $directory_data, $sortby );
 
+			$sql_join = implode( ' ', $this->joins );
+			$sql_where = implode( ' AND ', $this->where_clauses );
+			$sql_where = ! empty( $sql_where ) ? ' AND ' . $sql_where : '';
+
 			global $wpdb;
+
+			/*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}" );*/
+
 			$user_ids = $wpdb->get_col(
 				"SELECT SQL_CALC_FOUND_ROWS DISTINCT u.ID 
 				FROM {$wpdb->users} AS u
 				{$sql_join}
-				WHERE 1=1 {$this->sql_where}
+				WHERE 1=1 {$sql_where}
 				{$this->sql_order}
 				{$this->sql_limit}"
 			);
@@ -876,65 +744,5 @@ if ( ! class_exists( 'um\core\Member_Directory_Meta' ) ) {
 
 			wp_send_json_success( array( 'pagination' => $pagination_data, 'users' => $users, 'is_search' => $this->is_search ) );
 		}
-
-
-		/**
-		 * 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;
-		}
 	}
 }
\ No newline at end of file

programming_language: PHP
filename: class-member-directory.php
change_type: ModificationType.MODIFY
diff: @@ -1252,20 +1252,20 @@ if ( ! class_exists( 'um\core\Member_Directory' ) ) {
 				$meta_query = array(
 					'relation' => 'OR',
 					array(
-						'value'     => trim( $_POST['search'] ),
+						'value'     => trim( stripslashes( $_POST['search'] ) ),
 						'compare'   => '=',
 					),
 					array(
-						'value'     => trim( $_POST['search'] ),
+						'value'     => trim( stripslashes( $_POST['search'] ) ),
 						'compare'   => 'LIKE',
 					),
 					array(
-						'value'     => trim( serialize( strval( $_POST['search'] ) ) ),
+						'value'     => trim( serialize( strval( stripslashes( $_POST['search'] ) ) ) ),
 						'compare'   => 'LIKE',
 					),
 				);
 
-				$meta_query = apply_filters( 'um_member_directory_general_search_meta_query', $meta_query, $_POST['search'] );
+				$meta_query = apply_filters( 'um_member_directory_general_search_meta_query', $meta_query, stripslashes( $_POST['search'] ) );
 
 				$this->query_args['meta_query'][] = $meta_query;
 
@@ -1290,7 +1290,7 @@ if ( ! class_exists( 'um\core\Member_Directory' ) ) {
 		function change_meta_sql( $sql, $queries, $type, $primary_table, $primary_id_column, $context ) {
 			if ( ! empty( $_POST['search'] ) ) {
 				global $wpdb;
-				$search = trim( $_POST['search'] );
+				$search = trim( stripslashes( $_POST['search'] ) );
 				if ( ! empty( $search ) ) {
 
 					$meta_value = '%' . $wpdb->esc_like( $search ) . '%';
@@ -1870,15 +1870,15 @@ if ( ! class_exists( 'um\core\Member_Directory' ) ) {
 		 *
 		 *
 		 * @param array $directory_data
-		 * @param \WP_User_Query $result
+		 * @param int $total_users
 		 *
 		 * @return array
 		 */
-		function calculate_pagination( $directory_data, $result ) {
+		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'] <= $result->total_users ) ? $directory_data['max_users'] : $result->total_users;
+			$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'];
@@ -2292,7 +2292,7 @@ if ( ! class_exists( 'um\core\Member_Directory' ) ) {
 			 */
 			do_action( 'um_user_after_query', $this->query_args, $user_query );
 
-			$pagination_data = $this->calculate_pagination( $directory_data, $user_query );
+			$pagination_data = $this->calculate_pagination( $directory_data, $user_query->total_users );
 
 			$user_ids = ! empty( $user_query->results ) ? array_unique( $user_query->results ) : array();
 

programming_language: PHP
filename: members.php
change_type: ModificationType.MODIFY
diff: @@ -75,7 +75,7 @@ $search = isset( $args['search'] ) ? $args['search'] : false;
 $show_search = empty( $args['roles_can_search'] ) || ( ! empty( $priority_user_role ) && in_array( $priority_user_role, $args['roles_can_search'] ) );
 $search_from_url = '';
 if ( $search && $show_search ) {
-	$search_from_url = ! empty( $_GET[ 'search_' . $unique_hash ] ) ? $_GET[ 'search_' . $unique_hash ] : '';
+	$search_from_url = ! empty( $_GET[ 'search_' . $unique_hash ] ) ? stripslashes( $_GET[ 'search_' . $unique_hash ] ) : '';
 }
 
 

programming_language: PHP