PluginProbe
Property Hive / 2.3.0
Property Hive v2.3.0
2.3.1 2.3.0 2.2.6 2.2.5 2.2.4 2.2.3 2.2.2 1.4.46 1.4.47 1.4.48 1.4.49 1.4.5 1.4.50 1.4.51 1.4.52 1.4.53 1.4.54 1.4.55 1.4.56 1.4.57 1.4.58 1.4.59 1.4.6 1.4.60 1.4.61 All 261 releases
← All changes | includes/class-ph-query.php +1171 -143 1.4.492.3.0 View file →
@@ -1,5 +1,8 @@
1 1 <?php
2 +// phpcs:set WordPress.Security.ValidatedSanitizedInput customSanitizingFunctions[] ph_clean
3 +// ph_clean() recursively sanitizes text; presence, shape and unslashing checks remain separate.
4 +
2 5 /**
3 6 * Contains the query functions for PropertyHive which alter the front-end post queries and loops.
4 7 *
5 8 * @class PH_Query
@@ -17,8 +20,18 @@
17 20 * PH_Query Class
18 21 */
19 22 class PH_Query {
20 23
24 + /** Keyword normalized by this request's meta-query builder, shared across query instances. */
25 + private static $normalized_keyword = null;
26 +
27 + /** Read a department slug for this query without changing the shared request. */
28 + private function get_requested_department() {
29 + // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- A public search filter only; it does not authorize a write.
30 + return isset( $_REQUEST['department'] ) && is_string( $_REQUEST['department'] ) ? sanitize_text_field( wp_unslash( $_REQUEST['department'] ) ) : null;
31 + }
32 +
33 +
21 34 /** @public array Query vars to add to wp */
22 35 public $query_vars = array();
23 36
24 37 /** @public array Unfiltered property ids (before layered nav etc) */
@@ -35,8 +48,11 @@
35 48
36 49 /** @public array The meta query for the page */
37 50 public $meta_query = '';
38 51
52 + /** @public array The tax query for the page */
53 + public $tax_query = '';
54 +
39 55 /** @public array Post IDs matching layered nav only */
40 56 public $layered_nav_post__in = array();
41 57
42 58 /** @public array Stores post IDs matching layered nav, so price filter can find max price in view */
@@ -41,8 +57,11 @@
41 57
42 58 /** @public array Stores post IDs matching layered nav, so price filter can find max price in view */
43 59 public $layered_nav_property_ids = array();
44 60
61 + /** @public array Stores post IDs matching layered nav, so price filter can find max price in view */
62 + public $address_keyword_polygon_points = array();
63 +
45 64 /**
46 65 * Constructor for the query class. Hooks in methods.
47 66 *
48 67 * @access public
@@ -48,9 +67,8 @@
48 67 * @access public
49 68 */
50 69 public function __construct() {
51 70
52 - //add_action( 'init', array( $this, 'add_endpoints' ) );
53 71 add_action( 'init', array( $this, 'layered_nav_init' ) );
54 72 add_action( 'init', array( $this, 'price_filter_init' ) );
55 73
56 74 if ( ! is_admin() ) {
@@ -60,13 +78,152 @@
60 78 add_filter( 'pre_get_posts', array( $this, 'pre_get_posts' ) );
61 79 add_filter( 'the_posts', array( $this, 'the_posts' ), 11, 2 );
62 80 add_action( 'wp', array( $this, 'remove_property_query' ) );
63 81 add_action( 'wp', array( $this, 'remove_ordering_args' ) );
82 + add_filter( 'posts_where', array( $this, 'commercial_display_where' ), 10, 2 );
83 + add_filter( 'posts_where', array( $this, 'keyword_excerpt_where' ), 10, 2 );
84 + add_action( 'pre_get_posts', array( $this, 'custom_order_properties_by_availability' ), 10, 2 );
64 85 }
65 86
66 87 $this->init_query_vars();
67 88 }
68 89
90 + public function custom_order_properties_by_availability($query)
91 + {
92 + if ( is_admin() )
93 + {
94 + return;
95 + }
96 +
97 + if ( !$query->is_main_query() )
98 + {
99 + return;
100 + }
101 +
102 + if ( !is_post_type_archive('property') )
103 + {
104 + return;
105 + }
106 +
107 + if ( apply_filters( 'propertyhive_order_by_availability', false ) === false )
108 + {
109 + return;
110 + }
111 +
112 + $availability_order = get_option('propertyhive_taxonomy_terms_order_availability', array());
113 +
114 + if ( empty($availability_order) )
115 + {
116 + return;
117 + }
118 +
119 + // Sanitize and prepare the order
120 + $availability_order = explode("|", $availability_order);
121 + $availability_order = array_map('intval', $availability_order);
122 +
123 + // Modify the main query to join with term relationships and term taxonomy tables using custom aliases
124 + add_filter('posts_join', function ($join, $query)
125 + {
126 + global $wpdb;
127 +
128 + if ($query->is_main_query() && is_post_type_archive('property'))
129 + {
130 + $join .= " LEFT JOIN {$wpdb->term_relationships} AS avstr ON ({$wpdb->posts}.ID = avstr.object_id) ";
131 + $join .= " LEFT JOIN {$wpdb->term_taxonomy} AS avstt ON (avstr.term_taxonomy_id = avstt.term_taxonomy_id) ";
132 + }
133 +
134 + return $join;
135 + }, 10, 2);
136 +
137 + // Add a custom ordering clause
138 + add_filter('posts_orderby', function ($orderby, $query) use ($availability_order)
139 + {
140 + global $wpdb;
141 +
142 + if ($query->is_main_query() && is_post_type_archive('property')) {
143 + // Retrieve the original orderby clause
144 + $original_orderby = $orderby ? $orderby : "{$wpdb->posts}.post_date DESC";
145 +
146 + // Construct the custom order by clause
147 + $order_by_custom = "FIELD(avstt.term_id, " . implode(',', $availability_order) . ")";
148 +
149 + // Combine the custom order by with the original order by
150 + $orderby_combined = "$order_by_custom, $original_orderby";
151 +
152 + return $orderby_combined;
153 + }
154 +
155 + return $orderby;
156 + }, 10, 2);
157 + }
158 +
159 + public function keyword_excerpt_where( $where, $query )
160 + {
161 + if ( ( is_array($query->get('post_type')) && in_array('property', $query->get('post_type')) ) || ( !is_array($query->get('post_type')) && $query->get('post_type') == 'property' ) )
162 + {
163 + global $wpdb;
164 +
165 + // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- The public keyword filter reads the request, sanitizes/SQL-escapes it, and contributes only to the current SQL WHERE clause. This exact annotation covers only NonceVerification.Recommended; retain all sanitizer and SQL-safety checks.
166 + if ( isset($_REQUEST['keyword']) && is_string( $_REQUEST['keyword'] ) && $_REQUEST['keyword'] != '' )
167 + {
168 + // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only search; reuse the already-unslashed value produced by keyword_meta_query when available.
169 + $keyword = isset( self::$normalized_keyword ) && $_REQUEST['keyword'] === self::$normalized_keyword ? self::$normalized_keyword : sanitize_text_field( wp_unslash( $_REQUEST['keyword'] ) );
170 + $ref_pos = strpos($where, '_features_concatenated');
171 + if ( $ref_pos !== FALSE )
172 + {
173 + // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- The public keyword filter reads the request, sanitizes/SQL-escapes it, and contributes only to the current SQL WHERE clause. This exact annotation covers only NonceVerification.Recommended; retain all sanitizer and SQL-safety checks.
174 + $str_to_insert = " $wpdb->posts.post_excerpt LIKE '%" . esc_sql( $keyword ) . "%' OR ";
175 + $where = substr_replace($where, $str_to_insert, $ref_pos - 18, 0);
176 + }
177 + }
178 + }
179 +
180 + return $where;
181 + }
182 +
183 + public function commercial_display_where( $where, $query )
184 + {
185 + if ( $query->get('post_type') == 'property' )
186 + {
187 + global $wpdb;
188 +
189 + $commercial_display = get_option( 'propertyhive_commercial_display', '' );
190 +
191 + switch ( $commercial_display )
192 + {
193 + case "top_level_only":
194 + {
195 + $where .= " AND $wpdb->posts.post_parent=0 ";
196 + break;
197 + }
198 + case "top_level_only_but_units_when_filtered":
199 + {
200 + $unit_filter_parameters = apply_filters( 'propertyhive_unit_filter_parameters', array( 'minimum_floor_area', 'maximum_floor_area' ) );
201 + $unit_filter_parameter_found = false;
202 + foreach ( $unit_filter_parameters as $parameter )
203 + {
204 + // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- The public commercial display filter reads a request flag and contributes only to the current SQL WHERE clause. This exact annotation covers only NonceVerification.Recommended; retain all sanitizer and SQL-safety checks.
205 + if ( isset($_REQUEST[$parameter]) && ph_clean( wp_unslash( $_REQUEST[$parameter] ) ) != '' )
206 + {
207 + $unit_filter_parameter_found = true;
208 + }
209 + }
210 + if ( !$unit_filter_parameter_found )
211 + {
212 + $where .= " AND $wpdb->posts.post_parent=0 ";
213 + }
214 + break;
215 + }
216 + default:
217 + {
218 + // do nothing
219 + }
220 + }
221 + }
222 +
223 + return $where;
224 + }
225 +
69 226 /**
70 227 * Init query vars by loading options.
71 228 */
72 229 public function init_query_vars() {
@@ -79,21 +236,14 @@
79 236 /**
80 237 * Get any errors from querystring
81 238 */
82 239 public function get_errors() {
83 - if ( ! empty( $_GET['ph_error'] ) && ( $error = sanitize_text_field( $_GET['ph_error'] ) ) && ! ph_has_notice( $error, 'error' ) )
240 + // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- The public frontend reads ph_error to add a request-scoped notice; it does not write posts, options, user data, or other persistent state. This exact annotation covers only NonceVerification.Recommended; retain all sanitizer and SQL-safety checks.
241 + if ( ! empty( $_GET['ph_error'] ) && ( $error = sanitize_text_field( wp_unslash( $_GET['ph_error'] ) ) ) && ! ph_has_notice( $error, 'error' ) )
84 242 ph_add_notice( $error, 'error' );
85 243 }
86 244
87 245 /**
88 - * Add endpoints for query vars
89 - */
90 - public function add_endpoints() {
91 - foreach ( $this->query_vars as $key => $var )
92 - add_rewrite_endpoint( $var, EP_PAGES );
93 - }
94 -
95 - /**
96 246 * add_query_vars function.
97 247 *
98 248 * @access public
99 249 * @param array $vars
@@ -121,9 +271,11 @@
121 271 global $wp;
122 272
123 273 // Map query vars to their keys, or get them if endpoints are not supported
124 274 foreach ( $this->query_vars as $key => $var ) {
275 + // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- The public frontend normalizes a URL query variable into the current WP request query_vars; this is request/query state only and has no persistent write. This exact annotation covers only NonceVerification.Recommended; retain all sanitizer and SQL-safety checks.
125 276 if ( isset( $_GET[ $var ] ) ) {
277 + // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- The public frontend normalizes a URL query variable into the current WP request query_vars; this is request/query state only and has no persistent write. This exact annotation covers only NonceVerification.Recommended; retain all sanitizer and SQL-safety checks.
126 278 $wp->query_vars[ $key ] = sanitize_text_field( wp_unslash( $_GET[ $var ] ) );
127 279 }
128 280
129 281 elseif ( isset( $wp->query_vars[ $var ] ) ) {
@@ -180,8 +332,9 @@
180 332 if ( isset( $q->query['paged'] ) )
181 333 $q->set( 'paged', $q->query['paged'] );
182 334
183 335 // Define a variable so we know this is the front page search results later on
336 + // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedConstantFound -- Retain the existing frontend state constant for theme and extension compatibility.
184 337 define( 'SEARCH_RESULTS_IS_ON_FRONT', true );
185 338
186 339 // Get the actual WP page to avoid errors and let us use is_front_page()
187 340 // This is hacky but works. Awaiting http://core.trac.wordpress.org/ticket/21096
@@ -371,9 +524,9 @@
371 524 $post__in = array();
372 525 //$post__in = array_unique( apply_filters( 'loop_shop_post_in', array() ) );
373 526
374 527 // Ordering query vars
375 - $q->set( 'orderby', $ordering['orderby'] );
528 + $q->set( 'orderby', $ordering['orderby'] . ' post_title' );
376 529 $q->set( 'order', $ordering['order'] );
377 530 if ( isset( $ordering['meta_key'] ) )
378 531 $q->set( 'meta_key', $ordering['meta_key'] );
379 532
@@ -381,8 +534,9 @@
381 534 $q->set( 'meta_query', $meta_query );
382 535 $q->set( 'tax_query', $tax_query );
383 536 $q->set( 'date_query', $date_query );
384 537 $q->set( 'post__in', $post__in );
538 + // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound -- Existing public Property Hive extension hook loop_search_results_per_page; changing the established name would detach installed callbacks.
385 539 $q->set( 'posts_per_page', $q->get( 'posts_per_page' ) ? $q->get( 'posts_per_page' ) : apply_filters( 'loop_search_results_per_page', get_option( 'posts_per_page' ) ) );
386 540
387 541 // Set a special variable
388 542 $q->set( 'ph_query', true );
@@ -454,8 +608,9 @@
454 608 array(
455 609 'post_type' => 'property',
456 610 'numberposts' => -1,
457 611 'post_status' => 'publish',
612 + // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_query -- Cached property IDs must preserve the current search metadata predicates; only IDs are fetched, without totals or metadata/term cache priming.
458 613 'meta_query' => $this->meta_query,
459 614 'fields' => 'ids',
460 615 'no_found_rows' => true,
461 616 'update_post_meta_cache' => false,
@@ -490,11 +645,14 @@
490 645 * @access public
491 646 * @return array
492 647 */
493 648 public function get_search_results_ordering_args( $orderby = '', $order = '' ) {
649 + $request_department = $this->get_requested_department();
650 +
494 651 // Get ordering from query string unless defined
495 652 if ( ! $orderby ) {
496 - $orderby_value = isset( $_GET['orderby'] ) ? sanitize_text_field( $_GET['orderby'] ) : apply_filters( 'propertyhive_default_search_results_orderby', get_option( 'propertyhive_default_search_results_orderby' ) );
653 + // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- The public property search reads ordering/filter inputs and returns ordering arguments for the current query only. This exact annotation covers only NonceVerification.Recommended; retain all sanitizer and SQL-safety checks.
654 + $orderby_value = isset( $_GET['orderby'] ) ? sanitize_text_field( wp_unslash( $_GET['orderby'] ) ) : apply_filters( 'propertyhive_default_search_results_orderby', get_option( 'propertyhive_default_search_results_orderby' ) );
497 655
498 656 // Get order + orderby args from string
499 657 $orderby_value = explode( '-', $orderby_value );
500 658 $orderby = esc_attr( $orderby_value[0] );
@@ -507,23 +665,29 @@
507 665 $args = array();
508 666
509 667 // default - menu_order
510 668 if (
511 - ( isset($_REQUEST['department']) && $_REQUEST['department'] != 'commercial' ) ||
512 - ( !isset($_REQUEST['department']) && get_option( 'propertyhive_primary_department' ) != 'commercial' )
669 + // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- The public property search reads ordering/filter inputs and returns ordering arguments for the current query only. This exact annotation covers only NonceVerification.Recommended; retain all sanitizer and SQL-safety checks.
670 + ( isset($request_department) && $request_department != 'commercial' && ph_get_custom_department_based_on($request_department) != 'commercial' ) ||
671 + // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- The public property search reads ordering/filter inputs and returns ordering arguments for the current query only. This exact annotation covers only NonceVerification.Recommended; retain all sanitizer and SQL-safety checks.
672 + ( !isset($request_department) && get_option( 'propertyhive_primary_department' ) != 'commercial' && ph_get_custom_department_based_on(get_option( 'propertyhive_primary_department' )) != 'commercial' )
513 673 )
514 674 {
515 675 $args['orderby'] = 'meta_value_num';
516 676 $args['order'] = $order == 'ASC' ? 'ASC' : 'DESC';
677 + // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_key -- Search ordering maps supported price/floor-area/date choices to literal stored meta keys. switch cases and department branches set fixed keys; order direction is constrained to ASC/DESC.
517 678 $args['meta_key'] = '_price_actual';
518 679 }
519 680 elseif (
520 - ( isset($_REQUEST['department']) && $_REQUEST['department'] == 'commercial' ) ||
521 - ( !isset($_REQUEST['department']) && get_option( 'propertyhive_primary_department' ) == 'commercial' )
681 + // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- The public property search reads ordering/filter inputs and returns ordering arguments for the current query only. This exact annotation covers only NonceVerification.Recommended; retain all sanitizer and SQL-safety checks.
682 + ( isset($request_department) && ( $request_department == 'commercial' || ph_get_custom_department_based_on($request_department) == 'commercial' ) ) ||
683 + // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- The public property search reads ordering/filter inputs and returns ordering arguments for the current query only. This exact annotation covers only NonceVerification.Recommended; retain all sanitizer and SQL-safety checks.
684 + ( !isset($request_department) && ( get_option( 'propertyhive_primary_department' ) == 'commercial' || ph_get_custom_department_based_on(get_option( 'propertyhive_primary_department' )) == 'commercial' ) )
522 685 )
523 686 {
524 687 $args['orderby'] = 'meta_value_num';
525 688 $args['order'] = $order == 'ASC' ? 'ASC' : 'DESC';
689 + // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_key -- Search ordering maps supported price/floor-area/date choices to literal stored meta keys. switch cases and department branches set fixed keys; order direction is constrained to ASC/DESC.
526 690 $args['meta_key'] = '_floor_area_from_sqft';
527 691 }
528 692
529 693 switch ( $orderby ) {
@@ -530,16 +694,20 @@
530 694 case 'price' :
531 695 $args['orderby'] = 'meta_value_num';
532 696 $args['order'] = $order == 'ASC' ? 'ASC' : 'DESC';
533 697 if (
534 - ( isset($_REQUEST['department']) && $_REQUEST['department'] == 'commercial' ) ||
535 - ( !isset($_REQUEST['department']) && get_option( 'propertyhive_primary_department' ) == 'commercial' )
698 + // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- The public property search reads ordering/filter inputs and returns ordering arguments for the current query only. This exact annotation covers only NonceVerification.Recommended; retain all sanitizer and SQL-safety checks.
699 + ( isset($request_department) && ( $request_department == 'commercial' || ph_get_custom_department_based_on($request_department) == 'commercial' ) ) ||
700 + // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- The public property search reads ordering/filter inputs and returns ordering arguments for the current query only. This exact annotation covers only NonceVerification.Recommended; retain all sanitizer and SQL-safety checks.
701 + ( !isset($request_department) && ( get_option( 'propertyhive_primary_department' ) == 'commercial' || ph_get_custom_department_based_on(get_option( 'propertyhive_primary_department' )) == 'commercial' ) )
536 702 )
537 703 {
704 + // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_key -- Search ordering maps supported price/floor-area/date choices to literal stored meta keys. switch cases and department branches set fixed keys; order direction is constrained to ASC/DESC.
538 705 $args['meta_key'] = '_price_from_actual';
539 706 }
540 707 else
541 708 {
709 + // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_key -- Search ordering maps supported price/floor-area/date choices to literal stored meta keys. switch cases and department branches set fixed keys; order direction is constrained to ASC/DESC.
542 710 $args['meta_key'] = '_price_actual';
543 711 }
544 712 break;
545 713 case 'floor_area' :
@@ -544,10 +712,17 @@
544 712 break;
545 713 case 'floor_area' :
546 714 $args['orderby'] = 'meta_value_num';
547 715 $args['order'] = $order == 'ASC' ? 'ASC' : 'DESC';
716 + // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_key -- Search ordering maps supported price/floor-area/date choices to literal stored meta keys. switch cases and department branches set fixed keys; order direction is constrained to ASC/DESC.
548 717 $args['meta_key'] = '_floor_area_from_sqft';
549 718 break;
719 + case 'date' :
720 + $args['orderby'] = 'meta_value';
721 + $args['order'] = $order == 'ASC' ? 'ASC' : 'DESC';
722 + // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_key -- Search ordering maps supported price/floor-area/date choices to literal stored meta keys. switch cases and department branches set fixed keys; order direction is constrained to ASC/DESC.
723 + $args['meta_key'] = '_on_market_change_date';
724 + break;
550 725 default :
551 726 {
552 727 if ( $orderby != '' )
553 728 {
@@ -569,21 +744,25 @@
569 744 public function get_date_query() {
570 745
571 746 $date_query = array();
572 747
748 + // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- The public property search reads a filter value and contributes query/meta/taxonomy arguments for the current request only; no persistent state-changing operation is reached. This exact annotation covers only NonceVerification.Recommended; retain all sanitizer and SQL-safety checks.
573 749 if ( isset( $_REQUEST['added_from'] ) && $_REQUEST['added_from'] != '' )
574 750 {
575 751 $date_query = array(
576 752 'column' => 'post_date_gmt',
577 - 'after' => sanitize_text_field( $_REQUEST['added_from'] )
753 + // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- The public property search reads a filter value and contributes query/meta/taxonomy arguments for the current request only; no persistent state-changing operation is reached. This exact annotation covers only NonceVerification.Recommended; retain all sanitizer and SQL-safety checks.
754 + 'after' => sanitize_text_field( wp_unslash( $_REQUEST['added_from'] ) )
578 755 );
579 756 }
580 757
758 + // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- The public property search reads a filter value and contributes query/meta/taxonomy arguments for the current request only; no persistent state-changing operation is reached. This exact annotation covers only NonceVerification.Recommended; retain all sanitizer and SQL-safety checks.
581 759 if ( isset( $_REQUEST['added_from_hours'] ) && $_REQUEST['added_from_hours'] != '' )
582 760 {
583 761 $date_query = array(
584 762 'column' => 'post_date_gmt',
585 - 'after' => sanitize_text_field( $_REQUEST['added_from_hours'] ) . ' hours ago'
763 + // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- The public property search reads a filter value and contributes query/meta/taxonomy arguments for the current request only; no persistent state-changing operation is reached. This exact annotation covers only NonceVerification.Recommended; retain all sanitizer and SQL-safety checks.
764 + 'after' => sanitize_text_field( wp_unslash( $_REQUEST['added_from_hours'] ) ) . ' hours ago'
586 765 );
587 766 }
588 767
589 768 return array_filter( $date_query );
@@ -607,8 +786,10 @@
607 786 $meta_query = array();
608 787
609 788 $meta_query[] = $this->on_market_meta_query();
610 789 $meta_query[] = $this->department_meta_query($q);
790 + $meta_query[] = $this->featured_meta_query();
791 + $meta_query[] = $this->date_added_meta_query();
611 792 $meta_query[] = $this->address_keyword_meta_query();
612 793 $meta_query[] = $this->country_meta_query();
613 794 $meta_query[] = $this->minimum_price_meta_query();
614 795 $meta_query[] = $this->maximum_price_meta_query();
@@ -630,11 +811,16 @@
630 811 $meta_query[] = $this->floor_area_range_meta_query();
631 812 $meta_query[] = $this->commercial_for_sale_to_rent_meta_query();
632 813 $meta_query[] = $this->commercial_for_sale_meta_query();
633 814 $meta_query[] = $this->commercial_to_rent_meta_query();
815 + $meta_query[] = $this->commercial_minimum_price_meta_query();
816 + $meta_query[] = $this->commercial_maximum_price_meta_query();
817 + $meta_query[] = $this->commercial_minimum_rent_meta_query();
818 + $meta_query[] = $this->commercial_maximum_rent_meta_query();
634 819 $meta_query[] = $this->negotiator_meta_query();
635 820 $meta_query[] = $this->office_meta_query();
636 -
821 + $meta_query[] = $this->keyword_meta_query();
822 +
637 823 return array_filter( apply_filters( 'propertyhive_property_query_meta_query', $meta_query, $this ) );
638 824 }
639 825
640 826 /**
@@ -666,16 +852,18 @@
666 852 * @access public
667 853 * @return array
668 854 */
669 855 public function department_meta_query( $q ) {
856 + $request_department = $this->get_requested_department();
857 +
670 858
671 859 $meta_query = array();
672 860
673 - if ( isset( $_REQUEST['department'] ) && $_REQUEST['department'] != '' )
861 + if ( isset( $request_department ) && $request_department != '' )
674 862 {
675 863 $meta_query = array(
676 864 'key' => '_department',
677 - 'value' => sanitize_text_field( $_REQUEST['department'] ),
865 + 'value' => sanitize_text_field( $request_department ),
678 866 'compare' => '='
679 867 );
680 868 }
681 869 else
@@ -716,91 +904,314 @@
716 904 return $meta_query;
717 905 }
718 906
719 907 /**
908 + * Returns a meta query to handle featured
909 + *
910 + * @access public
911 + * @return array
912 + */
913 + public function featured_meta_query( ) {
914 +
915 + $meta_query = array();
916 +
917 + // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- The public property search reads a filter value and contributes query/meta/taxonomy arguments for the current request only; no persistent state-changing operation is reached. This exact annotation covers only NonceVerification.Recommended; retain all sanitizer and SQL-safety checks.
918 + if ( isset( $_REQUEST['featured'] ) && $_REQUEST['featured'] != '' )
919 + {
920 + $meta_query = array(
921 + 'key' => '_featured',
922 + 'value' => 'yes',
923 + 'compare' => '='
924 + );
925 + }
926 +
927 + return $meta_query;
928 + }
929 +
930 + /**
931 + * Returns a meta query to handle date added
932 + *
933 + * @access public
934 + * @return array
935 + */
936 + public function date_added_meta_query( ) {
937 +
938 + $meta_query = array();
939 +
940 + // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- The public property search reads a filter value and contributes query/meta/taxonomy arguments for the current request only; no persistent state-changing operation is reached. This exact annotation covers only NonceVerification.Recommended; retain all sanitizer and SQL-safety checks.
941 + if ( isset( $_REQUEST['date_added'] ) && $_REQUEST['date_added'] != '' && is_numeric($_REQUEST['date_added']) )
942 + {
943 + $meta_query = array(
944 + 'key' => '_on_market_change_date',
945 + // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- The public property search reads a filter value and contributes query/meta/taxonomy arguments for the current request only; no persistent state-changing operation is reached. This exact annotation covers only NonceVerification.Recommended; retain all sanitizer and SQL-safety checks.
946 + 'value' => gmdate('Y-m-d H:i:s', strtotime('-' . sanitize_text_field( wp_unslash( $_REQUEST['date_added'] ) ) . ' days')),
947 + 'compare' => '>=',
948 + 'type' => 'DATETIME',
949 + );
950 + }
951 +
952 + return $meta_query;
953 + }
954 +
955 + /**
720 956 * Returns a meta query to handle searching for a keyword in the address
721 957 *
722 958 * @access public
723 - * @param string $compare (default: 'IN')
724 959 * @return array
725 960 */
726 961 public function address_keyword_meta_query( ) {
727 962
728 963 $meta_query = array();
729 -
730 - if ( isset( $_REQUEST['address_keyword'] ) && $_REQUEST['address_keyword'] != '' )
964 +
965 + // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Public read-only search control; no persistent state change.
966 + if ( isset( $_REQUEST['address_keyword'] ) && !empty($_REQUEST['address_keyword']) )
731 967 {
732 - $_REQUEST['address_keyword'] = ph_clean( wp_unslash( $_REQUEST['address_keyword'] ) );
733 968
734 - // Remove country code from end (i.e. ', UK')
735 - $_REQUEST['address_keyword'] = preg_replace('/\,\s?[A-Z][A-Z]$/', '', $_REQUEST['address_keyword']);
969 + // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Public read-only address search; values are validated below before query construction.
970 + $address_input = ph_clean( wp_unslash( $_REQUEST['address_keyword'] ) );
971 + if ( ! is_string( $address_input ) && ! is_array( $address_input ) ) {
972 + return $meta_query;
973 + }
974 + foreach ( (array) $address_input as $address_value ) {
975 + if ( ! is_string( $address_value ) ) {
976 + return $meta_query;
977 + }
978 + }
979 + $address_input = ph_clean( $address_input );
980 + // Preserve the normalized request value consumed by existing extensions.
981 + $_REQUEST['address_keyword'] = $address_input;
736 982
737 - $address_keywords = array( $_REQUEST['address_keyword'] );
983 + $do_address_search = true;
984 + if ( is_string( $address_input ) && get_option( 'propertyhive_address_keyword_compare', '=' ) == 'polygon' )
985 + {
986 + $address_keyword_polygon = new PH_Address_Keyword_Polygon();
738 987
739 - if ( strpos( $_REQUEST['address_keyword'], ' ' ) !== FALSE )
740 - {
741 - $address_keywords[] = str_replace(" ", "-", ph_clean($_REQUEST['address_keyword']));
988 + $polygon_coordinates = $address_keyword_polygon->get_address_keyword_polygon_coordinates( $address_input . ', UK' );
989 +
990 + if ( $polygon_coordinates !== FALSE )
991 + {
992 + $this->address_keyword_polygon_points = $polygon_coordinates;
993 + add_filter( 'posts_where' , array( $this, 'where_properties_in_polygon' ), 1, 2 );
994 + $do_address_search = false;
995 + }
742 996 }
743 - if ( strpos( $_REQUEST['address_keyword'], '-' ) !== FALSE )
997 +
998 + if ( $do_address_search )
744 999 {
745 - $address_keywords[] = str_replace("-", " ", ph_clean($_REQUEST['address_keyword']));
746 - }
747 1000
748 - $meta_query = array('relation' => 'OR');
1001 + $address_keywords_to_query = is_array($address_input) ? $address_input : array( $address_input );
749 1002
750 - $address_fields_to_query = array(
751 - '_reference_number',
752 - '_address_street',
753 - '_address_two',
754 - '_address_three',
755 - '_address_four',
756 - '_address_postcode'
757 - );
1003 + $address_fields_to_query = array(
1004 + '_reference_number',
1005 + '_address_street',
1006 + '_address_two',
1007 + '_address_three',
1008 + '_address_four',
1009 + '_address_postcode',
1010 + '_address_concatenated',
1011 + );
758 1012
759 - $address_fields_to_query = apply_filters( 'propertyhive_address_fields_to_query', $address_fields_to_query );
1013 + $address_keywords = array();
760 1014
761 - foreach ( $address_keywords as $address_keyword )
762 - {
763 - foreach ( $address_fields_to_query as $address_field )
764 - {
765 - if ( $address_field == '_address_postcode' ) { continue; } // ignore postcode as that is handled differently afterwards
1015 + if ( !empty($address_keywords_to_query) )
1016 + {
1017 + foreach ( $address_keywords_to_query as $address_keyword )
1018 + {
1019 + // Remove country code from end (i.e. ', UK')
1020 + $address_keyword = preg_replace('/\,\s?[A-Z][A-Z]$/', '', $address_keyword);
766 1021
767 - $meta_query[] = array(
768 - 'key' => $address_field,
769 - 'value' => $address_keyword,
770 - 'compare' => get_option( 'propertyhive_address_keyword_compare', '=' )
771 - );
772 - }
773 - }
774 - if ( in_array('_address_postcode', $address_fields_to_query) )
775 - {
776 - if ( strlen($_REQUEST['address_keyword']) <= 4 )
1022 + // Extract postcode and use that if exists
1023 + $postcode_pattern = '/\b([A-Z]{1,2}[0-9][0-9A-Z]? ?[0-9]?[A-Z]{0,2})\b/i';
1024 + if ( preg_match($postcode_pattern, $address_keyword, $matches) )
1025 + {
1026 + $address_keyword = $matches[1];
1027 + }
1028 +
1029 + $address_keyword = trim($address_keyword);
1030 +
1031 + $address_keywords[] = ph_clean($address_keyword);
1032 +
1033 + if ( strpos( $address_keyword, ' ' ) !== FALSE )
1034 + {
1035 + $address_keywords[] = str_replace(" ", "-", ph_clean($address_keyword));
1036 + }
1037 + if ( strpos( $address_keyword, '-' ) !== FALSE )
1038 + {
1039 + $address_keywords[] = str_replace("-", " ", ph_clean($address_keyword));
1040 + }
1041 + if ( strpos( $address_keyword, '.' ) !== FALSE )
1042 + {
1043 + $address_keywords[] = str_replace(".", "", ph_clean($address_keyword));
1044 + }
1045 + if ( stripos( $address_keyword, 'st ' ) !== FALSE )
1046 + {
1047 + $address_keywords[] = str_ireplace("st ", "st. ", ph_clean($address_keyword));
1048 + }
1049 + if ( strpos( $address_keyword, '\'' ) !== FALSE )
1050 + {
1051 + $address_keywords[] = str_replace("'", "", ph_clean($address_keyword));
1052 + }
1053 + }
1054 + }
1055 +
1056 + $address_keywords = apply_filters( 'propertyhive_address_keywords_to_query', $address_keywords );
1057 +
1058 + $meta_query = array('relation' => 'OR');
1059 +
1060 + // add country to list of fields to query if it looks like we're working with an overseas site
1061 + $countries = get_option( 'propertyhive_countries', array() );
1062 + if ( !is_array($countries) ) { $countries = array(); }
1063 + if ( count($countries) > 1 )
777 1064 {
778 - $meta_query[] = array(
779 - 'key' => '_address_postcode',
780 - 'value' => ph_clean( $_REQUEST['address_keyword'] ),
781 - 'compare' => '='
782 - );
783 - $meta_query[] = array(
784 - 'key' => '_address_postcode',
785 - 'value' => '^' . ph_clean( $_REQUEST['address_keyword'] ) . '[ ]',
786 - 'compare' => 'RLIKE'
787 - );
1065 + $address_fields_to_query[] = '_address_country';
788 1066 }
789 - else
1067 +
1068 + $address_fields_to_query = array_unique($address_fields_to_query);
1069 + $address_fields_to_query = apply_filters( 'propertyhive_address_fields_to_query', $address_fields_to_query );
1070 +
1071 + foreach ( $address_keywords as $address_keyword )
790 1072 {
791 - $meta_query[] = array(
792 - 'key' => '_address_postcode',
793 - 'value' => ph_clean( $_REQUEST['address_keyword'] ),
794 - 'compare' => 'LIKE'
795 - );
796 - }
797 - }
1073 + foreach ( $address_fields_to_query as $address_field )
1074 + {
1075 + if ( in_array( $address_field, array('_address_postcode', '_address_country', '_address_concatenated') ) ) { continue; } // ignore postcode and country as they're handled differently afterwards
1076 +
1077 + $meta_query[] = array(
1078 + 'key' => $address_field,
1079 + 'value' => $address_keyword,
1080 + 'compare' => get_option( 'propertyhive_address_keyword_compare', '=' )
1081 + );
1082 + }
1083 +
1084 + if ( in_array('_address_postcode', $address_fields_to_query) )
1085 + {
1086 + if ( strlen($address_keyword) <= 4 )
1087 + {
1088 + $meta_query[] = array(
1089 + 'key' => '_address_postcode',
1090 + 'value' => ph_clean($address_keyword),
1091 + 'compare' => '='
1092 + );
1093 + // Run regex match where given keyword is at the start of the postcode ^
1094 + // followed by one or zero letters (for WC2E-style postcodes) [a-zA-Z]?
1095 + // then a single space [ ]
1096 + $meta_query[] = array(
1097 + 'key' => '_address_postcode',
1098 + 'value' => '^' . ph_clean($address_keyword) . '[a-zA-Z]?[ ]',
1099 + 'compare' => 'RLIKE'
1100 + );
1101 + }
1102 + else
1103 + {
1104 + $postcode = ph_clean($address_keyword);
1105 +
1106 + if ( preg_match('#^(GIR ?0AA|[A-PR-UWYZ]([0-9]{1,2}|([A-HK-Y][0-9]([0-9ABEHMNPRV-Y])?)|[0-9][A-HJKPS-UW])[0-9][ABD-HJLNP-UW-Z]{2})$#i', $postcode) )
1107 + {
1108 + // UK postcode found with no space
1109 +
1110 + if ( strlen($postcode) == 5 )
1111 + {
1112 + $first_part = substr($postcode, 0, 2);
1113 + $last_part = substr($postcode, 2, 3);
1114 +
1115 + $postcode = $first_part . ' ' . $last_part;
1116 + }
1117 + elseif ( strlen($postcode) == 6 )
1118 + {
1119 + $first_part = substr($postcode, 0, 3);
1120 + $last_part = substr($postcode, 3, 3);
1121 +
1122 + $postcode = $first_part . ' ' . $last_part;
1123 + }
1124 + elseif ( strlen($postcode) == 7 )
1125 + {
1126 + $first_part = substr($postcode, 0, 4);
1127 + $last_part = substr($postcode, 4, 3);
1128 +
1129 + $postcode = $first_part . ' ' . $last_part;
1130 + }
1131 + }
1132 +
1133 + $meta_query[] = array(
1134 + 'key' => '_address_postcode',
1135 + 'value' => ph_clean( $postcode ),
1136 + 'compare' => 'LIKE'
1137 + );
1138 + }
1139 + }
1140 +
1141 + if ( in_array('_address_country', $address_fields_to_query) )
1142 + {
1143 + $meta_query[] = array(
1144 + 'key' => '_address_country',
1145 + 'value' => $address_keyword,
1146 + 'compare' => '='
1147 + );
1148 +
1149 + // get country code for country entered
1150 + $PH_Countries = new PH_Countries();
1151 + $countries = $PH_Countries->countries;
1152 + if ( is_array($countries) && !empty($countries) )
1153 + {
1154 + foreach ( $countries as $country_code => $country )
1155 + {
1156 + if ( strtolower($address_keyword) == strtolower($country['name']) )
1157 + {
1158 + $meta_query[] = array(
1159 + 'key' => '_address_country',
1160 + 'value' => $country_code,
1161 + 'compare' => '='
1162 + );
1163 + break;
1164 + }
1165 + }
1166 + }
1167 + }
1168 +
1169 + if (
1170 + !preg_match('/^(?:[A-Z]{2}\d|[A-Z]\d)/i', $address_keyword) &&
1171 + in_array('_address_concatenated', $address_fields_to_query)
1172 + )
1173 + {
1174 + $meta_query[] = array(
1175 + 'key' => '_address_concatenated',
1176 + 'value' => $address_keyword,
1177 + 'compare' => 'LIKE'
1178 + );
1179 + }
1180 + }
1181 +
1182 + }
798 1183 }
799 1184
800 1185 return $meta_query;
801 1186 }
802 1187
1188 + public function where_properties_in_polygon( $where, $query )
1189 + {
1190 + global $wpdb;
1191 +
1192 + if ( !empty($this->address_keyword_polygon_points) )
1193 + {
1194 + $where .= " AND
1195 + ST_CONTAINS(
1196 + ST_GEOMFROMTEXT('POLYGON((" . implode(", ", $this->address_keyword_polygon_points) . "))'),
1197 + ST_GEOMFROMTEXT(
1198 + CONCAT(
1199 + 'POINT(',
1200 + COALESCE((SELECT meta_value FROM $wpdb->postmeta WHERE $wpdb->postmeta.meta_key='_latitude' AND $wpdb->postmeta.meta_value != '' AND $wpdb->postmeta.meta_value != 0 AND $wpdb->postmeta.post_id = $wpdb->posts.ID LIMIT 1), '0'),
1201 + ' ',
1202 + COALESCE((SELECT meta_value FROM $wpdb->postmeta WHERE $wpdb->postmeta.meta_key='_longitude' AND $wpdb->postmeta.meta_value != '' AND $wpdb->postmeta.meta_value != 0 AND $wpdb->postmeta.post_id = $wpdb->posts.ID LIMIT 1), '0'),
1203 + ')'
1204 + )
1205 + )
1206 + )";
1207 + }
1208 +
1209 + remove_filter( 'posts_where' , array( $this, 'where_properties_in_polygon' ), 1, 2 );
1210 +
1211 + return $where;
1212 + }
1213 +
803 1214 /**
804 1215 * Returns a meta query to handle country
805 1216 *
806 1217 * @access public
@@ -809,15 +1220,28 @@
809 1220 public function country_meta_query( ) {
810 1221
811 1222 $meta_query = array();
812 1223
1224 + // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- The public property search reads a filter value and contributes query/meta/taxonomy arguments for the current request only; no persistent state-changing operation is reached. This exact annotation covers only NonceVerification.Recommended; retain all sanitizer and SQL-safety checks.
813 1225 if ( isset( $_REQUEST['country'] ) && $_REQUEST['country'] != '' )
814 1226 {
815 1227 $meta_query = array(
816 1228 'key' => '_address_country',
817 - 'value' => ph_clean( $_REQUEST['country'] )
1229 + // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- The public property search reads a filter value and contributes query/meta/taxonomy arguments for the current request only; no persistent state-changing operation is reached. This exact annotation covers only NonceVerification.Recommended; retain all sanitizer and SQL-safety checks.
1230 + 'value' => ph_clean( wp_unslash( $_REQUEST['country'] ) )
818 1231 );
819 1232 }
1233 +
1234 + // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- The public property search reads a filter value and contributes query/meta/taxonomy arguments for the current request only; no persistent state-changing operation is reached. This exact annotation covers only NonceVerification.Recommended; retain all sanitizer and SQL-safety checks.
1235 + if ( isset( $_REQUEST['country_not'] ) && $_REQUEST['country_not'] != '' )
1236 + {
1237 + $meta_query = array(
1238 + 'key' => '_address_country',
1239 + // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- The public property search reads a filter value and contributes query/meta/taxonomy arguments for the current request only; no persistent state-changing operation is reached. This exact annotation covers only NonceVerification.Recommended; retain all sanitizer and SQL-safety checks.
1240 + 'value' => ph_clean( wp_unslash( $_REQUEST['country_not'] ) ),
1241 + 'compare' => '!='
1242 + );
1243 + }
820 1244
821 1245 return $meta_query;
822 1246 }
823 1247
@@ -827,19 +1251,30 @@
827 1251 * @access public
828 1252 * @return array
829 1253 */
830 1254 public function minimum_price_meta_query( ) {
1255 + $request_department = $this->get_requested_department();
1256 +
831 1257
832 1258 $meta_query = array();
833 1259
834 1260 if (
835 - isset( $_REQUEST['department'] ) && $_REQUEST['department'] == 'residential-sales' &&
1261 + isset( $request_department ) && ( $request_department == 'residential-sales' || ph_get_custom_department_based_on($request_department) == 'residential-sales' ) &&
1262 + // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- The public property search reads a filter value and contributes query/meta/taxonomy arguments for the current request only; no persistent state-changing operation is reached. This exact annotation covers only NonceVerification.Recommended; retain all sanitizer and SQL-safety checks.
836 1263 isset( $_REQUEST['minimum_price'] ) && $_REQUEST['minimum_price'] != ''
837 1264 )
838 1265 {
1266 + // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- The public property search reads a filter value and contributes query/meta/taxonomy arguments for the current request only; no persistent state-changing operation is reached. This exact annotation covers only NonceVerification.Recommended; retain all sanitizer and SQL-safety checks.
1267 + $minimum_price = is_string( $_REQUEST['minimum_price'] ) ? sanitize_text_field( wp_unslash( $_REQUEST['minimum_price'] ) ) : '';
1268 +
1269 + if ( !is_numeric($minimum_price) )
1270 + {
1271 + return $meta_query;
1272 + }
1273 +
839 1274 $search_form_currency = get_option( 'propertyhive_search_form_currency', 'GBP' );
1275 + $search_form_currency = apply_filters( 'propertyhive_query_search_form_currency', $search_form_currency );
840 1276
841 - $minimum_price = $_REQUEST['minimum_price'];
842 1277 if ( $search_form_currency != 'GBP' )
843 1278 {
844 1279 // Convert $_REQUEST['minimum_price'] to GBP
845 1280 $ph_countries = new PH_Countries();
@@ -864,19 +1299,30 @@
864 1299 * @access public
865 1300 * @return array
866 1301 */
867 1302 public function maximum_price_meta_query( ) {
1303 + $request_department = $this->get_requested_department();
1304 +
868 1305
869 1306 $meta_query = array();
870 1307
871 1308 if (
872 - isset( $_REQUEST['department'] ) && $_REQUEST['department'] == 'residential-sales' &&
1309 + isset( $request_department ) && ( $request_department == 'residential-sales' || ph_get_custom_department_based_on($request_department) == 'residential-sales' ) &&
1310 + // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- The public property search reads a filter value and contributes query/meta/taxonomy arguments for the current request only; no persistent state-changing operation is reached. This exact annotation covers only NonceVerification.Recommended; retain all sanitizer and SQL-safety checks.
873 1311 isset( $_REQUEST['maximum_price'] ) && $_REQUEST['maximum_price'] != ''
874 1312 )
875 1313 {
1314 + // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- The public property search reads a filter value and contributes query/meta/taxonomy arguments for the current request only; no persistent state-changing operation is reached. This exact annotation covers only NonceVerification.Recommended; retain all sanitizer and SQL-safety checks.
1315 + $maximum_price = is_string( $_REQUEST['maximum_price'] ) ? sanitize_text_field( wp_unslash( $_REQUEST['maximum_price'] ) ) : '';
1316 +
1317 + if ( !is_numeric($maximum_price) )
1318 + {
1319 + return $meta_query;
1320 + }
1321 +
876 1322 $search_form_currency = get_option( 'propertyhive_search_form_currency', 'GBP' );
1323 + $search_form_currency = apply_filters( 'propertyhive_query_search_form_currency', $search_form_currency );
877 1324
878 - $maximum_price = $_REQUEST['maximum_price'];
879 1325 if ( $search_form_currency != 'GBP' )
880 1326 {
881 1327 // Convert $_REQUEST['maximum_price'] to GBP
882 1328 $ph_countries = new PH_Countries();
@@ -901,24 +1347,34 @@
901 1347 * @access public
902 1348 * @return array
903 1349 */
904 1350 public function price_range_meta_query( ) {
1351 + $request_department = $this->get_requested_department();
1352 +
905 1353
906 1354 $meta_query = array();
907 1355
908 1356 if (
909 - isset( $_REQUEST['department'] ) && $_REQUEST['department'] == 'residential-sales' &&
910 - isset( $_REQUEST['price_range'] ) && $_REQUEST['price_range'] != ''
1357 + isset( $request_department ) && ( $request_department == 'residential-sales' || ph_get_custom_department_based_on($request_department) == 'residential-sales' ) &&
1358 + // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- The public property search reads a filter value and contributes query/meta/taxonomy arguments for the current request only; no persistent state-changing operation is reached. This exact annotation covers only NonceVerification.Recommended; retain all sanitizer and SQL-safety checks.
1359 + isset( $_REQUEST['price_range'] ) && is_string( $_REQUEST['price_range'] ) && $_REQUEST['price_range'] != ''
911 1360 )
912 1361 {
913 - $explode_price_range = explode("-", ph_clean($_REQUEST['price_range']));
1362 + // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- The public property search reads a filter value and contributes query/meta/taxonomy arguments for the current request only; no persistent state-changing operation is reached. This exact annotation covers only NonceVerification.Recommended; retain all sanitizer and SQL-safety checks.
1363 + $explode_price_range = explode("-", ph_clean( wp_unslash( $_REQUEST['price_range'] ) ));
914 1364
915 1365 $search_form_currency = get_option( 'propertyhive_search_form_currency', 'GBP' );
1366 + $search_form_currency = apply_filters( 'propertyhive_query_search_form_currency', $search_form_currency );
916 1367
917 1368 if ( isset($explode_price_range[0]) && $explode_price_range[0] != '' )
918 1369 {
919 1370 $minimum_price = $explode_price_range[0];
920 1371
1372 + if ( !is_numeric($minimum_price) )
1373 + {
1374 + return $meta_query;
1375 + }
1376 +
921 1377 if ( $search_form_currency != 'GBP' )
922 1378 {
923 1379 // Convert $explode_price_range[0] to GBP
924 1380 $ph_countries = new PH_Countries();
@@ -935,8 +1391,14 @@
935 1391 }
936 1392 if ( isset($explode_price_range[1]) && $explode_price_range[1] != '' )
937 1393 {
938 1394 $maximum_price = $explode_price_range[1];
1395 +
1396 + if ( !is_numeric($maximum_price) )
1397 + {
1398 + return $meta_query;
1399 + }
1400 +
939 1401 if ( $search_form_currency != 'GBP' )
940 1402 {
941 1403 // Convert $explode_price_range[1] to GBP
942 1404 $ph_countries = new PH_Countries();
@@ -962,19 +1424,30 @@
962 1424 * @access public
963 1425 * @return array
964 1426 */
965 1427 public function minimum_rent_meta_query( ) {
1428 + $request_department = $this->get_requested_department();
1429 +
966 1430
967 1431 $meta_query = array();
968 1432
969 1433 if (
970 - isset( $_REQUEST['department'] ) && $_REQUEST['department'] == 'residential-lettings' &&
1434 + isset( $request_department ) && ( $request_department == 'residential-lettings' || ph_get_custom_department_based_on($request_department) == 'residential-lettings' ) &&
1435 + // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- The public property search reads a filter value and contributes query/meta/taxonomy arguments for the current request only; no persistent state-changing operation is reached. This exact annotation covers only NonceVerification.Recommended; retain all sanitizer and SQL-safety checks.
971 1436 isset( $_REQUEST['minimum_rent'] ) && $_REQUEST['minimum_rent'] != ''
972 1437 )
973 1438 {
1439 + // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- The public property search reads a filter value and contributes query/meta/taxonomy arguments for the current request only; no persistent state-changing operation is reached. This exact annotation covers only NonceVerification.Recommended; retain all sanitizer and SQL-safety checks.
1440 + $minimum_rent = is_string( $_REQUEST['minimum_rent'] ) ? sanitize_text_field( wp_unslash( $_REQUEST['minimum_rent'] ) ) : '';
1441 +
1442 + if ( !is_numeric($minimum_rent) )
1443 + {
1444 + return $meta_query;
1445 + }
1446 +
974 1447 $search_form_currency = get_option( 'propertyhive_search_form_currency', 'GBP' );
1448 + $search_form_currency = apply_filters( 'propertyhive_query_search_form_currency', $search_form_currency );
975 1449
976 - $minimum_rent = $_REQUEST['minimum_rent'];
977 1450 if ( $search_form_currency != 'GBP' )
978 1451 {
979 1452 // Convert $_REQUEST['minimum_rent'] to GBP
980 1453 $ph_countries = new PH_Countries();
@@ -981,8 +1454,17 @@
981 1454
982 1455 $minimum_rent = $ph_countries->convert_price_to_gbp( $minimum_rent, $search_form_currency );
983 1456 }
984 1457
1458 + $rent_frequency = apply_filters( 'propertyhive_search_form_rent_frequency', 'pcm' );
1459 + switch ($rent_frequency)
1460 + {
1461 + case "pd": { $minimum_rent = ($minimum_rent * 365) / 12; break; }
1462 + case "pw": { $minimum_rent = ($minimum_rent * 52) / 12; break; }
1463 + case "pq": { $minimum_rent = ($minimum_rent * 4) / 12; break; }
1464 + case "pa": { $minimum_rent = $minimum_rent / 12; break; }
1465 + }
1466 +
985 1467 $meta_query = array(
986 1468 'key' => '_price_actual',
987 1469 'value' => ph_clean( floor( $minimum_rent ) ),
988 1470 'compare' => '>=',
@@ -999,19 +1481,30 @@
999 1481 * @access public
1000 1482 * @return array
1001 1483 */
1002 1484 public function maximum_rent_meta_query( ) {
1485 + $request_department = $this->get_requested_department();
1486 +
1003 1487
1004 1488 $meta_query = array();
1005 1489
1006 1490 if (
1007 - isset( $_REQUEST['department'] ) && $_REQUEST['department'] == 'residential-lettings' &&
1491 + isset( $request_department ) && ( $request_department == 'residential-lettings' || ph_get_custom_department_based_on($request_department) == 'residential-lettings' ) &&
1492 + // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- The public property search reads a filter value and contributes query/meta/taxonomy arguments for the current request only; no persistent state-changing operation is reached. This exact annotation covers only NonceVerification.Recommended; retain all sanitizer and SQL-safety checks.
1008 1493 isset( $_REQUEST['maximum_rent'] ) && $_REQUEST['maximum_rent'] != ''
1009 1494 )
1010 1495 {
1496 + // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- The public property search reads a filter value and contributes query/meta/taxonomy arguments for the current request only; no persistent state-changing operation is reached. This exact annotation covers only NonceVerification.Recommended; retain all sanitizer and SQL-safety checks.
1497 + $maximum_rent = is_string( $_REQUEST['maximum_rent'] ) ? sanitize_text_field( wp_unslash( $_REQUEST['maximum_rent'] ) ) : '';
1498 +
1499 + if ( !is_numeric($maximum_rent) )
1500 + {
1501 + return $meta_query;
1502 + }
1503 +
1011 1504 $search_form_currency = get_option( 'propertyhive_search_form_currency', 'GBP' );
1505 + $search_form_currency = apply_filters( 'propertyhive_query_search_form_currency', $search_form_currency );
1012 1506
1013 - $maximum_rent = $_REQUEST['maximum_rent'];
1014 1507 if ( $search_form_currency != 'GBP' )
1015 1508 {
1016 1509 // Convert $_REQUEST['maximum_rent'] to GBP
1017 1510 $ph_countries = new PH_Countries();
@@ -1018,8 +1511,17 @@
1018 1511
1019 1512 $maximum_rent = $ph_countries->convert_price_to_gbp( $maximum_rent, $search_form_currency );
1020 1513 }
1021 1514
1515 + $rent_frequency = apply_filters( 'propertyhive_search_form_rent_frequency', 'pcm' );
1516 + switch ($rent_frequency)
1517 + {
1518 + case "pd": { $maximum_rent = ($maximum_rent * 365) / 12; break; }
1519 + case "pw": { $maximum_rent = ($maximum_rent * 52) / 12; break; }
1520 + case "pq": { $maximum_rent = ($maximum_rent * 4) / 12; break; }
1521 + case "pa": { $maximum_rent = $maximum_rent / 12; break; }
1522 + }
1523 +
1022 1524 $meta_query = array(
1023 1525 'key' => '_price_actual',
1024 1526 'value' => ph_clean( ceil( $maximum_rent ) ),
1025 1527 'compare' => '<=',
@@ -1036,23 +1538,36 @@
1036 1538 * @access public
1037 1539 * @return array
1038 1540 */
1039 1541 public function rent_range_meta_query( ) {
1542 + $request_department = $this->get_requested_department();
1543 +
1040 1544
1041 1545 $meta_query = array();
1042 1546
1043 1547 if (
1044 - isset( $_REQUEST['department'] ) && $_REQUEST['department'] == 'residential-lettings' &&
1045 - isset( $_REQUEST['rent_range'] ) && $_REQUEST['rent_range'] != ''
1548 + isset( $request_department ) && ( $request_department == 'residential-lettings' || ph_get_custom_department_based_on($request_department) == 'residential-lettings' ) &&
1549 + // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- The public property search reads a filter value and contributes query/meta/taxonomy arguments for the current request only; no persistent state-changing operation is reached. This exact annotation covers only NonceVerification.Recommended; retain all sanitizer and SQL-safety checks.
1550 + isset( $_REQUEST['rent_range'] ) && is_string( $_REQUEST['rent_range'] ) && $_REQUEST['rent_range'] != ''
1046 1551 )
1047 1552 {
1048 - $explode_rent_range = explode("-", ph_clean($_REQUEST['rent_range']));
1553 + // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- The public property search reads a filter value and contributes query/meta/taxonomy arguments for the current request only; no persistent state-changing operation is reached. This exact annotation covers only NonceVerification.Recommended; retain all sanitizer and SQL-safety checks.
1554 + $explode_rent_range = explode("-", ph_clean( wp_unslash( $_REQUEST['rent_range'] ) ));
1049 1555
1050 1556 $search_form_currency = get_option( 'propertyhive_search_form_currency', 'GBP' );
1557 + $search_form_currency = apply_filters( 'propertyhive_query_search_form_currency', $search_form_currency );
1051 1558
1559 + $rent_frequency = apply_filters( 'propertyhive_search_form_rent_frequency', 'pcm' );
1560 +
1052 1561 if ( isset($explode_rent_range[0]) && $explode_rent_range[0] != '' )
1053 1562 {
1054 1563 $minimum_rent = $explode_rent_range[0];
1564 +
1565 + if ( !is_numeric($minimum_rent) )
1566 + {
1567 + return $meta_query;
1568 + }
1569 +
1055 1570 if ( $search_form_currency != 'GBP' )
1056 1571 {
1057 1572 // Convert $explode_rent_range[0] to GBP
1058 1573 $ph_countries = new PH_Countries();
@@ -1059,8 +1574,16 @@
1059 1574
1060 1575 $minimum_rent = $ph_countries->convert_price_to_gbp( $minimum_rent, $search_form_currency );
1061 1576 }
1062 1577
1578 + switch ($rent_frequency)
1579 + {
1580 + case "pd": { $minimum_rent = ($minimum_rent * 365) / 12; break; }
1581 + case "pw": { $minimum_rent = ($minimum_rent * 52) / 12; break; }
1582 + case "pq": { $minimum_rent = ($minimum_rent * 4) / 12; break; }
1583 + case "pa": { $minimum_rent = $minimum_rent / 12; break; }
1584 + }
1585 +
1063 1586 $meta_query[] = array(
1064 1587 'key' => '_price_actual',
1065 1588 'value' => sanitize_text_field( floor( $minimum_rent ) ),
1066 1589 'compare' => '>=',
@@ -1069,8 +1592,14 @@
1069 1592 }
1070 1593 if ( isset($explode_rent_range[1]) && $explode_rent_range[1] != '' )
1071 1594 {
1072 1595 $maximum_rent = $explode_rent_range[1];
1596 +
1597 + if ( !is_numeric($maximum_rent) )
1598 + {
1599 + return $meta_query;
1600 + }
1601 +
1073 1602 if ( $search_form_currency != 'GBP' )
1074 1603 {
1075 1604 // Convert $explode_rent_range[1] to GBP
1076 1605 $ph_countries = new PH_Countries();
@@ -1077,8 +1606,16 @@
1077 1606
1078 1607 $maximum_rent = $ph_countries->convert_price_to_gbp( $maximum_rent, $search_form_currency );
1079 1608 }
1080 1609
1610 + switch ($rent_frequency)
1611 + {
1612 + case "pd": { $maximum_rent = ($maximum_rent * 365) / 12; break; }
1613 + case "pw": { $maximum_rent = ($maximum_rent * 52) / 12; break; }
1614 + case "pq": { $maximum_rent = ($maximum_rent * 4) / 12; break; }
1615 + case "pa": { $maximum_rent = $maximum_rent / 12; break; }
1616 + }
1617 +
1081 1618 $meta_query[] = array(
1082 1619 'key' => '_price_actual',
1083 1620 'value' => sanitize_text_field( ceil( $maximum_rent ) ),
1084 1621 'compare' => '<=',
@@ -1096,22 +1633,26 @@
1096 1633 * @access public
1097 1634 * @return array
1098 1635 */
1099 1636 public function bedrooms_meta_query( ) {
1637 + $request_department = $this->get_requested_department();
1638 +
1100 1639
1101 1640 $meta_query = array();
1102 1641
1103 1642 if (
1104 1643 (
1105 - (isset( $_REQUEST['department'] ) && $_REQUEST['department'] == 'residential-sales') ||
1106 - (isset( $_REQUEST['department'] ) && $_REQUEST['department'] == 'residential-lettings')
1644 + (isset( $request_department ) && ( $request_department == 'residential-sales' || ph_get_custom_department_based_on($request_department) == 'residential-sales' )) ||
1645 + (isset( $request_department ) && ( $request_department == 'residential-lettings' || ph_get_custom_department_based_on($request_department) == 'residential-lettings' ))
1107 1646 ) &&
1647 + // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- The public property search reads a filter value and contributes query/meta/taxonomy arguments for the current request only; no persistent state-changing operation is reached. This exact annotation covers only NonceVerification.Recommended; retain all sanitizer and SQL-safety checks.
1108 1648 isset( $_REQUEST['bedrooms'] ) && $_REQUEST['bedrooms'] != ''
1109 1649 )
1110 1650 {
1111 1651 $meta_query = array(
1112 1652 'key' => '_bedrooms',
1113 - 'value' => ph_clean( $_REQUEST['bedrooms'] ),
1653 + // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- The public property search reads a filter value and contributes query/meta/taxonomy arguments for the current request only; no persistent state-changing operation is reached. This exact annotation covers only NonceVerification.Recommended; retain all sanitizer and SQL-safety checks.
1654 + 'value' => ph_clean( wp_unslash( $_REQUEST['bedrooms'] ) ),
1114 1655 'compare' => '=',
1115 1656 'type' => 'NUMERIC'
1116 1657 );
1117 1658 }
@@ -1125,22 +1666,26 @@
1125 1666 * @access public
1126 1667 * @return array
1127 1668 */
1128 1669 public function minimum_bedrooms_meta_query( ) {
1670 + $request_department = $this->get_requested_department();
1671 +
1129 1672
1130 1673 $meta_query = array();
1131 1674
1132 1675 if (
1133 1676 (
1134 - (isset( $_REQUEST['department'] ) && $_REQUEST['department'] == 'residential-sales') ||
1135 - (isset( $_REQUEST['department'] ) && $_REQUEST['department'] == 'residential-lettings')
1677 + (isset( $request_department ) && ( $request_department == 'residential-sales' || ph_get_custom_department_based_on($request_department) == 'residential-sales' )) ||
1678 + (isset( $request_department ) && ( $request_department == 'residential-lettings' || ph_get_custom_department_based_on($request_department) == 'residential-lettings' ))
1136 1679 ) &&
1680 + // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- The public property search reads a filter value and contributes query/meta/taxonomy arguments for the current request only; no persistent state-changing operation is reached. This exact annotation covers only NonceVerification.Recommended; retain all sanitizer and SQL-safety checks.
1137 1681 isset( $_REQUEST['minimum_bedrooms'] ) && $_REQUEST['minimum_bedrooms'] != ''
1138 1682 )
1139 1683 {
1140 1684 $meta_query = array(
1141 1685 'key' => '_bedrooms',
1142 - 'value' => ph_clean( $_REQUEST['minimum_bedrooms'] ),
1686 + // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- The public property search reads a filter value and contributes query/meta/taxonomy arguments for the current request only; no persistent state-changing operation is reached. This exact annotation covers only NonceVerification.Recommended; retain all sanitizer and SQL-safety checks.
1687 + 'value' => ph_clean( wp_unslash( $_REQUEST['minimum_bedrooms'] ) ),
1143 1688 'compare' => '>=',
1144 1689 'type' => 'NUMERIC'
1145 1690 );
1146 1691 }
@@ -1154,22 +1699,26 @@
1154 1699 * @access public
1155 1700 * @return array
1156 1701 */
1157 1702 public function maximum_bedrooms_meta_query( ) {
1703 + $request_department = $this->get_requested_department();
1704 +
1158 1705
1159 1706 $meta_query = array();
1160 1707
1161 1708 if (
1162 1709 (
1163 - (isset( $_REQUEST['department'] ) && $_REQUEST['department'] == 'residential-sales') ||
1164 - (isset( $_REQUEST['department'] ) && $_REQUEST['department'] == 'residential-lettings')
1710 + (isset( $request_department ) && ( $request_department == 'residential-sales' || ph_get_custom_department_based_on($request_department) == 'residential-sales' )) ||
1711 + (isset( $request_department ) && ( $request_department == 'residential-lettings' || ph_get_custom_department_based_on($request_department) == 'residential-lettings' ))
1165 1712 ) &&
1713 + // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- The public property search reads a filter value and contributes query/meta/taxonomy arguments for the current request only; no persistent state-changing operation is reached. This exact annotation covers only NonceVerification.Recommended; retain all sanitizer and SQL-safety checks.
1166 1714 isset( $_REQUEST['maximum_bedrooms'] ) && $_REQUEST['maximum_bedrooms'] != ''
1167 1715 )
1168 1716 {
1169 1717 $meta_query = array(
1170 1718 'key' => '_bedrooms',
1171 - 'value' => ph_clean( $_REQUEST['maximum_bedrooms'] ),
1719 + // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- The public property search reads a filter value and contributes query/meta/taxonomy arguments for the current request only; no persistent state-changing operation is reached. This exact annotation covers only NonceVerification.Recommended; retain all sanitizer and SQL-safety checks.
1720 + 'value' => ph_clean( wp_unslash( $_REQUEST['maximum_bedrooms'] ) ),
1172 1721 'compare' => '<=',
1173 1722 'type' => 'NUMERIC'
1174 1723 );
1175 1724 }
@@ -1183,22 +1732,26 @@
1183 1732 * @access public
1184 1733 * @return array
1185 1734 */
1186 1735 public function minimum_bathrooms_meta_query( ) {
1736 + $request_department = $this->get_requested_department();
1737 +
1187 1738
1188 1739 $meta_query = array();
1189 1740
1190 1741 if (
1191 1742 (
1192 - (isset( $_REQUEST['department'] ) && $_REQUEST['department'] == 'residential-sales') ||
1193 - (isset( $_REQUEST['department'] ) && $_REQUEST['department'] == 'residential-lettings')
1743 + (isset( $request_department ) && ( $request_department == 'residential-sales' || ph_get_custom_department_based_on($request_department) == 'residential-sales' )) ||
1744 + (isset( $request_department ) && ( $request_department == 'residential-lettings' || ph_get_custom_department_based_on($request_department) == 'residential-lettings' ))
1194 1745 ) &&
1746 + // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- The public property search reads a filter value and contributes query/meta/taxonomy arguments for the current request only; no persistent state-changing operation is reached. This exact annotation covers only NonceVerification.Recommended; retain all sanitizer and SQL-safety checks.
1195 1747 isset( $_REQUEST['minimum_bathrooms'] ) && $_REQUEST['minimum_bathrooms'] != ''
1196 1748 )
1197 1749 {
1198 1750 $meta_query = array(
1199 1751 'key' => '_bathrooms',
1200 - 'value' => ph_clean( $_REQUEST['minimum_bathrooms'] ),
1752 + // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- The public property search reads a filter value and contributes query/meta/taxonomy arguments for the current request only; no persistent state-changing operation is reached. This exact annotation covers only NonceVerification.Recommended; retain all sanitizer and SQL-safety checks.
1753 + 'value' => ph_clean( wp_unslash( $_REQUEST['minimum_bathrooms'] ) ),
1201 1754 'compare' => '>=',
1202 1755 'type' => 'NUMERIC'
1203 1756 );
1204 1757 }
@@ -1212,22 +1765,26 @@
1212 1765 * @access public
1213 1766 * @return array
1214 1767 */
1215 1768 public function maximum_bathrooms_meta_query( ) {
1769 + $request_department = $this->get_requested_department();
1770 +
1216 1771
1217 1772 $meta_query = array();
1218 1773
1219 1774 if (
1220 1775 (
1221 - (isset( $_REQUEST['department'] ) && $_REQUEST['department'] == 'residential-sales') ||
1222 - (isset( $_REQUEST['department'] ) && $_REQUEST['department'] == 'residential-lettings')
1776 + (isset( $request_department ) && ( $request_department == 'residential-sales' || ph_get_custom_department_based_on($request_department) == 'residential-sales' )) ||
1777 + (isset( $request_department ) && ( $request_department == 'residential-lettings' || ph_get_custom_department_based_on($request_department) == 'residential-lettings' ))
1223 1778 ) &&
1779 + // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- The public property search reads a filter value and contributes query/meta/taxonomy arguments for the current request only; no persistent state-changing operation is reached. This exact annotation covers only NonceVerification.Recommended; retain all sanitizer and SQL-safety checks.
1224 1780 isset( $_REQUEST['maximum_bathrooms'] ) && $_REQUEST['maximum_bathrooms'] != ''
1225 1781 )
1226 1782 {
1227 1783 $meta_query = array(
1228 1784 'key' => '_bathrooms',
1229 - 'value' => ph_clean( $_REQUEST['maximum_bathrooms'] ),
1785 + // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- The public property search reads a filter value and contributes query/meta/taxonomy arguments for the current request only; no persistent state-changing operation is reached. This exact annotation covers only NonceVerification.Recommended; retain all sanitizer and SQL-safety checks.
1786 + 'value' => ph_clean( wp_unslash( $_REQUEST['maximum_bathrooms'] ) ),
1230 1787 'compare' => '<=',
1231 1788 'type' => 'NUMERIC'
1232 1789 );
1233 1790 }
@@ -1241,22 +1798,26 @@
1241 1798 * @access public
1242 1799 * @return array
1243 1800 */
1244 1801 public function minimum_reception_rooms_meta_query( ) {
1802 + $request_department = $this->get_requested_department();
1803 +
1245 1804
1246 1805 $meta_query = array();
1247 1806
1248 1807 if (
1249 1808 (
1250 - (isset( $_REQUEST['department'] ) && $_REQUEST['department'] == 'residential-sales') ||
1251 - (isset( $_REQUEST['department'] ) && $_REQUEST['department'] == 'residential-lettings')
1809 + (isset( $request_department ) && ( $request_department == 'residential-sales' || ph_get_custom_department_based_on($request_department) == 'residential-sales' )) ||
1810 + (isset( $request_department ) && ( $request_department == 'residential-lettings' || ph_get_custom_department_based_on($request_department) == 'residential-lettings' ))
1252 1811 ) &&
1812 + // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- The public property search reads a filter value and contributes query/meta/taxonomy arguments for the current request only; no persistent state-changing operation is reached. This exact annotation covers only NonceVerification.Recommended; retain all sanitizer and SQL-safety checks.
1253 1813 isset( $_REQUEST['minimum_reception_rooms'] ) && $_REQUEST['minimum_reception_rooms'] != ''
1254 1814 )
1255 1815 {
1256 1816 $meta_query = array(
1257 1817 'key' => '_reception_rooms',
1258 - 'value' => ph_clean( $_REQUEST['minimum_reception_rooms'] ),
1818 + // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- The public property search reads a filter value and contributes query/meta/taxonomy arguments for the current request only; no persistent state-changing operation is reached. This exact annotation covers only NonceVerification.Recommended; retain all sanitizer and SQL-safety checks.
1819 + 'value' => ph_clean( wp_unslash( $_REQUEST['minimum_reception_rooms'] ) ),
1259 1820 'compare' => '>=',
1260 1821 'type' => 'NUMERIC'
1261 1822 );
1262 1823 }
@@ -1270,22 +1831,26 @@
1270 1831 * @access public
1271 1832 * @return array
1272 1833 */
1273 1834 public function maximum_reception_rooms_meta_query( ) {
1835 + $request_department = $this->get_requested_department();
1836 +
1274 1837
1275 1838 $meta_query = array();
1276 1839
1277 1840 if (
1278 1841 (
1279 - (isset( $_REQUEST['department'] ) && $_REQUEST['department'] == 'residential-sales') ||
1280 - (isset( $_REQUEST['department'] ) && $_REQUEST['department'] == 'residential-lettings')
1842 + (isset( $request_department ) && ( $request_department == 'residential-sales' || ph_get_custom_department_based_on($request_department) == 'residential-sales' )) ||
1843 + (isset( $request_department ) && ( $request_department == 'residential-lettings' || ph_get_custom_department_based_on($request_department) == 'residential-lettings' ))
1281 1844 ) &&
1845 + // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- The public property search reads a filter value and contributes query/meta/taxonomy arguments for the current request only; no persistent state-changing operation is reached. This exact annotation covers only NonceVerification.Recommended; retain all sanitizer and SQL-safety checks.
1282 1846 isset( $_REQUEST['maximum_reception_rooms'] ) && $_REQUEST['maximum_reception_rooms'] != ''
1283 1847 )
1284 1848 {
1285 1849 $meta_query = array(
1286 1850 'key' => '_reception_rooms',
1287 - 'value' => ph_clean( $_REQUEST['maximum_reception_rooms'] ),
1851 + // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- The public property search reads a filter value and contributes query/meta/taxonomy arguments for the current request only; no persistent state-changing operation is reached. This exact annotation covers only NonceVerification.Recommended; retain all sanitizer and SQL-safety checks.
1852 + 'value' => ph_clean( wp_unslash( $_REQUEST['maximum_reception_rooms'] ) ),
1288 1853 'compare' => '<=',
1289 1854 'type' => 'NUMERIC'
1290 1855 );
1291 1856 }
@@ -1299,17 +1864,21 @@
1299 1864 * @access public
1300 1865 * @return array
1301 1866 */
1302 1867 public function available_date_from_meta_query( ) {
1868 + $request_department = $this->get_requested_department();
1869 +
1303 1870
1304 1871 $meta_query = array();
1305 1872
1306 1873 if (
1307 - isset( $_REQUEST['department'] ) && $_REQUEST['department'] == 'residential-lettings' &&
1308 - isset( $_REQUEST['available_date_from'] ) && $_REQUEST['available_date_from'] != ''
1874 + isset( $request_department ) && ( $request_department == 'residential-lettings' || ph_get_custom_department_based_on($request_department) == 'residential-lettings' ) &&
1875 + // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- The public property search reads a filter value and contributes query/meta/taxonomy arguments for the current request only; no persistent state-changing operation is reached. This exact annotation covers only NonceVerification.Recommended; retain all sanitizer and SQL-safety checks.
1876 + isset( $_REQUEST['available_date_from'] ) && is_string( $_REQUEST['available_date_from'] ) && $_REQUEST['available_date_from'] != ''
1309 1877 )
1310 1878 {
1311 - $available_date = ph_clean($_REQUEST['available_date_from']);
1879 + // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- The public property search reads a filter value and contributes query/meta/taxonomy arguments for the current request only; no persistent state-changing operation is reached. This exact annotation covers only NonceVerification.Recommended; retain all sanitizer and SQL-safety checks.
1880 + $available_date = ph_clean( wp_unslash( $_REQUEST['available_date_from'] ) );
1312 1881 if ( strpos($available_date, '/') !== FALSE )
1313 1882 {
1314 1883 // it's been provided in the format dd/mm/yyyy
1315 1884 $explode_available_date = explode("/", $available_date);
@@ -1334,27 +1903,40 @@
1334 1903 * @access public
1335 1904 * @return array
1336 1905 */
1337 1906 public function minimum_floor_area_meta_query( ) {
1907 + $request_department = $this->get_requested_department();
1908 +
1338 1909
1339 1910 $meta_query = array();
1340 1911
1341 1912 if (
1342 - isset( $_REQUEST['department'] ) && $_REQUEST['department'] == 'commercial' &&
1913 + isset( $request_department ) && ( $request_department == 'commercial' || ph_get_custom_department_based_on($request_department) == 'commercial' ) &&
1914 + // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- The public property search reads a filter value and contributes query/meta/taxonomy arguments for the current request only; no persistent state-changing operation is reached. This exact annotation covers only NonceVerification.Recommended; retain all sanitizer and SQL-safety checks.
1343 1915 isset( $_REQUEST['minimum_floor_area'] ) && $_REQUEST['minimum_floor_area'] != '' &&
1344 1916 (
1917 + // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- The public property search reads a filter value and contributes query/meta/taxonomy arguments for the current request only; no persistent state-changing operation is reached. This exact annotation covers only NonceVerification.Recommended; retain all sanitizer and SQL-safety checks.
1345 1918 !isset( $_REQUEST['maximum_floor_area'] ) ||
1919 + // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- The public property search reads a filter value and contributes query/meta/taxonomy arguments for the current request only; no persistent state-changing operation is reached. This exact annotation covers only NonceVerification.Recommended; retain all sanitizer and SQL-safety checks.
1346 1920 ( isset( $_REQUEST['maximum_floor_area'] ) && $_REQUEST['maximum_floor_area'] == '' )
1347 1921 )
1348 1922 )
1349 1923 {
1924 + // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- The public property search reads a filter value and contributes query/meta/taxonomy arguments for the current request only; no persistent state-changing operation is reached. This exact annotation covers only NonceVerification.Recommended; retain all sanitizer and SQL-safety checks.
1925 + $value = ph_clean( wp_unslash( $_REQUEST['minimum_floor_area'] ) );
1926 + if ( apply_filters('propertyhive_default_commercial_search_floor_area_unit', 'sqft') != 'sqft' )
1927 + {
1928 + // Convert value from square metres to square feet
1929 + $value = $value * 10.76391041671;
1930 + }
1931 +
1350 1932 $meta_query = array(
1351 - 'key' => '_floor_area_from_sqft',
1352 - 'value' => ph_clean( $_REQUEST['minimum_floor_area'] ),
1933 + 'key' => '_floor_area_to_sqft',
1934 + 'value' => $value,
1353 1935 'compare' => '>=',
1354 1936 'type' => 'NUMERIC'
1355 - );
1356 - }
1937 + );
1938 + }
1357 1939
1358 1940 return $meta_query;
1359 1941 }
1360 1942
@@ -1364,26 +1946,39 @@
1364 1946 * @access public
1365 1947 * @return array
1366 1948 */
1367 1949 public function maximum_floor_area_meta_query( ) {
1950 + $request_department = $this->get_requested_department();
1951 +
1368 1952
1369 1953 $meta_query = array();
1370 1954
1371 1955 if (
1372 - isset( $_REQUEST['department'] ) && $_REQUEST['department'] == 'commercial' &&
1956 + isset( $request_department ) && ( $request_department == 'commercial' || ph_get_custom_department_based_on($request_department) == 'commercial' ) &&
1957 + // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- The public property search reads a filter value and contributes query/meta/taxonomy arguments for the current request only; no persistent state-changing operation is reached. This exact annotation covers only NonceVerification.Recommended; retain all sanitizer and SQL-safety checks.
1373 1958 isset( $_REQUEST['maximum_floor_area'] ) && $_REQUEST['maximum_floor_area'] != '' &&
1374 1959 (
1960 + // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- The public property search reads a filter value and contributes query/meta/taxonomy arguments for the current request only; no persistent state-changing operation is reached. This exact annotation covers only NonceVerification.Recommended; retain all sanitizer and SQL-safety checks.
1375 1961 !isset( $_REQUEST['minimum_floor_area'] ) ||
1962 + // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- The public property search reads a filter value and contributes query/meta/taxonomy arguments for the current request only; no persistent state-changing operation is reached. This exact annotation covers only NonceVerification.Recommended; retain all sanitizer and SQL-safety checks.
1376 1963 ( isset( $_REQUEST['minimum_floor_area'] ) && $_REQUEST['minimum_floor_area'] == '' )
1377 1964 )
1378 1965 )
1379 1966 {
1967 + // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- The public property search reads a filter value and contributes query/meta/taxonomy arguments for the current request only; no persistent state-changing operation is reached. This exact annotation covers only NonceVerification.Recommended; retain all sanitizer and SQL-safety checks.
1968 + $value = ph_clean( wp_unslash( $_REQUEST['maximum_floor_area'] ) );
1969 + if ( apply_filters('propertyhive_default_commercial_search_floor_area_unit', 'sqft') != 'sqft' )
1970 + {
1971 + // Convert value from square metres to square feet
1972 + $value = $value * 10.76391041671;
1973 + }
1974 +
1380 1975 $meta_query = array(
1381 - 'key' => '_floor_area_to_sqft',
1382 - 'value' => ph_clean( $_REQUEST['maximum_floor_area'] ),
1976 + 'key' => '_floor_area_from_sqft',
1977 + 'value' => $value,
1383 1978 'compare' => '<=',
1384 1979 'type' => 'NUMERIC'
1385 - );
1980 + );
1386 1981 }
1387 1982
1388 1983 return $meta_query;
1389 1984 }
@@ -1394,26 +1989,41 @@
1394 1989 * @access public
1395 1990 * @return array
1396 1991 */
1397 1992 public function minimum_maximum_floor_area_meta_query( ) {
1993 + $request_department = $this->get_requested_department();
1994 +
1398 1995
1399 1996 $meta_query = array();
1400 1997
1401 1998 if (
1402 - isset( $_REQUEST['department'] ) && $_REQUEST['department'] == 'commercial' &&
1999 + isset( $request_department ) && ( $request_department == 'commercial' || ph_get_custom_department_based_on($request_department) == 'commercial' ) &&
2000 + // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- The public property search reads a filter value and contributes query/meta/taxonomy arguments for the current request only; no persistent state-changing operation is reached. This exact annotation covers only NonceVerification.Recommended; retain all sanitizer and SQL-safety checks.
1403 2001 isset( $_REQUEST['minimum_floor_area'] ) && $_REQUEST['minimum_floor_area'] != '' &&
2002 + // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- The public property search reads a filter value and contributes query/meta/taxonomy arguments for the current request only; no persistent state-changing operation is reached. This exact annotation covers only NonceVerification.Recommended; retain all sanitizer and SQL-safety checks.
1404 2003 isset( $_REQUEST['maximum_floor_area'] ) && $_REQUEST['maximum_floor_area'] != ''
1405 2004 )
1406 2005 {
2006 + // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- The public property search reads a filter value and contributes query/meta/taxonomy arguments for the current request only; no persistent state-changing operation is reached. This exact annotation covers only NonceVerification.Recommended; retain all sanitizer and SQL-safety checks.
2007 + $maximum_floor_area = ph_clean( wp_unslash( $_REQUEST['maximum_floor_area'] ) );
2008 + // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- The public property search reads a filter value and contributes query/meta/taxonomy arguments for the current request only; no persistent state-changing operation is reached. This exact annotation covers only NonceVerification.Recommended; retain all sanitizer and SQL-safety checks.
2009 + $minimum_floor_area = ph_clean( wp_unslash( $_REQUEST['minimum_floor_area'] ) );
2010 + if ( apply_filters('propertyhive_default_commercial_search_floor_area_unit', 'sqft') != 'sqft' )
2011 + {
2012 + // Convert value from square metres to square feet
2013 + $maximum_floor_area = $maximum_floor_area * 10.76391041671;
2014 + $minimum_floor_area = $minimum_floor_area * 10.76391041671;
2015 + }
2016 +
1407 2017 $meta_query[] = array(
1408 2018 'key' => '_floor_area_from_sqft',
1409 - 'value' => ph_clean( $_REQUEST['maximum_floor_area'] ),
2019 + 'value' => $maximum_floor_area,
1410 2020 'compare' => '<=',
1411 2021 'type' => 'NUMERIC'
1412 2022 );
1413 2023 $meta_query[] = array(
1414 2024 'key' => '_floor_area_to_sqft',
1415 - 'value' => ph_clean( $_REQUEST['minimum_floor_area'] ),
2025 + 'value' => $minimum_floor_area,
1416 2026 'compare' => '>=',
1417 2027 'type' => 'NUMERIC'
1418 2028 );
1419 2029 }
@@ -1428,17 +2038,21 @@
1428 2038 * @access public
1429 2039 * @return array
1430 2040 */
1431 2041 public function floor_area_range_meta_query( ) {
2042 + $request_department = $this->get_requested_department();
2043 +
1432 2044
1433 2045 $meta_query = array();
1434 2046
1435 2047 if (
1436 - isset( $_REQUEST['department'] ) && $_REQUEST['department'] == 'commercial' &&
1437 - isset( $_REQUEST['floor_area_range'] ) && $_REQUEST['floor_area_range'] != ''
2048 + isset( $request_department ) && ( $request_department == 'commercial' || ph_get_custom_department_based_on($request_department) == 'commercial' ) &&
2049 + // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- The public property search reads a filter value and contributes query/meta/taxonomy arguments for the current request only; no persistent state-changing operation is reached. This exact annotation covers only NonceVerification.Recommended; retain all sanitizer and SQL-safety checks.
2050 + isset( $_REQUEST['floor_area_range'] ) && is_string( $_REQUEST['floor_area_range'] ) && $_REQUEST['floor_area_range'] != ''
1438 2051 )
1439 2052 {
1440 - $explode_floor_area_range = explode("-", ph_clean($_REQUEST['floor_area_range']));
2053 + // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- The public property search reads a filter value and contributes query/meta/taxonomy arguments for the current request only; no persistent state-changing operation is reached. This exact annotation covers only NonceVerification.Recommended; retain all sanitizer and SQL-safety checks.
2054 + $explode_floor_area_range = explode("-", ph_clean( wp_unslash( $_REQUEST['floor_area_range'] ) ));
1441 2055
1442 2056 if ( isset($explode_floor_area_range[0]) && $explode_floor_area_range[0] != '' )
1443 2057 {
1444 2058 $meta_query = array(
@@ -1468,13 +2082,16 @@
1468 2082 * @access public
1469 2083 * @return array
1470 2084 */
1471 2085 public function commercial_for_sale_to_rent_meta_query( ) {
2086 + $request_department = $this->get_requested_department();
2087 +
1472 2088
1473 2089 $meta_query = array();
1474 2090
1475 2091 if (
1476 - isset( $_REQUEST['department'] ) && $_REQUEST['department'] == 'commercial' &&
2092 + isset( $request_department ) && ( $request_department == 'commercial' || ph_get_custom_department_based_on($request_department) == 'commercial' ) &&
2093 + // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- The public property search reads a filter value and contributes query/meta/taxonomy arguments for the current request only; no persistent state-changing operation is reached. This exact annotation covers only NonceVerification.Recommended; retain all sanitizer and SQL-safety checks.
1477 2094 isset( $_REQUEST['commercial_for_sale_to_rent'] ) && $_REQUEST['commercial_for_sale_to_rent'] == 'for_sale'
1478 2095 )
1479 2096 {
1480 2097 $meta_query = array(
@@ -1484,9 +2101,10 @@
1484 2101 );
1485 2102 }
1486 2103
1487 2104 if (
1488 - isset( $_REQUEST['department'] ) && $_REQUEST['department'] == 'commercial' &&
2105 + isset( $request_department ) && ( $request_department == 'commercial' || ph_get_custom_department_based_on($request_department) == 'commercial' ) &&
2106 + // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- The public property search reads a filter value and contributes query/meta/taxonomy arguments for the current request only; no persistent state-changing operation is reached. This exact annotation covers only NonceVerification.Recommended; retain all sanitizer and SQL-safety checks.
1489 2107 isset( $_REQUEST['commercial_for_sale_to_rent'] ) && $_REQUEST['commercial_for_sale_to_rent'] == 'to_rent'
1490 2108 )
1491 2109 {
1492 2110 $meta_query = array(
@@ -1505,13 +2123,16 @@
1505 2123 * @access public
1506 2124 * @return array
1507 2125 */
1508 2126 public function commercial_for_sale_meta_query( ) {
2127 + $request_department = $this->get_requested_department();
2128 +
1509 2129
1510 2130 $meta_query = array();
1511 2131
1512 2132 if (
1513 - isset( $_REQUEST['department'] ) && $_REQUEST['department'] == 'commercial' &&
2133 + isset( $request_department ) && ( $request_department == 'commercial' || ph_get_custom_department_based_on($request_department) == 'commercial' ) &&
2134 + // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- The public property search reads a filter value and contributes query/meta/taxonomy arguments for the current request only; no persistent state-changing operation is reached. This exact annotation covers only NonceVerification.Recommended; retain all sanitizer and SQL-safety checks.
1514 2135 isset( $_REQUEST['commercial_for_sale'] ) && $_REQUEST['commercial_for_sale'] == '1'
1515 2136 )
1516 2137 {
1517 2138 $meta_query = array(
@@ -1530,13 +2151,16 @@
1530 2151 * @access public
1531 2152 * @return array
1532 2153 */
1533 2154 public function commercial_to_rent_meta_query( ) {
2155 + $request_department = $this->get_requested_department();
2156 +
1534 2157
1535 2158 $meta_query = array();
1536 2159
1537 2160 if (
1538 - isset( $_REQUEST['department'] ) && $_REQUEST['department'] == 'commercial' &&
2161 + isset( $request_department ) && ( $request_department == 'commercial' || ph_get_custom_department_based_on($request_department) == 'commercial' ) &&
2162 + // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- The public property search reads a filter value and contributes query/meta/taxonomy arguments for the current request only; no persistent state-changing operation is reached. This exact annotation covers only NonceVerification.Recommended; retain all sanitizer and SQL-safety checks.
1539 2163 isset( $_REQUEST['commercial_to_rent'] ) && $_REQUEST['commercial_to_rent'] == '1'
1540 2164 )
1541 2165 {
1542 2166 $meta_query = array(
@@ -1549,8 +2173,228 @@
1549 2173 return $meta_query;
1550 2174 }
1551 2175
1552 2176 /**
2177 + * Returns a meta query to handle commercial minimum price
2178 + *
2179 + * @access public
2180 + * @return array
2181 + */
2182 + public function commercial_minimum_price_meta_query( ) {
2183 + $request_department = $this->get_requested_department();
2184 +
2185 +
2186 + $meta_query = array();
2187 +
2188 + if (
2189 + isset( $request_department ) && ( $request_department == 'commercial' || ph_get_custom_department_based_on($request_department) == 'commercial' ) &&
2190 + (
2191 + // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- The public property search reads a filter value and contributes query/meta/taxonomy arguments for the current request only; no persistent state-changing operation is reached. This exact annotation covers only NonceVerification.Recommended; retain all sanitizer and SQL-safety checks.
2192 + ( isset( $_REQUEST['commercial_for_sale_to_rent'] ) && $_REQUEST['commercial_for_sale_to_rent'] == 'for_sale' )
2193 + ||
2194 + // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- The public property search reads a filter value and contributes query/meta/taxonomy arguments for the current request only; no persistent state-changing operation is reached. This exact annotation covers only NonceVerification.Recommended; retain all sanitizer and SQL-safety checks.
2195 + ( isset( $_REQUEST['commercial_for_sale'] ) && $_REQUEST['commercial_for_sale'] == '1' )
2196 + ) &&
2197 + // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- The public property search reads a filter value and contributes query/meta/taxonomy arguments for the current request only; no persistent state-changing operation is reached. This exact annotation covers only NonceVerification.Recommended; retain all sanitizer and SQL-safety checks.
2198 + isset( $_REQUEST['commercial_minimum_price'] ) && $_REQUEST['commercial_minimum_price'] != ''
2199 + )
2200 + {
2201 + // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- The public property search reads a filter value and contributes query/meta/taxonomy arguments for the current request only; no persistent state-changing operation is reached. This exact annotation covers only NonceVerification.Recommended; retain all sanitizer and SQL-safety checks.
2202 + $minimum_price = is_string( $_REQUEST['commercial_minimum_price'] ) ? sanitize_text_field( wp_unslash( $_REQUEST['commercial_minimum_price'] ) ) : '';
2203 +
2204 + if ( !is_numeric($minimum_price) )
2205 + {
2206 + return $meta_query;
2207 + }
2208 +
2209 + $search_form_currency = get_option( 'propertyhive_search_form_currency', 'GBP' );
2210 + $search_form_currency = apply_filters( 'propertyhive_query_search_form_currency', $search_form_currency );
2211 +
2212 + if ( $search_form_currency != 'GBP' )
2213 + {
2214 + // Convert $_REQUEST['minimum_price'] to GBP
2215 + $ph_countries = new PH_Countries();
2216 +
2217 + $minimum_price = $ph_countries->convert_price_to_gbp( $minimum_price, $search_form_currency );
2218 + }
2219 +
2220 + $meta_query = array(
2221 + 'key' => '_price_to_actual',
2222 + 'value' => ph_clean( floor( $minimum_price ) ),
2223 + 'compare' => '>=',
2224 + 'type' => 'NUMERIC'
2225 + );
2226 + }
2227 +
2228 + return $meta_query;
2229 + }
2230 +
2231 + /**
2232 + * Returns a meta query to handle commercial maximum price
2233 + *
2234 + * @access public
2235 + * @return array
2236 + */
2237 + public function commercial_maximum_price_meta_query( ) {
2238 + $request_department = $this->get_requested_department();
2239 +
2240 +
2241 + $meta_query = array();
2242 +
2243 + if (
2244 + isset( $request_department ) && ( $request_department == 'commercial' || ph_get_custom_department_based_on($request_department) == 'commercial' ) &&
2245 + (
2246 + // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- The public property search reads a filter value and contributes query/meta/taxonomy arguments for the current request only; no persistent state-changing operation is reached. This exact annotation covers only NonceVerification.Recommended; retain all sanitizer and SQL-safety checks.
2247 + ( isset( $_REQUEST['commercial_for_sale_to_rent'] ) && $_REQUEST['commercial_for_sale_to_rent'] == 'for_sale' )
2248 + ||
2249 + // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- The public property search reads a filter value and contributes query/meta/taxonomy arguments for the current request only; no persistent state-changing operation is reached. This exact annotation covers only NonceVerification.Recommended; retain all sanitizer and SQL-safety checks.
2250 + ( isset( $_REQUEST['commercial_for_sale'] ) && $_REQUEST['commercial_for_sale'] == '1' )
2251 + ) &&
2252 + // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- The public property search reads a filter value and contributes query/meta/taxonomy arguments for the current request only; no persistent state-changing operation is reached. This exact annotation covers only NonceVerification.Recommended; retain all sanitizer and SQL-safety checks.
2253 + isset( $_REQUEST['commercial_maximum_price'] ) && $_REQUEST['commercial_maximum_price'] != ''
2254 + )
2255 + {
2256 + // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- The public property search reads a filter value and contributes query/meta/taxonomy arguments for the current request only; no persistent state-changing operation is reached. This exact annotation covers only NonceVerification.Recommended; retain all sanitizer and SQL-safety checks.
2257 + $maximum_price = is_string( $_REQUEST['commercial_maximum_price'] ) ? sanitize_text_field( wp_unslash( $_REQUEST['commercial_maximum_price'] ) ) : '';
2258 +
2259 + if ( !is_numeric($maximum_price) )
2260 + {
2261 + return $meta_query;
2262 + }
2263 +
2264 + $search_form_currency = get_option( 'propertyhive_search_form_currency', 'GBP' );
2265 + $search_form_currency = apply_filters( 'propertyhive_query_search_form_currency', $search_form_currency );
2266 +
2267 + if ( $search_form_currency != 'GBP' )
2268 + {
2269 + // Convert $_REQUEST['maximum_price'] to GBP
2270 + $ph_countries = new PH_Countries();
2271 +
2272 + $maximum_price = $ph_countries->convert_price_to_gbp( $maximum_price, $search_form_currency );
2273 + }
2274 +
2275 + $meta_query = array(
2276 + 'key' => '_price_from_actual',
2277 + 'value' => ph_clean( ceil( $maximum_price ) ),
2278 + 'compare' => '<=',
2279 + 'type' => 'NUMERIC'
2280 + );
2281 + }
2282 +
2283 + return $meta_query;
2284 + }
2285 +
2286 + /**
2287 + * Returns a meta query to handle commercial minimum rent
2288 + *
2289 + * @access public
2290 + * @return array
2291 + */
2292 + public function commercial_minimum_rent_meta_query( ) {
2293 + $request_department = $this->get_requested_department();
2294 +
2295 +
2296 + $meta_query = array();
2297 +
2298 + if (
2299 + isset( $request_department ) && ( $request_department == 'commercial' || ph_get_custom_department_based_on($request_department) == 'commercial' ) &&
2300 + (
2301 + // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- The public property search reads a filter value and contributes query/meta/taxonomy arguments for the current request only; no persistent state-changing operation is reached. This exact annotation covers only NonceVerification.Recommended; retain all sanitizer and SQL-safety checks.
2302 + ( isset( $_REQUEST['commercial_for_sale_to_rent'] ) && $_REQUEST['commercial_for_sale_to_rent'] == 'to_rent' )
2303 + ||
2304 + // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- The public property search reads a filter value and contributes query/meta/taxonomy arguments for the current request only; no persistent state-changing operation is reached. This exact annotation covers only NonceVerification.Recommended; retain all sanitizer and SQL-safety checks.
2305 + ( isset( $_REQUEST['commercial_to_rent'] ) && $_REQUEST['commercial_to_rent'] == '1' )
2306 + ) &&
2307 + // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- The public property search reads a filter value and contributes query/meta/taxonomy arguments for the current request only; no persistent state-changing operation is reached. This exact annotation covers only NonceVerification.Recommended; retain all sanitizer and SQL-safety checks.
2308 + isset( $_REQUEST['commercial_minimum_rent'] ) && $_REQUEST['commercial_minimum_rent'] != ''
2309 + )
2310 + {
2311 + // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- The public property search reads a filter value and contributes query/meta/taxonomy arguments for the current request only; no persistent state-changing operation is reached. This exact annotation covers only NonceVerification.Recommended; retain all sanitizer and SQL-safety checks.
2312 + $minimum_rent = is_string( $_REQUEST['commercial_minimum_rent'] ) ? sanitize_text_field( wp_unslash( $_REQUEST['commercial_minimum_rent'] ) ) : '';
2313 +
2314 + if ( !is_numeric($minimum_rent) )
2315 + {
2316 + return $meta_query;
2317 + }
2318 +
2319 + $search_form_currency = get_option( 'propertyhive_search_form_currency', 'GBP' );
2320 + $search_form_currency = apply_filters( 'propertyhive_query_search_form_currency', $search_form_currency );
2321 +
2322 + if ( $search_form_currency != 'GBP' )
2323 + {
2324 + // Convert $_REQUEST['minimum_rent'] to GBP
2325 + $ph_countries = new PH_Countries();
2326 +
2327 + $minimum_rent = $ph_countries->convert_price_to_gbp( $minimum_rent, $search_form_currency );
2328 + }
2329 +
2330 + $meta_query = array(
2331 + 'key' => '_rent_to_actual',
2332 + 'value' => ph_clean( floor( $minimum_rent ) ),
2333 + 'compare' => '>=',
2334 + 'type' => 'NUMERIC'
2335 + );
2336 + }
2337 +
2338 + return $meta_query;
2339 + }
2340 +
2341 + /**
2342 + * Returns a meta query to handle commercial maximum rent
2343 + *
2344 + * @access public
2345 + * @return array
2346 + */
2347 + public function commercial_maximum_rent_meta_query( ) {
2348 + $request_department = $this->get_requested_department();
2349 +
2350 +
2351 + $meta_query = array();
2352 +
2353 + if (
2354 + isset( $request_department ) && ( $request_department == 'commercial' || ph_get_custom_department_based_on($request_department) == 'commercial' ) &&
2355 + (
2356 + // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- The public property search reads a filter value and contributes query/meta/taxonomy arguments for the current request only; no persistent state-changing operation is reached. This exact annotation covers only NonceVerification.Recommended; retain all sanitizer and SQL-safety checks.
2357 + ( isset( $_REQUEST['commercial_for_sale_to_rent'] ) && $_REQUEST['commercial_for_sale_to_rent'] == 'to_rent' )
2358 + ||
2359 + // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- The public property search reads a filter value and contributes query/meta/taxonomy arguments for the current request only; no persistent state-changing operation is reached. This exact annotation covers only NonceVerification.Recommended; retain all sanitizer and SQL-safety checks.
2360 + ( isset( $_REQUEST['commercial_to_rent'] ) && $_REQUEST['commercial_to_rent'] == '1' )
2361 + ) &&
2362 + // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- The public property search reads a filter value and contributes query/meta/taxonomy arguments for the current request only; no persistent state-changing operation is reached. This exact annotation covers only NonceVerification.Recommended; retain all sanitizer and SQL-safety checks.
2363 + isset( $_REQUEST['commercial_maximum_rent'] ) && $_REQUEST['commercial_maximum_rent'] != ''
2364 + )
2365 + {
2366 + // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- The public property search reads a filter value and contributes query/meta/taxonomy arguments for the current request only; no persistent state-changing operation is reached. This exact annotation covers only NonceVerification.Recommended; retain all sanitizer and SQL-safety checks.
2367 + $maximum_rent = is_string( $_REQUEST['commercial_maximum_rent'] ) ? sanitize_text_field( wp_unslash( $_REQUEST['commercial_maximum_rent'] ) ) : '';
2368 +
2369 + if ( !is_numeric($maximum_rent) )
2370 + {
2371 + return $meta_query;
2372 + }
2373 +
2374 + $search_form_currency = get_option( 'propertyhive_search_form_currency', 'GBP' );
2375 + $search_form_currency = apply_filters( 'propertyhive_query_search_form_currency', $search_form_currency );
2376 +
2377 + if ( $search_form_currency != 'GBP' )
2378 + {
2379 + // Convert $_REQUEST['maximum_rent'] to GBP
2380 + $ph_countries = new PH_Countries();
2381 +
2382 + $maximum_rent = $ph_countries->convert_price_to_gbp( $maximum_rent, $search_form_currency );
2383 + }
2384 +
2385 + $meta_query = array(
2386 + 'key' => '_rent_from_actual',
2387 + 'value' => ph_clean( ceil( $maximum_rent ) ),
2388 + 'compare' => '<=',
2389 + 'type' => 'NUMERIC'
2390 + );
2391 + }
2392 +
2393 + return $meta_query;
2394 + }
2395 +
2396 + /**
1553 2397 * Returns a meta query to handle property negotiator
1554 2398 *
1555 2399 * @access public
1556 2400 * @return array
@@ -1558,12 +2402,14 @@
1558 2402 public function negotiator_meta_query( ) {
1559 2403
1560 2404 $meta_query = array();
1561 2405
2406 + // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- The public property search reads a filter value and contributes query/meta/taxonomy arguments for the current request only; no persistent state-changing operation is reached. This exact annotation covers only NonceVerification.Recommended; retain all sanitizer and SQL-safety checks.
1562 2407 if ( isset( $_REQUEST['negotiator_id'] ) && $_REQUEST['negotiator_id'] != '' )
1563 2408 {
1564 2409 $meta_query = array(
1565 2410 'key' => '_negotiator_id',
2411 + // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- The public property search reads a filter value and contributes query/meta/taxonomy arguments for the current request only; no persistent state-changing operation is reached. This exact annotation covers only NonceVerification.Recommended; retain all sanitizer and SQL-safety checks.
1566 2412 'value' => (int)$_REQUEST['negotiator_id'],
1567 2413 'compare' => '='
1568 2414 );
1569 2415 }
@@ -1581,14 +2427,16 @@
1581 2427 public function office_meta_query( ) {
1582 2428
1583 2429 $meta_query = array();
1584 2430
2431 + // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- The public property search reads a filter value and contributes query/meta/taxonomy arguments for the current request only; no persistent state-changing operation is reached. This exact annotation covers only NonceVerification.Recommended; retain all sanitizer and SQL-safety checks.
1585 2432 if ( isset( $_REQUEST['officeID'] ) && $_REQUEST['officeID'] != '' )
1586 2433 {
1587 2434 $meta_query = array(
1588 2435 'key' => '_office_id',
1589 - 'value' => (int)$_REQUEST['officeID'],
1590 - 'compare' => '='
2436 + // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- The public property search reads a filter value and contributes query/meta/taxonomy arguments for the current request only; no persistent state-changing operation is reached. This exact annotation covers only NonceVerification.Recommended; retain all sanitizer and SQL-safety checks.
2437 + 'value' => ph_clean( wp_unslash( (array) $_REQUEST['officeID'] ) ),
2438 + 'compare' => 'IN'
1591 2439 );
1592 2440 }
1593 2441
1594 2442 return $meta_query;
@@ -1593,8 +2441,168 @@
1593 2441
1594 2442 return $meta_query;
1595 2443 }
1596 2444
2445 + /**
2446 + * Returns a meta query to handle searching for a keyword in the features and descriptions
2447 + *
2448 + * @access public
2449 + * @return array
2450 + */
2451 + public function keyword_meta_query( ) {
2452 +
2453 + $meta_query = array();
2454 +
2455 + // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Public read-only search input; query construction does not change persistent state.
2456 + if ( isset( $_REQUEST['keyword'] ) && is_string( $_REQUEST['keyword'] ) && $_REQUEST['keyword'] != '' )
2457 + {
2458 +
2459 + // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Public read-only search input is type checked above.
2460 + $request_keyword = sanitize_text_field( wp_unslash( $_REQUEST['keyword'] ) );
2461 +
2462 + // Remove country code from end (i.e. ', UK')
2463 +
2464 + $request_keyword = preg_replace('/\,\s?[A-Z][A-Z]$/', '', $request_keyword);
2465 +
2466 + // Extract postcode and use that if exists
2467 + $postcode_pattern = '/\b([A-Z]{1,2}[0-9][0-9A-Z]? ?[0-9]?[A-Z]{0,2})\b/i';
2468 +
2469 + if ( preg_match($postcode_pattern, $request_keyword, $matches) )
2470 + {
2471 + $request_keyword = $matches[1];
2472 + }
2473 +
2474 + $request_keyword = trim($request_keyword);
2475 +
2476 + // Keep the normalized request value available to the existing excerpt query and extension filters.
2477 + $_REQUEST['keyword'] = $request_keyword;
2478 + self::$normalized_keyword = $request_keyword;
2479 +
2480 + $keywords = array( $request_keyword );
2481 +
2482 + if ( strpos( $request_keyword, ' ' ) !== FALSE )
2483 + {
2484 +
2485 + $keywords[] = str_replace(" ", "-", ph_clean($request_keyword));
2486 + }
2487 +
2488 + if ( strpos( $request_keyword, '-' ) !== FALSE )
2489 + {
2490 +
2491 + $keywords[] = str_replace("-", " ", ph_clean($request_keyword));
2492 + }
2493 +
2494 + if ( strpos( $request_keyword, '.' ) !== FALSE )
2495 + {
2496 +
2497 + $keywords[] = str_replace(".", "", ph_clean($request_keyword));
2498 + }
2499 +
2500 + if ( stripos( $request_keyword, 'st ' ) !== FALSE )
2501 + {
2502 +
2503 + $keywords[] = str_ireplace("st ", "st. ", ph_clean($request_keyword));
2504 + }
2505 +
2506 + if ( strpos( $request_keyword, '\'' ) !== FALSE )
2507 + {
2508 +
2509 + $keywords[] = str_replace("'", "", ph_clean($request_keyword));
2510 + }
2511 +
2512 + $meta_query = array( 'relation' => 'OR' );
2513 +
2514 + $fields_to_query = array(
2515 + '_features_concatenated',
2516 + '_descriptions_concatenated',
2517 + '_reference_number',
2518 + '_address_street',
2519 + '_address_two',
2520 + '_address_three',
2521 + '_address_four',
2522 + '_address_postcode',
2523 + );
2524 +
2525 + $fields_to_query = apply_filters( 'propertyhive_keyword_fields_to_query', $fields_to_query );
2526 +
2527 + foreach ( $keywords as $keyword )
2528 + {
2529 + foreach ( $fields_to_query as $field )
2530 + {
2531 + if ( $field == '_address_postcode' ) { continue; } // ignore postcode as that is handled differently afterwards
2532 +
2533 + $meta_query[] = array(
2534 + 'key' => $field,
2535 + 'value' => $keyword,
2536 + 'compare' => 'LIKE'
2537 + );
2538 + }
2539 + }
2540 + if ( in_array('_address_postcode', $fields_to_query) )
2541 + {
2542 +
2543 + if ( strlen($request_keyword) <= 4 )
2544 + {
2545 + $meta_query[] = array(
2546 + 'key' => '_address_postcode',
2547 +
2548 + 'value' => ph_clean( $request_keyword ),
2549 + 'compare' => '='
2550 + );
2551 + // Run regex match where given keyword is at the start of the postcode ^
2552 + // followed by one or zero letters (for WC2E-style postcodes) [a-zA-Z]?
2553 + // then a single space [ ]
2554 + $meta_query[] = array(
2555 + 'key' => '_address_postcode',
2556 +
2557 + 'value' => '^' . ph_clean( $request_keyword ) . '[a-zA-Z]?[ ]',
2558 + 'compare' => 'RLIKE'
2559 + );
2560 + }
2561 + else
2562 + {
2563 +
2564 + $postcode = ph_clean( $request_keyword );
2565 +
2566 + if ( preg_match('#^(GIR ?0AA|[A-PR-UWYZ]([0-9]{1,2}|([A-HK-Y][0-9]([0-9ABEHMNPRV-Y])?)|[0-9][A-HJKPS-UW])[0-9][ABD-HJLNP-UW-Z]{2})$#i', $postcode) )
2567 + {
2568 + // UK postcode found with no space
2569 +
2570 + if ( strlen($postcode) == 5 )
2571 + {
2572 + $first_part = substr($postcode, 0, 2);
2573 + $last_part = substr($postcode, 2, 3);
2574 +
2575 + $postcode = $first_part . ' ' . $last_part;
2576 + }
2577 + elseif ( strlen($postcode) == 6 )
2578 + {
2579 + $first_part = substr($postcode, 0, 3);
2580 + $last_part = substr($postcode, 3, 3);
2581 +
2582 + $postcode = $first_part . ' ' . $last_part;
2583 + }
2584 + elseif ( strlen($postcode) == 7 )
2585 + {
2586 + $first_part = substr($postcode, 0, 4);
2587 + $last_part = substr($postcode, 4, 3);
2588 +
2589 + $postcode = $first_part . ' ' . $last_part;
2590 + }
2591 + }
2592 +
2593 + $meta_query[] = array(
2594 + 'key' => '_address_postcode',
2595 + 'value' => ph_clean( $postcode ),
2596 + 'compare' => 'LIKE'
2597 + );
2598 + }
2599 + }
2600 + }
2601 +
2602 + return $meta_query;
2603 + }
2604 +
1597 2605 /**
1598 2606 * Appends taxonomy queries to an array.
1599 2607 * @access public
1600 2608 * @param array $tax_query
@@ -1603,17 +2611,30 @@
1603 2611 public function get_tax_query( $tax_query = array() ) {
1604 2612 if ( ! is_array( $tax_query ) )
1605 2613 $tax_query = array();
1606 2614
2615 + // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Public read-only taxonomy search; this does not authorize a write.
1607 2616 if ( isset($_REQUEST) && !empty($_REQUEST) )
1608 2617 {
2618 +
2619 + // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Public read-only taxonomy search; each value is validated and sanitized below.
1609 2620 foreach ( $_REQUEST as $key => $value )
1610 2621 {
1611 - if ( taxonomy_exists($key) && isset( $_REQUEST[$key] ) && !empty($_REQUEST[$key]) && $this->taxonomy_allowed_for_department( $key ) )
2622 +
2623 + if ( taxonomy_exists($key) && !empty($value) && $this->taxonomy_allowed_for_department( $key ) )
1612 2624 {
2625 + $terms = (array) $value;
2626 + foreach ( $terms as $term ) {
2627 + if ( ! is_string( $term ) && ! is_int( $term ) ) {
2628 + continue 2;
2629 + }
2630 + }
2631 + $operator = $key == 'property_feature' ? 'AND' : 'IN';
2632 +
1613 2633 $tax_query[] = array(
1614 2634 'taxonomy' => $key,
1615 - 'terms' => ph_clean( (is_array($value)) ? $value : array( $value ) )
2635 + 'terms' => ph_clean( wp_unslash( $terms ) ),
2636 + 'operator' => $operator,
1616 2637 );
1617 2638 }
1618 2639 }
1619 2640 }
@@ -1622,15 +2643,22 @@
1622 2643 }
1623 2644
1624 2645 private function taxonomy_allowed_for_department( $taxonomy )
1625 2646 {
1626 - if ( isset( $_REQUEST['department'] ) && $_REQUEST['department'] != '' )
2647 + $request_department = $this->get_requested_department();
2648 +
2649 + if ( isset( $request_department ) && $request_department != '' )
1627 2650 {
1628 - $department = ph_clean($_REQUEST['department']);
2651 + $department = ph_clean($request_department);
1629 2652 }
1630 2653 else
1631 2654 {
1632 2655 $department = get_option( 'propertyhive_primary_department', 'residential-sales' );
2656 + }
2657 +
2658 + if ( ph_get_custom_department_based_on( $department ) !== false )
2659 + {
2660 + $department = ph_get_custom_department_based_on( $department );
1633 2661 }
1634 2662
1635 2663 switch ( $department )
1636 2664 {