PluginProbe
Property Hive / 2.3.1
Property Hive v2.3.1
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 +1179 -150 1.4.472.3.1 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,46 +665,49 @@
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 - case 'rand' :
531 - $args['orderby'] = 'rand';
532 - break;
533 - case 'date' :
534 - $args['orderby'] = 'date';
535 - $args['order'] = $order == 'ASC' ? 'ASC' : 'DESC';
536 - break;
537 694 case 'price' :
538 695 $args['orderby'] = 'meta_value_num';
539 696 $args['order'] = $order == 'ASC' ? 'ASC' : 'DESC';
540 697 if (
541 - ( isset($_REQUEST['department']) && $_REQUEST['department'] == 'commercial' ) ||
542 - ( !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' ) )
543 702 )
544 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.
545 705 $args['meta_key'] = '_price_from_actual';
546 706 }
547 707 else
548 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.
549 710 $args['meta_key'] = '_price_actual';
550 711 }
551 712 break;
552 713 case 'floor_area' :
@@ -551,10 +712,25 @@
551 712 break;
552 713 case 'floor_area' :
553 714 $args['orderby'] = 'meta_value_num';
554 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.
555 717 $args['meta_key'] = '_floor_area_from_sqft';
556 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;
725 + default :
726 + {
727 + if ( $orderby != '' )
728 + {
729 + $args['orderby'] = $orderby;
730 + $args['order'] = $order == 'ASC' ? 'ASC' : 'DESC';
731 + }
732 + }
557 733 }
558 734
559 735 return apply_filters( 'propertyhive_get_search_results_ordering_args', $args );
560 736 }
@@ -568,21 +744,25 @@
568 744 public function get_date_query() {
569 745
570 746 $date_query = array();
571 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.
572 749 if ( isset( $_REQUEST['added_from'] ) && $_REQUEST['added_from'] != '' )
573 750 {
574 751 $date_query = array(
575 752 'column' => 'post_date_gmt',
576 - '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'] ) )
577 755 );
578 756 }
579 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.
580 759 if ( isset( $_REQUEST['added_from_hours'] ) && $_REQUEST['added_from_hours'] != '' )
581 760 {
582 761 $date_query = array(
583 762 'column' => 'post_date_gmt',
584 - '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'
585 765 );
586 766 }
587 767
588 768 return array_filter( $date_query );
@@ -606,8 +786,10 @@
606 786 $meta_query = array();
607 787
608 788 $meta_query[] = $this->on_market_meta_query();
609 789 $meta_query[] = $this->department_meta_query($q);
790 + $meta_query[] = $this->featured_meta_query();
791 + $meta_query[] = $this->date_added_meta_query();
610 792 $meta_query[] = $this->address_keyword_meta_query();
611 793 $meta_query[] = $this->country_meta_query();
612 794 $meta_query[] = $this->minimum_price_meta_query();
613 795 $meta_query[] = $this->maximum_price_meta_query();
@@ -629,11 +811,16 @@
629 811 $meta_query[] = $this->floor_area_range_meta_query();
630 812 $meta_query[] = $this->commercial_for_sale_to_rent_meta_query();
631 813 $meta_query[] = $this->commercial_for_sale_meta_query();
632 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();
633 819 $meta_query[] = $this->negotiator_meta_query();
634 820 $meta_query[] = $this->office_meta_query();
635 -
821 + $meta_query[] = $this->keyword_meta_query();
822 +
636 823 return array_filter( apply_filters( 'propertyhive_property_query_meta_query', $meta_query, $this ) );
637 824 }
638 825
639 826 /**
@@ -665,16 +852,18 @@
665 852 * @access public
666 853 * @return array
667 854 */
668 855 public function department_meta_query( $q ) {
856 + $request_department = $this->get_requested_department();
857 +
669 858
670 859 $meta_query = array();
671 860
672 - if ( isset( $_REQUEST['department'] ) && $_REQUEST['department'] != '' )
861 + if ( isset( $request_department ) && $request_department != '' )
673 862 {
674 863 $meta_query = array(
675 864 'key' => '_department',
676 - 'value' => sanitize_text_field( $_REQUEST['department'] ),
865 + 'value' => sanitize_text_field( $request_department ),
677 866 'compare' => '='
678 867 );
679 868 }
680 869 else
@@ -715,91 +904,314 @@
715 904 return $meta_query;
716 905 }
717 906
718 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 + /**
719 956 * Returns a meta query to handle searching for a keyword in the address
720 957 *
721 958 * @access public
722 - * @param string $compare (default: 'IN')
723 959 * @return array
724 960 */
725 961 public function address_keyword_meta_query( ) {
726 962
727 963 $meta_query = array();
728 -
729 - 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']) )
730 967 {
731 - $_REQUEST['address_keyword'] = ph_clean( wp_unslash( $_REQUEST['address_keyword'] ) );
732 968
733 - // Remove country code from end (i.e. ', UK')
734 - $_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;
735 982
736 - $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();
737 987
738 - if ( strpos( $_REQUEST['address_keyword'], ' ' ) !== FALSE )
739 - {
740 - $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 + }
741 996 }
742 - if ( strpos( $_REQUEST['address_keyword'], '-' ) !== FALSE )
997 +
998 + if ( $do_address_search )
743 999 {
744 - $address_keywords[] = str_replace("-", " ", ph_clean($_REQUEST['address_keyword']));
745 - }
746 1000
747 - $meta_query = array('relation' => 'OR');
1001 + $address_keywords_to_query = is_array($address_input) ? $address_input : array( $address_input );
748 1002
749 - $address_fields_to_query = array(
750 - '_reference_number',
751 - '_address_street',
752 - '_address_two',
753 - '_address_three',
754 - '_address_four',
755 - '_address_postcode'
756 - );
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 + );
757 1012
758 - $address_fields_to_query = apply_filters( 'propertyhive_address_fields_to_query', $address_fields_to_query );
1013 + $address_keywords = array();
759 1014
760 - foreach ( $address_keywords as $address_keyword )
761 - {
762 - foreach ( $address_fields_to_query as $address_field )
763 - {
764 - 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);
765 1021
766 - $meta_query[] = array(
767 - 'key' => $address_field,
768 - 'value' => $address_keyword,
769 - 'compare' => get_option( 'propertyhive_address_keyword_compare', '=' )
770 - );
771 - }
772 - }
773 - if ( in_array('_address_postcode', $address_fields_to_query) )
774 - {
775 - 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 )
776 1064 {
777 - $meta_query[] = array(
778 - 'key' => '_address_postcode',
779 - 'value' => ph_clean( $_REQUEST['address_keyword'] ),
780 - 'compare' => '='
781 - );
782 - $meta_query[] = array(
783 - 'key' => '_address_postcode',
784 - 'value' => '^' . ph_clean( $_REQUEST['address_keyword'] ) . '[ ]',
785 - 'compare' => 'RLIKE'
786 - );
1065 + $address_fields_to_query[] = '_address_country';
787 1066 }
788 - 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 )
789 1072 {
790 - $meta_query[] = array(
791 - 'key' => '_address_postcode',
792 - 'value' => ph_clean( $_REQUEST['address_keyword'] ),
793 - 'compare' => 'LIKE'
794 - );
795 - }
796 - }
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 + }
797 1183 }
798 1184
799 1185 return $meta_query;
800 1186 }
801 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 +
802 1214 /**
803 1215 * Returns a meta query to handle country
804 1216 *
805 1217 * @access public
@@ -808,15 +1220,28 @@
808 1220 public function country_meta_query( ) {
809 1221
810 1222 $meta_query = array();
811 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.
812 1225 if ( isset( $_REQUEST['country'] ) && $_REQUEST['country'] != '' )
813 1226 {
814 1227 $meta_query = array(
815 1228 'key' => '_address_country',
816 - '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'] ) )
817 1231 );
818 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 + }
819 1244
820 1245 return $meta_query;
821 1246 }
822 1247
@@ -826,19 +1251,30 @@
826 1251 * @access public
827 1252 * @return array
828 1253 */
829 1254 public function minimum_price_meta_query( ) {
1255 + $request_department = $this->get_requested_department();
1256 +
830 1257
831 1258 $meta_query = array();
832 1259
833 1260 if (
834 - 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.
835 1263 isset( $_REQUEST['minimum_price'] ) && $_REQUEST['minimum_price'] != ''
836 1264 )
837 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 +
838 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 );
839 1276
840 - $minimum_price = $_REQUEST['minimum_price'];
841 1277 if ( $search_form_currency != 'GBP' )
842 1278 {
843 1279 // Convert $_REQUEST['minimum_price'] to GBP
844 1280 $ph_countries = new PH_Countries();
@@ -863,19 +1299,30 @@
863 1299 * @access public
864 1300 * @return array
865 1301 */
866 1302 public function maximum_price_meta_query( ) {
1303 + $request_department = $this->get_requested_department();
1304 +
867 1305
868 1306 $meta_query = array();
869 1307
870 1308 if (
871 - 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.
872 1311 isset( $_REQUEST['maximum_price'] ) && $_REQUEST['maximum_price'] != ''
873 1312 )
874 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 +
875 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 );
876 1324
877 - $maximum_price = $_REQUEST['maximum_price'];
878 1325 if ( $search_form_currency != 'GBP' )
879 1326 {
880 1327 // Convert $_REQUEST['maximum_price'] to GBP
881 1328 $ph_countries = new PH_Countries();
@@ -900,24 +1347,34 @@
900 1347 * @access public
901 1348 * @return array
902 1349 */
903 1350 public function price_range_meta_query( ) {
1351 + $request_department = $this->get_requested_department();
1352 +
904 1353
905 1354 $meta_query = array();
906 1355
907 1356 if (
908 - isset( $_REQUEST['department'] ) && $_REQUEST['department'] == 'residential-sales' &&
909 - 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'] != ''
910 1360 )
911 1361 {
912 - $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'] ) ));
913 1364
914 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 );
915 1367
916 1368 if ( isset($explode_price_range[0]) && $explode_price_range[0] != '' )
917 1369 {
918 1370 $minimum_price = $explode_price_range[0];
919 1371
1372 + if ( !is_numeric($minimum_price) )
1373 + {
1374 + return $meta_query;
1375 + }
1376 +
920 1377 if ( $search_form_currency != 'GBP' )
921 1378 {
922 1379 // Convert $explode_price_range[0] to GBP
923 1380 $ph_countries = new PH_Countries();
@@ -934,8 +1391,14 @@
934 1391 }
935 1392 if ( isset($explode_price_range[1]) && $explode_price_range[1] != '' )
936 1393 {
937 1394 $maximum_price = $explode_price_range[1];
1395 +
1396 + if ( !is_numeric($maximum_price) )
1397 + {
1398 + return $meta_query;
1399 + }
1400 +
938 1401 if ( $search_form_currency != 'GBP' )
939 1402 {
940 1403 // Convert $explode_price_range[1] to GBP
941 1404 $ph_countries = new PH_Countries();
@@ -961,19 +1424,30 @@
961 1424 * @access public
962 1425 * @return array
963 1426 */
964 1427 public function minimum_rent_meta_query( ) {
1428 + $request_department = $this->get_requested_department();
1429 +
965 1430
966 1431 $meta_query = array();
967 1432
968 1433 if (
969 - 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.
970 1436 isset( $_REQUEST['minimum_rent'] ) && $_REQUEST['minimum_rent'] != ''
971 1437 )
972 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 +
973 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 );
974 1449
975 - $minimum_rent = $_REQUEST['minimum_rent'];
976 1450 if ( $search_form_currency != 'GBP' )
977 1451 {
978 1452 // Convert $_REQUEST['minimum_rent'] to GBP
979 1453 $ph_countries = new PH_Countries();
@@ -980,8 +1454,17 @@
980 1454
981 1455 $minimum_rent = $ph_countries->convert_price_to_gbp( $minimum_rent, $search_form_currency );
982 1456 }
983 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 +
984 1467 $meta_query = array(
985 1468 'key' => '_price_actual',
986 1469 'value' => ph_clean( floor( $minimum_rent ) ),
987 1470 'compare' => '>=',
@@ -998,19 +1481,30 @@
998 1481 * @access public
999 1482 * @return array
1000 1483 */
1001 1484 public function maximum_rent_meta_query( ) {
1485 + $request_department = $this->get_requested_department();
1486 +
1002 1487
1003 1488 $meta_query = array();
1004 1489
1005 1490 if (
1006 - 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.
1007 1493 isset( $_REQUEST['maximum_rent'] ) && $_REQUEST['maximum_rent'] != ''
1008 1494 )
1009 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 +
1010 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 );
1011 1506
1012 - $maximum_rent = $_REQUEST['maximum_rent'];
1013 1507 if ( $search_form_currency != 'GBP' )
1014 1508 {
1015 1509 // Convert $_REQUEST['maximum_rent'] to GBP
1016 1510 $ph_countries = new PH_Countries();
@@ -1017,8 +1511,17 @@
1017 1511
1018 1512 $maximum_rent = $ph_countries->convert_price_to_gbp( $maximum_rent, $search_form_currency );
1019 1513 }
1020 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 +
1021 1524 $meta_query = array(
1022 1525 'key' => '_price_actual',
1023 1526 'value' => ph_clean( ceil( $maximum_rent ) ),
1024 1527 'compare' => '<=',
@@ -1035,23 +1538,36 @@
1035 1538 * @access public
1036 1539 * @return array
1037 1540 */
1038 1541 public function rent_range_meta_query( ) {
1542 + $request_department = $this->get_requested_department();
1543 +
1039 1544
1040 1545 $meta_query = array();
1041 1546
1042 1547 if (
1043 - isset( $_REQUEST['department'] ) && $_REQUEST['department'] == 'residential-lettings' &&
1044 - 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'] != ''
1045 1551 )
1046 1552 {
1047 - $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'] ) ));
1048 1555
1049 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 );
1050 1558
1559 + $rent_frequency = apply_filters( 'propertyhive_search_form_rent_frequency', 'pcm' );
1560 +
1051 1561 if ( isset($explode_rent_range[0]) && $explode_rent_range[0] != '' )
1052 1562 {
1053 1563 $minimum_rent = $explode_rent_range[0];
1564 +
1565 + if ( !is_numeric($minimum_rent) )
1566 + {
1567 + return $meta_query;
1568 + }
1569 +
1054 1570 if ( $search_form_currency != 'GBP' )
1055 1571 {
1056 1572 // Convert $explode_rent_range[0] to GBP
1057 1573 $ph_countries = new PH_Countries();
@@ -1058,8 +1574,16 @@
1058 1574
1059 1575 $minimum_rent = $ph_countries->convert_price_to_gbp( $minimum_rent, $search_form_currency );
1060 1576 }
1061 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 +
1062 1586 $meta_query[] = array(
1063 1587 'key' => '_price_actual',
1064 1588 'value' => sanitize_text_field( floor( $minimum_rent ) ),
1065 1589 'compare' => '>=',
@@ -1068,8 +1592,14 @@
1068 1592 }
1069 1593 if ( isset($explode_rent_range[1]) && $explode_rent_range[1] != '' )
1070 1594 {
1071 1595 $maximum_rent = $explode_rent_range[1];
1596 +
1597 + if ( !is_numeric($maximum_rent) )
1598 + {
1599 + return $meta_query;
1600 + }
1601 +
1072 1602 if ( $search_form_currency != 'GBP' )
1073 1603 {
1074 1604 // Convert $explode_rent_range[1] to GBP
1075 1605 $ph_countries = new PH_Countries();
@@ -1076,8 +1606,16 @@
1076 1606
1077 1607 $maximum_rent = $ph_countries->convert_price_to_gbp( $maximum_rent, $search_form_currency );
1078 1608 }
1079 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 +
1080 1618 $meta_query[] = array(
1081 1619 'key' => '_price_actual',
1082 1620 'value' => sanitize_text_field( ceil( $maximum_rent ) ),
1083 1621 'compare' => '<=',
@@ -1095,22 +1633,26 @@
1095 1633 * @access public
1096 1634 * @return array
1097 1635 */
1098 1636 public function bedrooms_meta_query( ) {
1637 + $request_department = $this->get_requested_department();
1638 +
1099 1639
1100 1640 $meta_query = array();
1101 1641
1102 1642 if (
1103 1643 (
1104 - (isset( $_REQUEST['department'] ) && $_REQUEST['department'] == 'residential-sales') ||
1105 - (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' ))
1106 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.
1107 1648 isset( $_REQUEST['bedrooms'] ) && $_REQUEST['bedrooms'] != ''
1108 1649 )
1109 1650 {
1110 1651 $meta_query = array(
1111 1652 'key' => '_bedrooms',
1112 - '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'] ) ),
1113 1655 'compare' => '=',
1114 1656 'type' => 'NUMERIC'
1115 1657 );
1116 1658 }
@@ -1124,22 +1666,26 @@
1124 1666 * @access public
1125 1667 * @return array
1126 1668 */
1127 1669 public function minimum_bedrooms_meta_query( ) {
1670 + $request_department = $this->get_requested_department();
1671 +
1128 1672
1129 1673 $meta_query = array();
1130 1674
1131 1675 if (
1132 1676 (
1133 - (isset( $_REQUEST['department'] ) && $_REQUEST['department'] == 'residential-sales') ||
1134 - (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' ))
1135 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.
1136 1681 isset( $_REQUEST['minimum_bedrooms'] ) && $_REQUEST['minimum_bedrooms'] != ''
1137 1682 )
1138 1683 {
1139 1684 $meta_query = array(
1140 1685 'key' => '_bedrooms',
1141 - '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'] ) ),
1142 1688 'compare' => '>=',
1143 1689 'type' => 'NUMERIC'
1144 1690 );
1145 1691 }
@@ -1153,22 +1699,26 @@
1153 1699 * @access public
1154 1700 * @return array
1155 1701 */
1156 1702 public function maximum_bedrooms_meta_query( ) {
1703 + $request_department = $this->get_requested_department();
1704 +
1157 1705
1158 1706 $meta_query = array();
1159 1707
1160 1708 if (
1161 1709 (
1162 - (isset( $_REQUEST['department'] ) && $_REQUEST['department'] == 'residential-sales') ||
1163 - (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' ))
1164 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.
1165 1714 isset( $_REQUEST['maximum_bedrooms'] ) && $_REQUEST['maximum_bedrooms'] != ''
1166 1715 )
1167 1716 {
1168 1717 $meta_query = array(
1169 1718 'key' => '_bedrooms',
1170 - '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'] ) ),
1171 1721 'compare' => '<=',
1172 1722 'type' => 'NUMERIC'
1173 1723 );
1174 1724 }
@@ -1182,22 +1732,26 @@
1182 1732 * @access public
1183 1733 * @return array
1184 1734 */
1185 1735 public function minimum_bathrooms_meta_query( ) {
1736 + $request_department = $this->get_requested_department();
1737 +
1186 1738
1187 1739 $meta_query = array();
1188 1740
1189 1741 if (
1190 1742 (
1191 - (isset( $_REQUEST['department'] ) && $_REQUEST['department'] == 'residential-sales') ||
1192 - (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' ))
1193 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.
1194 1747 isset( $_REQUEST['minimum_bathrooms'] ) && $_REQUEST['minimum_bathrooms'] != ''
1195 1748 )
1196 1749 {
1197 1750 $meta_query = array(
1198 1751 'key' => '_bathrooms',
1199 - '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'] ) ),
1200 1754 'compare' => '>=',
1201 1755 'type' => 'NUMERIC'
1202 1756 );
1203 1757 }
@@ -1211,22 +1765,26 @@
1211 1765 * @access public
1212 1766 * @return array
1213 1767 */
1214 1768 public function maximum_bathrooms_meta_query( ) {
1769 + $request_department = $this->get_requested_department();
1770 +
1215 1771
1216 1772 $meta_query = array();
1217 1773
1218 1774 if (
1219 1775 (
1220 - (isset( $_REQUEST['department'] ) && $_REQUEST['department'] == 'residential-sales') ||
1221 - (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' ))
1222 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.
1223 1780 isset( $_REQUEST['maximum_bathrooms'] ) && $_REQUEST['maximum_bathrooms'] != ''
1224 1781 )
1225 1782 {
1226 1783 $meta_query = array(
1227 1784 'key' => '_bathrooms',
1228 - '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'] ) ),
1229 1787 'compare' => '<=',
1230 1788 'type' => 'NUMERIC'
1231 1789 );
1232 1790 }
@@ -1240,22 +1798,26 @@
1240 1798 * @access public
1241 1799 * @return array
1242 1800 */
1243 1801 public function minimum_reception_rooms_meta_query( ) {
1802 + $request_department = $this->get_requested_department();
1803 +
1244 1804
1245 1805 $meta_query = array();
1246 1806
1247 1807 if (
1248 1808 (
1249 - (isset( $_REQUEST['department'] ) && $_REQUEST['department'] == 'residential-sales') ||
1250 - (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' ))
1251 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.
1252 1813 isset( $_REQUEST['minimum_reception_rooms'] ) && $_REQUEST['minimum_reception_rooms'] != ''
1253 1814 )
1254 1815 {
1255 1816 $meta_query = array(
1256 1817 'key' => '_reception_rooms',
1257 - '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'] ) ),
1258 1820 'compare' => '>=',
1259 1821 'type' => 'NUMERIC'
1260 1822 );
1261 1823 }
@@ -1269,22 +1831,26 @@
1269 1831 * @access public
1270 1832 * @return array
1271 1833 */
1272 1834 public function maximum_reception_rooms_meta_query( ) {
1835 + $request_department = $this->get_requested_department();
1836 +
1273 1837
1274 1838 $meta_query = array();
1275 1839
1276 1840 if (
1277 1841 (
1278 - (isset( $_REQUEST['department'] ) && $_REQUEST['department'] == 'residential-sales') ||
1279 - (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' ))
1280 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.
1281 1846 isset( $_REQUEST['maximum_reception_rooms'] ) && $_REQUEST['maximum_reception_rooms'] != ''
1282 1847 )
1283 1848 {
1284 1849 $meta_query = array(
1285 1850 'key' => '_reception_rooms',
1286 - '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'] ) ),
1287 1853 'compare' => '<=',
1288 1854 'type' => 'NUMERIC'
1289 1855 );
1290 1856 }
@@ -1298,17 +1864,21 @@
1298 1864 * @access public
1299 1865 * @return array
1300 1866 */
1301 1867 public function available_date_from_meta_query( ) {
1868 + $request_department = $this->get_requested_department();
1869 +
1302 1870
1303 1871 $meta_query = array();
1304 1872
1305 1873 if (
1306 - isset( $_REQUEST['department'] ) && $_REQUEST['department'] == 'residential-lettings' &&
1307 - 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'] != ''
1308 1877 )
1309 1878 {
1310 - $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'] ) );
1311 1881 if ( strpos($available_date, '/') !== FALSE )
1312 1882 {
1313 1883 // it's been provided in the format dd/mm/yyyy
1314 1884 $explode_available_date = explode("/", $available_date);
@@ -1333,27 +1903,40 @@
1333 1903 * @access public
1334 1904 * @return array
1335 1905 */
1336 1906 public function minimum_floor_area_meta_query( ) {
1907 + $request_department = $this->get_requested_department();
1908 +
1337 1909
1338 1910 $meta_query = array();
1339 1911
1340 1912 if (
1341 - 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.
1342 1915 isset( $_REQUEST['minimum_floor_area'] ) && $_REQUEST['minimum_floor_area'] != '' &&
1343 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.
1344 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.
1345 1920 ( isset( $_REQUEST['maximum_floor_area'] ) && $_REQUEST['maximum_floor_area'] == '' )
1346 1921 )
1347 1922 )
1348 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 +
1349 1932 $meta_query = array(
1350 - 'key' => '_floor_area_from_sqft',
1351 - 'value' => ph_clean( $_REQUEST['minimum_floor_area'] ),
1933 + 'key' => '_floor_area_to_sqft',
1934 + 'value' => $value,
1352 1935 'compare' => '>=',
1353 1936 'type' => 'NUMERIC'
1354 - );
1355 - }
1937 + );
1938 + }
1356 1939
1357 1940 return $meta_query;
1358 1941 }
1359 1942
@@ -1363,26 +1946,39 @@
1363 1946 * @access public
1364 1947 * @return array
1365 1948 */
1366 1949 public function maximum_floor_area_meta_query( ) {
1950 + $request_department = $this->get_requested_department();
1951 +
1367 1952
1368 1953 $meta_query = array();
1369 1954
1370 1955 if (
1371 - 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.
1372 1958 isset( $_REQUEST['maximum_floor_area'] ) && $_REQUEST['maximum_floor_area'] != '' &&
1373 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.
1374 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.
1375 1963 ( isset( $_REQUEST['minimum_floor_area'] ) && $_REQUEST['minimum_floor_area'] == '' )
1376 1964 )
1377 1965 )
1378 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 +
1379 1975 $meta_query = array(
1380 - 'key' => '_floor_area_to_sqft',
1381 - 'value' => ph_clean( $_REQUEST['maximum_floor_area'] ),
1976 + 'key' => '_floor_area_from_sqft',
1977 + 'value' => $value,
1382 1978 'compare' => '<=',
1383 1979 'type' => 'NUMERIC'
1384 - );
1980 + );
1385 1981 }
1386 1982
1387 1983 return $meta_query;
1388 1984 }
@@ -1393,26 +1989,41 @@
1393 1989 * @access public
1394 1990 * @return array
1395 1991 */
1396 1992 public function minimum_maximum_floor_area_meta_query( ) {
1993 + $request_department = $this->get_requested_department();
1994 +
1397 1995
1398 1996 $meta_query = array();
1399 1997
1400 1998 if (
1401 - 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.
1402 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.
1403 2003 isset( $_REQUEST['maximum_floor_area'] ) && $_REQUEST['maximum_floor_area'] != ''
1404 2004 )
1405 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 +
1406 2017 $meta_query[] = array(
1407 2018 'key' => '_floor_area_from_sqft',
1408 - 'value' => ph_clean( $_REQUEST['maximum_floor_area'] ),
2019 + 'value' => $maximum_floor_area,
1409 2020 'compare' => '<=',
1410 2021 'type' => 'NUMERIC'
1411 2022 );
1412 2023 $meta_query[] = array(
1413 2024 'key' => '_floor_area_to_sqft',
1414 - 'value' => ph_clean( $_REQUEST['minimum_floor_area'] ),
2025 + 'value' => $minimum_floor_area,
1415 2026 'compare' => '>=',
1416 2027 'type' => 'NUMERIC'
1417 2028 );
1418 2029 }
@@ -1427,17 +2038,21 @@
1427 2038 * @access public
1428 2039 * @return array
1429 2040 */
1430 2041 public function floor_area_range_meta_query( ) {
2042 + $request_department = $this->get_requested_department();
2043 +
1431 2044
1432 2045 $meta_query = array();
1433 2046
1434 2047 if (
1435 - isset( $_REQUEST['department'] ) && $_REQUEST['department'] == 'commercial' &&
1436 - 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'] != ''
1437 2051 )
1438 2052 {
1439 - $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'] ) ));
1440 2055
1441 2056 if ( isset($explode_floor_area_range[0]) && $explode_floor_area_range[0] != '' )
1442 2057 {
1443 2058 $meta_query = array(
@@ -1467,13 +2082,16 @@
1467 2082 * @access public
1468 2083 * @return array
1469 2084 */
1470 2085 public function commercial_for_sale_to_rent_meta_query( ) {
2086 + $request_department = $this->get_requested_department();
2087 +
1471 2088
1472 2089 $meta_query = array();
1473 2090
1474 2091 if (
1475 - 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.
1476 2094 isset( $_REQUEST['commercial_for_sale_to_rent'] ) && $_REQUEST['commercial_for_sale_to_rent'] == 'for_sale'
1477 2095 )
1478 2096 {
1479 2097 $meta_query = array(
@@ -1483,9 +2101,10 @@
1483 2101 );
1484 2102 }
1485 2103
1486 2104 if (
1487 - 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.
1488 2107 isset( $_REQUEST['commercial_for_sale_to_rent'] ) && $_REQUEST['commercial_for_sale_to_rent'] == 'to_rent'
1489 2108 )
1490 2109 {
1491 2110 $meta_query = array(
@@ -1504,13 +2123,16 @@
1504 2123 * @access public
1505 2124 * @return array
1506 2125 */
1507 2126 public function commercial_for_sale_meta_query( ) {
2127 + $request_department = $this->get_requested_department();
2128 +
1508 2129
1509 2130 $meta_query = array();
1510 2131
1511 2132 if (
1512 - 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.
1513 2135 isset( $_REQUEST['commercial_for_sale'] ) && $_REQUEST['commercial_for_sale'] == '1'
1514 2136 )
1515 2137 {
1516 2138 $meta_query = array(
@@ -1529,13 +2151,16 @@
1529 2151 * @access public
1530 2152 * @return array
1531 2153 */
1532 2154 public function commercial_to_rent_meta_query( ) {
2155 + $request_department = $this->get_requested_department();
2156 +
1533 2157
1534 2158 $meta_query = array();
1535 2159
1536 2160 if (
1537 - 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.
1538 2163 isset( $_REQUEST['commercial_to_rent'] ) && $_REQUEST['commercial_to_rent'] == '1'
1539 2164 )
1540 2165 {
1541 2166 $meta_query = array(
@@ -1548,8 +2173,228 @@
1548 2173 return $meta_query;
1549 2174 }
1550 2175
1551 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 + /**
1552 2397 * Returns a meta query to handle property negotiator
1553 2398 *
1554 2399 * @access public
1555 2400 * @return array
@@ -1557,12 +2402,14 @@
1557 2402 public function negotiator_meta_query( ) {
1558 2403
1559 2404 $meta_query = array();
1560 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.
1561 2407 if ( isset( $_REQUEST['negotiator_id'] ) && $_REQUEST['negotiator_id'] != '' )
1562 2408 {
1563 2409 $meta_query = array(
1564 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.
1565 2412 'value' => (int)$_REQUEST['negotiator_id'],
1566 2413 'compare' => '='
1567 2414 );
1568 2415 }
@@ -1580,14 +2427,16 @@
1580 2427 public function office_meta_query( ) {
1581 2428
1582 2429 $meta_query = array();
1583 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.
1584 2432 if ( isset( $_REQUEST['officeID'] ) && $_REQUEST['officeID'] != '' )
1585 2433 {
1586 2434 $meta_query = array(
1587 2435 'key' => '_office_id',
1588 - 'value' => (int)$_REQUEST['officeID'],
1589 - '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'
1590 2439 );
1591 2440 }
1592 2441
1593 2442 return $meta_query;
@@ -1592,8 +2441,168 @@
1592 2441
1593 2442 return $meta_query;
1594 2443 }
1595 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 +
1596 2605 /**
1597 2606 * Appends taxonomy queries to an array.
1598 2607 * @access public
1599 2608 * @param array $tax_query
@@ -1602,17 +2611,30 @@
1602 2611 public function get_tax_query( $tax_query = array() ) {
1603 2612 if ( ! is_array( $tax_query ) )
1604 2613 $tax_query = array();
1605 2614
2615 + // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Public read-only taxonomy search; this does not authorize a write.
1606 2616 if ( isset($_REQUEST) && !empty($_REQUEST) )
1607 2617 {
2618 +
2619 + // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Public read-only taxonomy search; each value is validated and sanitized below.
1608 2620 foreach ( $_REQUEST as $key => $value )
1609 2621 {
1610 - 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 ) )
1611 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 +
1612 2633 $tax_query[] = array(
1613 2634 'taxonomy' => $key,
1614 - 'terms' => ph_clean( (is_array($value)) ? $value : array( $value ) )
2635 + 'terms' => ph_clean( wp_unslash( $terms ) ),
2636 + 'operator' => $operator,
1615 2637 );
1616 2638 }
1617 2639 }
1618 2640 }
@@ -1621,15 +2643,22 @@
1621 2643 }
1622 2644
1623 2645 private function taxonomy_allowed_for_department( $taxonomy )
1624 2646 {
1625 - if ( isset( $_REQUEST['department'] ) && $_REQUEST['department'] != '' )
2647 + $request_department = $this->get_requested_department();
2648 +
2649 + if ( isset( $request_department ) && $request_department != '' )
1626 2650 {
1627 - $department = ph_clean($_REQUEST['department']);
2651 + $department = ph_clean($request_department);
1628 2652 }
1629 2653 else
1630 2654 {
1631 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 );
1632 2661 }
1633 2662
1634 2663 switch ( $department )
1635 2664 {