From 249682559012734a4f7d71f52609b2f301ea55b1 Mon Sep 17 00:00:00 2001 From: nikitasinelnikov Date: Wed, 8 Jan 2020 15:19:24 +0200 Subject: [PATCH] - fixed vulnerability with uploading cover/profile photo for other user ID; - re-written member directory meta queries; - fixed search line additional slashes; --- includes/admin/core/class-admin-settings.php | 1 + includes/core/class-files.php | 11 + includes/core/class-member-directory-meta.php | 766 +++++++----------- includes/core/class-member-directory.php | 18 +- templates/members.php | 2 +- 5 files changed, 309 insertions(+), 489 deletions(-) diff --git a/includes/admin/core/class-admin-settings.php b/includes/admin/core/class-admin-settings.php index f082b4cd..4ee96a5f 100644 --- a/includes/admin/core/class-admin-settings.php +++ b/includes/admin/core/class-admin-settings.php @@ -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 ); diff --git a/includes/core/class-files.php b/includes/core/class-files.php index c766748c..cc2f8650 100644 --- a/includes/core/class-files.php +++ b/includes/core/class-files.php @@ -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 * diff --git a/includes/core/class-member-directory-meta.php b/includes/core/class-member-directory-meta.php index 2776f7e1..2f25f16b 100644 --- a/includes/core/class-member-directory-meta.php +++ b/includes/core/class-member-directory-meta.php @@ -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 + * + * @example + * + */ + $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 - * - * @example - * - */ - $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 - * - * @example - * - */ - $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 diff --git a/includes/core/class-member-directory.php b/includes/core/class-member-directory.php index 11ac2533..1aa31494 100644 --- a/includes/core/class-member-directory.php +++ b/includes/core/class-member-directory.php @@ -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(); diff --git a/templates/members.php b/templates/members.php index 52cb675c..48bf7bed 100644 --- a/templates/members.php +++ b/templates/members.php @@ -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 ] ) : ''; }