PluginProbe
Property Hive / trunk
Property Hive vtrunk
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 1.4.62 All 260 releases
← All changes | includes/class-ph-query.php +1165 -166 1.4.53trunk View file →
@@ -1,5 +1,8 @@
1 1 <?php
2 +// phpcs:set WordPress.Security.ValidatedSanitizedInput customSanitizingFunctions[] ph_clean
3 +// ph_clean() recursively sanitizes text; presence, shape and unslashing checks remain separate.
4 +
2 5 /**
3 6 * Contains the query functions for PropertyHive which alter the front-end post queries and loops.
4 7 *
5 8 * @class PH_Query
@@ -17,8 +20,18 @@
17 20 * PH_Query Class
18 21 */
19 22 class PH_Query {
20 23
24 + /** Keyword normalized by this request's meta-query builder, shared across query instances. */
25 + private static $normalized_keyword = null;
26 +
27 + /** Read a department slug for this query without changing the shared request. */
28 + private function get_requested_department() {
29 + // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- A public search filter only; it does not authorize a write.
30 + return isset( $_REQUEST['department'] ) && is_string( $_REQUEST['department'] ) ? sanitize_text_field( wp_unslash( $_REQUEST['department'] ) ) : null;
31 + }
32 +
33 +
21 34 /** @public array Query vars to add to wp */
22 35 public $query_vars = array();
23 36
24 37 /** @public array Unfiltered property ids (before layered nav etc) */
@@ -35,8 +48,11 @@
35 48
36 49 /** @public array The meta query for the page */
37 50 public $meta_query = '';
38 51
52 + /** @public array The tax query for the page */
53 + public $tax_query = '';
54 +
39 55 /** @public array Post IDs matching layered nav only */
40 56 public $layered_nav_post__in = array();
41 57
42 58 /** @public array Stores post IDs matching layered nav, so price filter can find max price in view */
@@ -41,8 +57,11 @@
41 57
42 58 /** @public array Stores post IDs matching layered nav, so price filter can find max price in view */
43 59 public $layered_nav_property_ids = array();
44 60
61 + /** @public array Stores post IDs matching layered nav, so price filter can find max price in view */
62 + public $address_keyword_polygon_points = array();
63 +
45 64 /**
46 65 * Constructor for the query class. Hooks in methods.
47 66 *
48 67 * @access public
@@ -48,9 +67,8 @@
48 67 * @access public
49 68 */
50 69 public function __construct() {
51 70
52 - //add_action( 'init', array( $this, 'add_endpoints' ) );
53 71 add_action( 'init', array( $this, 'layered_nav_init' ) );
54 72 add_action( 'init', array( $this, 'price_filter_init' ) );
55 73
56 74 if ( ! is_admin() ) {
@@ -60,13 +78,152 @@
60 78 add_filter( 'pre_get_posts', array( $this, 'pre_get_posts' ) );
61 79 add_filter( 'the_posts', array( $this, 'the_posts' ), 11, 2 );
62 80 add_action( 'wp', array( $this, 'remove_property_query' ) );
63 81 add_action( 'wp', array( $this, 'remove_ordering_args' ) );
82 + add_filter( 'posts_where', array( $this, 'commercial_display_where' ), 10, 2 );
83 + add_filter( 'posts_where', array( $this, 'keyword_excerpt_where' ), 10, 2 );
84 + add_action( 'pre_get_posts', array( $this, 'custom_order_properties_by_availability' ), 10, 2 );
64 85 }
65 86
66 87 $this->init_query_vars();
67 88 }
68 89
90 + public function custom_order_properties_by_availability($query)
91 + {
92 + if ( is_admin() )
93 + {
94 + return;
95 + }
96 +
97 + if ( !$query->is_main_query() )
98 + {
99 + return;
100 + }
101 +
102 + if ( !is_post_type_archive('property') )
103 + {
104 + return;
105 + }
106 +
107 + if ( apply_filters( 'propertyhive_order_by_availability', false ) === false )
108 + {
109 + return;
110 + }
111 +
112 + $availability_order = get_option('propertyhive_taxonomy_terms_order_availability', array());
113 +
114 + if ( empty($availability_order) )
115 + {
116 + return;
117 + }
118 +
119 + // Sanitize and prepare the order
120 + $availability_order = explode("|", $availability_order);
121 + $availability_order = array_map('intval', $availability_order);
122 +
123 + // Modify the main query to join with term relationships and term taxonomy tables using custom aliases
124 + add_filter('posts_join', function ($join, $query)
125 + {
126 + global $wpdb;
127 +
128 + if ($query->is_main_query() && is_post_type_archive('property'))
129 + {
130 + $join .= " LEFT JOIN {$wpdb->term_relationships} AS avstr ON ({$wpdb->posts}.ID = avstr.object_id) ";
131 + $join .= " LEFT JOIN {$wpdb->term_taxonomy} AS avstt ON (avstr.term_taxonomy_id = avstt.term_taxonomy_id) ";
132 + }
133 +
134 + return $join;
135 + }, 10, 2);
136 +
137 + // Add a custom ordering clause
138 + add_filter('posts_orderby', function ($orderby, $query) use ($availability_order)
139 + {
140 + global $wpdb;
141 +
142 + if ($query->is_main_query() && is_post_type_archive('property')) {
143 + // Retrieve the original orderby clause
144 + $original_orderby = $orderby ? $orderby : "{$wpdb->posts}.post_date DESC";
145 +
146 + // Construct the custom order by clause
147 + $order_by_custom = "FIELD(avstt.term_id, " . implode(',', $availability_order) . ")";
148 +
149 + // Combine the custom order by with the original order by
150 + $orderby_combined = "$order_by_custom, $original_orderby";
151 +
152 + return $orderby_combined;
153 + }
154 +
155 + return $orderby;
156 + }, 10, 2);
157 + }
158 +
159 + public function keyword_excerpt_where( $where, $query )
160 + {
161 + if ( ( is_array($query->get('post_type')) && in_array('property', $query->get('post_type')) ) || ( !is_array($query->get('post_type')) && $query->get('post_type') == 'property' ) )
162 + {
163 + global $wpdb;
164 +
165 + // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- The public keyword filter reads the request, sanitizes/SQL-escapes it, and contributes only to the current SQL WHERE clause. This exact annotation covers only NonceVerification.Recommended; retain all sanitizer and SQL-safety checks.
166 + if ( isset($_REQUEST['keyword']) && is_string( $_REQUEST['keyword'] ) && $_REQUEST['keyword'] != '' )
167 + {
168 + // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only search; reuse the already-unslashed value produced by keyword_meta_query when available.
169 + $keyword = isset( self::$normalized_keyword ) && $_REQUEST['keyword'] === self::$normalized_keyword ? self::$normalized_keyword : sanitize_text_field( wp_unslash( $_REQUEST['keyword'] ) );
170 + $ref_pos = strpos($where, '_features_concatenated');
171 + if ( $ref_pos !== FALSE )
172 + {
173 + // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- The public keyword filter reads the request, sanitizes/SQL-escapes it, and contributes only to the current SQL WHERE clause. This exact annotation covers only NonceVerification.Recommended; retain all sanitizer and SQL-safety checks.
174 + $str_to_insert = " $wpdb->posts.post_excerpt LIKE '%" . esc_sql( $keyword ) . "%' OR ";
175 + $where = substr_replace($where, $str_to_insert, $ref_pos - 18, 0);
176 + }
177 + }
178 + }
179 +
180 + return $where;
181 + }
182 +
183 + public function commercial_display_where( $where, $query )
184 + {
185 + if ( $query->get('post_type') == 'property' )
186 + {
187 + global $wpdb;
188 +
189 + $commercial_display = get_option( 'propertyhive_commercial_display', '' );
190 +
191 + switch ( $commercial_display )
192 + {
193 + case "top_level_only":
194 + {
195 + $where .= " AND $wpdb->posts.post_parent=0 ";
196 + break;
197 + }
198 + case "top_level_only_but_units_when_filtered":
199 + {
200 + $unit_filter_parameters = apply_filters( 'propertyhive_unit_filter_parameters', array( 'minimum_floor_area', 'maximum_floor_area' ) );
201 + $unit_filter_parameter_found = false;
202 + foreach ( $unit_filter_parameters as $parameter )
203 + {
204 + // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- The public commercial display filter reads a request flag and contributes only to the current SQL WHERE clause. This exact annotation covers only NonceVerification.Recommended; retain all sanitizer and SQL-safety checks.
205 + if ( isset($_REQUEST[$parameter]) && ph_clean( wp_unslash( $_REQUEST[$parameter] ) ) != '' )
206 + {
207 + $unit_filter_parameter_found = true;
208 + }
209 + }
210 + if ( !$unit_filter_parameter_found )
211 + {
212 + $where .= " AND $wpdb->posts.post_parent=0 ";
213 + }
214 + break;
215 + }
216 + default:
217 + {
218 + // do nothing
219 + }
220 + }
221 + }
222 +
223 + return $where;
224 + }
225 +
69 226 /**
70 227 * Init query vars by loading options.
71 228 */
72 229 public function init_query_vars() {
@@ -79,21 +236,14 @@
79 236 /**
80 237 * Get any errors from querystring
81 238 */
82 239 public function get_errors() {
83 - if ( ! empty( $_GET['ph_error'] ) && ( $error = sanitize_text_field( $_GET['ph_error'] ) ) && ! ph_has_notice( $error, 'error' ) )
240 + // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- The public frontend reads ph_error to add a request-scoped notice; it does not write posts, options, user data, or other persistent state. This exact annotation covers only NonceVerification.Recommended; retain all sanitizer and SQL-safety checks.
241 + if ( ! empty( $_GET['ph_error'] ) && ( $error = sanitize_text_field( wp_unslash( $_GET['ph_error'] ) ) ) && ! ph_has_notice( $error, 'error' ) )
84 242 ph_add_notice( $error, 'error' );
85 243 }
86 244
87 245 /**
88 - * Add endpoints for query vars
89 - */
90 - public function add_endpoints() {
91 - foreach ( $this->query_vars as $key => $var )
92 - add_rewrite_endpoint( $var, EP_PAGES );
93 - }
94 -
95 - /**
96 246 * add_query_vars function.
97 247 *
98 248 * @access public
99 249 * @param array $vars
@@ -121,9 +271,11 @@
121 271 global $wp;
122 272
123 273 // Map query vars to their keys, or get them if endpoints are not supported
124 274 foreach ( $this->query_vars as $key => $var ) {
275 + // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- The public frontend normalizes a URL query variable into the current WP request query_vars; this is request/query state only and has no persistent write. This exact annotation covers only NonceVerification.Recommended; retain all sanitizer and SQL-safety checks.
125 276 if ( isset( $_GET[ $var ] ) ) {
277 + // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- The public frontend normalizes a URL query variable into the current WP request query_vars; this is request/query state only and has no persistent write. This exact annotation covers only NonceVerification.Recommended; retain all sanitizer and SQL-safety checks.
126 278 $wp->query_vars[ $key ] = sanitize_text_field( wp_unslash( $_GET[ $var ] ) );
127 279 }
128 280
129 281 elseif ( isset( $wp->query_vars[ $var ] ) ) {
@@ -180,8 +332,9 @@
180 332 if ( isset( $q->query['paged'] ) )
181 333 $q->set( 'paged', $q->query['paged'] );
182 334
183 335 // Define a variable so we know this is the front page search results later on
336 + // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedConstantFound -- Retain the existing frontend state constant for theme and extension compatibility.
184 337 define( 'SEARCH_RESULTS_IS_ON_FRONT', true );
185 338
186 339 // Get the actual WP page to avoid errors and let us use is_front_page()
187 340 // This is hacky but works. Awaiting http://core.trac.wordpress.org/ticket/21096
@@ -371,9 +524,9 @@
371 524 $post__in = array();
372 525 //$post__in = array_unique( apply_filters( 'loop_shop_post_in', array() ) );
373 526
374 527 // Ordering query vars
375 - $q->set( 'orderby', $ordering['orderby'] );
528 + $q->set( 'orderby', $ordering['orderby'] . ' post_title' );
376 529 $q->set( 'order', $ordering['order'] );
377 530 if ( isset( $ordering['meta_key'] ) )
378 531 $q->set( 'meta_key', $ordering['meta_key'] );
379 532
@@ -381,8 +534,9 @@
381 534 $q->set( 'meta_query', $meta_query );
382 535 $q->set( 'tax_query', $tax_query );
383 536 $q->set( 'date_query', $date_query );
384 537 $q->set( 'post__in', $post__in );
538 + // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound -- Existing public Property Hive extension hook loop_search_results_per_page; changing the established name would detach installed callbacks.
385 539 $q->set( 'posts_per_page', $q->get( 'posts_per_page' ) ? $q->get( 'posts_per_page' ) : apply_filters( 'loop_search_results_per_page', get_option( 'posts_per_page' ) ) );
386 540
387 541 // Set a special variable
388 542 $q->set( 'ph_query', true );
@@ -454,8 +608,9 @@
454 608 array(
455 609 'post_type' => 'property',
456 610 'numberposts' => -1,
457 611 'post_status' => 'publish',
612 + // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_query -- Cached property IDs must preserve the current search metadata predicates; only IDs are fetched, without totals or metadata/term cache priming.
458 613 'meta_query' => $this->meta_query,
459 614 'fields' => 'ids',
460 615 'no_found_rows' => true,
461 616 'update_post_meta_cache' => false,
@@ -490,11 +645,14 @@
490 645 * @access public
491 646 * @return array
492 647 */
493 648 public function get_search_results_ordering_args( $orderby = '', $order = '' ) {
649 + $request_department = $this->get_requested_department();
650 +
494 651 // Get ordering from query string unless defined
495 652 if ( ! $orderby ) {
496 - $orderby_value = isset( $_GET['orderby'] ) ? sanitize_text_field( $_GET['orderby'] ) : apply_filters( 'propertyhive_default_search_results_orderby', get_option( 'propertyhive_default_search_results_orderby' ) );
653 + // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- The public property search reads ordering/filter inputs and returns ordering arguments for the current query only. This exact annotation covers only NonceVerification.Recommended; retain all sanitizer and SQL-safety checks.
654 + $orderby_value = isset( $_GET['orderby'] ) ? sanitize_text_field( wp_unslash( $_GET['orderby'] ) ) : apply_filters( 'propertyhive_default_search_results_orderby', get_option( 'propertyhive_default_search_results_orderby' ) );
497 655
498 656 // Get order + orderby args from string
499 657 $orderby_value = explode( '-', $orderby_value );
500 658 $orderby = esc_attr( $orderby_value[0] );
@@ -507,23 +665,29 @@
507 665 $args = array();
508 666
509 667 // default - menu_order
510 668 if (
511 - ( isset($_REQUEST['department']) && $_REQUEST['department'] != 'commercial' ) ||
512 - ( !isset($_REQUEST['department']) && get_option( 'propertyhive_primary_department' ) != 'commercial' )
669 + // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- The public property search reads ordering/filter inputs and returns ordering arguments for the current query only. This exact annotation covers only NonceVerification.Recommended; retain all sanitizer and SQL-safety checks.
670 + ( isset($request_department) && $request_department != 'commercial' && ph_get_custom_department_based_on($request_department) != 'commercial' ) ||
671 + // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- The public property search reads ordering/filter inputs and returns ordering arguments for the current query only. This exact annotation covers only NonceVerification.Recommended; retain all sanitizer and SQL-safety checks.
672 + ( !isset($request_department) && get_option( 'propertyhive_primary_department' ) != 'commercial' && ph_get_custom_department_based_on(get_option( 'propertyhive_primary_department' )) != 'commercial' )
513 673 )
514 674 {
515 675 $args['orderby'] = 'meta_value_num';
516 676 $args['order'] = $order == 'ASC' ? 'ASC' : 'DESC';
677 + // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_key -- Search ordering maps supported price/floor-area/date choices to literal stored meta keys. switch cases and department branches set fixed keys; order direction is constrained to ASC/DESC.
517 678 $args['meta_key'] = '_price_actual';
518 679 }
519 680 elseif (
520 - ( isset($_REQUEST['department']) && $_REQUEST['department'] == 'commercial' ) ||
521 - ( !isset($_REQUEST['department']) && get_option( 'propertyhive_primary_department' ) == 'commercial' )
681 + // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- The public property search reads ordering/filter inputs and returns ordering arguments for the current query only. This exact annotation covers only NonceVerification.Recommended; retain all sanitizer and SQL-safety checks.
682 + ( isset($request_department) && ( $request_department == 'commercial' || ph_get_custom_department_based_on($request_department) == 'commercial' ) ) ||
683 + // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- The public property search reads ordering/filter inputs and returns ordering arguments for the current query only. This exact annotation covers only NonceVerification.Recommended; retain all sanitizer and SQL-safety checks.
684 + ( !isset($request_department) && ( get_option( 'propertyhive_primary_department' ) == 'commercial' || ph_get_custom_department_based_on(get_option( 'propertyhive_primary_department' )) == 'commercial' ) )
522 685 )
523 686 {
524 687 $args['orderby'] = 'meta_value_num';
525 688 $args['order'] = $order == 'ASC' ? 'ASC' : 'DESC';
689 + // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_key -- Search ordering maps supported price/floor-area/date choices to literal stored meta keys. switch cases and department branches set fixed keys; order direction is constrained to ASC/DESC.
526 690 $args['meta_key'] = '_floor_area_from_sqft';
527 691 }
528 692
529 693 switch ( $orderby ) {
@@ -530,16 +694,20 @@
530 694 case 'price' :
531 695 $args['orderby'] = 'meta_value_num';
532 696 $args['order'] = $order == 'ASC' ? 'ASC' : 'DESC';
533 697 if (
534 - ( isset($_REQUEST['department']) && $_REQUEST['department'] == 'commercial' ) ||
535 - ( !isset($_REQUEST['department']) && get_option( 'propertyhive_primary_department' ) == 'commercial' )
698 + // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- The public property search reads ordering/filter inputs and returns ordering arguments for the current query only. This exact annotation covers only NonceVerification.Recommended; retain all sanitizer and SQL-safety checks.
699 + ( isset($request_department) && ( $request_department == 'commercial' || ph_get_custom_department_based_on($request_department) == 'commercial' ) ) ||
700 + // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- The public property search reads ordering/filter inputs and returns ordering arguments for the current query only. This exact annotation covers only NonceVerification.Recommended; retain all sanitizer and SQL-safety checks.
701 + ( !isset($request_department) && ( get_option( 'propertyhive_primary_department' ) == 'commercial' || ph_get_custom_department_based_on(get_option( 'propertyhive_primary_department' )) == 'commercial' ) )
536 702 )
537 703 {
704 + // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_key -- Search ordering maps supported price/floor-area/date choices to literal stored meta keys. switch cases and department branches set fixed keys; order direction is constrained to ASC/DESC.
538 705 $args['meta_key'] = '_price_from_actual';
539 706 }
540 707 else
541 708 {
709 + // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_key -- Search ordering maps supported price/floor-area/date choices to literal stored meta keys. switch cases and department branches set fixed keys; order direction is constrained to ASC/DESC.
542 710 $args['meta_key'] = '_price_actual';
543 711 }
544 712 break;
545 713 case 'floor_area' :
@@ -544,10 +712,17 @@
544 712 break;
545 713 case 'floor_area' :
546 714 $args['orderby'] = 'meta_value_num';
547 715 $args['order'] = $order == 'ASC' ? 'ASC' : 'DESC';
716 + // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_key -- Search ordering maps supported price/floor-area/date choices to literal stored meta keys. switch cases and department branches set fixed keys; order direction is constrained to ASC/DESC.
548 717 $args['meta_key'] = '_floor_area_from_sqft';
549 718 break;
719 + case 'date' :
720 + $args['orderby'] = 'meta_value';
721 + $args['order'] = $order == 'ASC' ? 'ASC' : 'DESC';
722 + // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_key -- Search ordering maps supported price/floor-area/date choices to literal stored meta keys. switch cases and department branches set fixed keys; order direction is constrained to ASC/DESC.
723 + $args['meta_key'] = '_on_market_change_date';
724 + break;
550 725 default :
551 726 {
552 727 if ( $orderby != '' )
553 728 {
@@ -569,21 +744,25 @@
569 744 public function get_date_query() {
570 745
571 746 $date_query = array();
572 747
748 + // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- The public property search reads a filter value and contributes query/meta/taxonomy arguments for the current request only; no persistent state-changing operation is reached. This exact annotation covers only NonceVerification.Recommended; retain all sanitizer and SQL-safety checks.
573 749 if ( isset( $_REQUEST['added_from'] ) && $_REQUEST['added_from'] != '' )
574 750 {
575 751 $date_query = array(
576 752 'column' => 'post_date_gmt',
577 - 'after' => sanitize_text_field( $_REQUEST['added_from'] )
753 + // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- The public property search reads a filter value and contributes query/meta/taxonomy arguments for the current request only; no persistent state-changing operation is reached. This exact annotation covers only NonceVerification.Recommended; retain all sanitizer and SQL-safety checks.
754 + 'after' => sanitize_text_field( wp_unslash( $_REQUEST['added_from'] ) )
578 755 );
579 756 }
580 757
758 + // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- The public property search reads a filter value and contributes query/meta/taxonomy arguments for the current request only; no persistent state-changing operation is reached. This exact annotation covers only NonceVerification.Recommended; retain all sanitizer and SQL-safety checks.
581 759 if ( isset( $_REQUEST['added_from_hours'] ) && $_REQUEST['added_from_hours'] != '' )
582 760 {
583 761 $date_query = array(
584 762 'column' => 'post_date_gmt',
585 - 'after' => sanitize_text_field( $_REQUEST['added_from_hours'] ) . ' hours ago'
763 + // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- The public property search reads a filter value and contributes query/meta/taxonomy arguments for the current request only; no persistent state-changing operation is reached. This exact annotation covers only NonceVerification.Recommended; retain all sanitizer and SQL-safety checks.
764 + 'after' => sanitize_text_field( wp_unslash( $_REQUEST['added_from_hours'] ) ) . ' hours ago'
586 765 );
587 766 }
588 767
589 768 return array_filter( $date_query );
@@ -607,8 +786,10 @@
607 786 $meta_query = array();
608 787
609 788 $meta_query[] = $this->on_market_meta_query();
610 789 $meta_query[] = $this->department_meta_query($q);
790 + $meta_query[] = $this->featured_meta_query();
791 + $meta_query[] = $this->date_added_meta_query();
611 792 $meta_query[] = $this->address_keyword_meta_query();
612 793 $meta_query[] = $this->country_meta_query();
613 794 $meta_query[] = $this->minimum_price_meta_query();
614 795 $meta_query[] = $this->maximum_price_meta_query();
@@ -630,11 +811,16 @@
630 811 $meta_query[] = $this->floor_area_range_meta_query();
631 812 $meta_query[] = $this->commercial_for_sale_to_rent_meta_query();
632 813 $meta_query[] = $this->commercial_for_sale_meta_query();
633 814 $meta_query[] = $this->commercial_to_rent_meta_query();
815 + $meta_query[] = $this->commercial_minimum_price_meta_query();
816 + $meta_query[] = $this->commercial_maximum_price_meta_query();
817 + $meta_query[] = $this->commercial_minimum_rent_meta_query();
818 + $meta_query[] = $this->commercial_maximum_rent_meta_query();
634 819 $meta_query[] = $this->negotiator_meta_query();
635 820 $meta_query[] = $this->office_meta_query();
636 -
821 + $meta_query[] = $this->keyword_meta_query();
822 +
637 823 return array_filter( apply_filters( 'propertyhive_property_query_meta_query', $meta_query, $this ) );
638 824 }
639 825
640 826 /**
@@ -666,16 +852,18 @@
666 852 * @access public
667 853 * @return array
668 854 */
669 855 public function department_meta_query( $q ) {
856 + $request_department = $this->get_requested_department();
857 +
670 858
671 859 $meta_query = array();
672 860
673 - if ( isset( $_REQUEST['department'] ) && $_REQUEST['department'] != '' )
861 + if ( isset( $request_department ) && $request_department != '' )
674 862 {
675 863 $meta_query = array(
676 864 'key' => '_department',
677 - 'value' => sanitize_text_field( $_REQUEST['department'] ),
865 + 'value' => sanitize_text_field( $request_department ),
678 866 'compare' => '='
679 867 );
680 868 }
681 869 else
@@ -716,120 +904,314 @@
716 904 return $meta_query;
717 905 }
718 906
719 907 /**
908 + * Returns a meta query to handle featured
909 + *
910 + * @access public
911 + * @return array
912 + */
913 + public function featured_meta_query( ) {
914 +
915 + $meta_query = array();
916 +
917 + // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- The public property search reads a filter value and contributes query/meta/taxonomy arguments for the current request only; no persistent state-changing operation is reached. This exact annotation covers only NonceVerification.Recommended; retain all sanitizer and SQL-safety checks.
918 + if ( isset( $_REQUEST['featured'] ) && $_REQUEST['featured'] != '' )
919 + {
920 + $meta_query = array(
921 + 'key' => '_featured',
922 + 'value' => 'yes',
923 + 'compare' => '='
924 + );
925 + }
926 +
927 + return $meta_query;
928 + }
929 +
930 + /**
931 + * Returns a meta query to handle date added
932 + *
933 + * @access public
934 + * @return array
935 + */
936 + public function date_added_meta_query( ) {
937 +
938 + $meta_query = array();
939 +
940 + // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- The public property search reads a filter value and contributes query/meta/taxonomy arguments for the current request only; no persistent state-changing operation is reached. This exact annotation covers only NonceVerification.Recommended; retain all sanitizer and SQL-safety checks.
941 + if ( isset( $_REQUEST['date_added'] ) && $_REQUEST['date_added'] != '' && is_numeric($_REQUEST['date_added']) )
942 + {
943 + $meta_query = array(
944 + 'key' => '_on_market_change_date',
945 + // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- The public property search reads a filter value and contributes query/meta/taxonomy arguments for the current request only; no persistent state-changing operation is reached. This exact annotation covers only NonceVerification.Recommended; retain all sanitizer and SQL-safety checks.
946 + 'value' => gmdate('Y-m-d H:i:s', strtotime('-' . sanitize_text_field( wp_unslash( $_REQUEST['date_added'] ) ) . ' days')),
947 + 'compare' => '>=',
948 + 'type' => 'DATETIME',
949 + );
950 + }
951 +
952 + return $meta_query;
953 + }
954 +
955 + /**
720 956 * Returns a meta query to handle searching for a keyword in the address
721 957 *
722 958 * @access public
723 - * @param string $compare (default: 'IN')
724 959 * @return array
725 960 */
726 961 public function address_keyword_meta_query( ) {
727 962
728 963 $meta_query = array();
729 -
730 - if ( isset( $_REQUEST['address_keyword'] ) && $_REQUEST['address_keyword'] != '' )
964 +
965 + // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Public read-only search control; no persistent state change.
966 + if ( isset( $_REQUEST['address_keyword'] ) && !empty($_REQUEST['address_keyword']) )
731 967 {
732 - $_REQUEST['address_keyword'] = ph_clean( wp_unslash( $_REQUEST['address_keyword'] ) );
733 968
734 - // Remove country code from end (i.e. ', UK')
735 - $_REQUEST['address_keyword'] = preg_replace('/\,\s?[A-Z][A-Z]$/', '', $_REQUEST['address_keyword']);
969 + // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Public read-only address search; values are validated below before query construction.
970 + $address_input = ph_clean( wp_unslash( $_REQUEST['address_keyword'] ) );
971 + if ( ! is_string( $address_input ) && ! is_array( $address_input ) ) {
972 + return $meta_query;
973 + }
974 + foreach ( (array) $address_input as $address_value ) {
975 + if ( ! is_string( $address_value ) ) {
976 + return $meta_query;
977 + }
978 + }
979 + $address_input = ph_clean( $address_input );
980 + // Preserve the normalized request value consumed by existing extensions.
981 + $_REQUEST['address_keyword'] = $address_input;
736 982
737 - $address_keywords = array( $_REQUEST['address_keyword'] );
983 + $do_address_search = true;
984 + if ( is_string( $address_input ) && get_option( 'propertyhive_address_keyword_compare', '=' ) == 'polygon' )
985 + {
986 + $address_keyword_polygon = new PH_Address_Keyword_Polygon();
738 987
739 - if ( strpos( $_REQUEST['address_keyword'], ' ' ) !== FALSE )
740 - {
741 - $address_keywords[] = str_replace(" ", "-", ph_clean($_REQUEST['address_keyword']));
988 + $polygon_coordinates = $address_keyword_polygon->get_address_keyword_polygon_coordinates( $address_input . ', UK' );
989 +
990 + if ( $polygon_coordinates !== FALSE )
991 + {
992 + $this->address_keyword_polygon_points = $polygon_coordinates;
993 + add_filter( 'posts_where' , array( $this, 'where_properties_in_polygon' ), 1, 2 );
994 + $do_address_search = false;
995 + }
742 996 }
743 - if ( strpos( $_REQUEST['address_keyword'], '-' ) !== FALSE )
997 +
998 + if ( $do_address_search )
744 999 {
745 - $address_keywords[] = str_replace("-", " ", ph_clean($_REQUEST['address_keyword']));
746 - }
747 1000
748 - $meta_query = array('relation' => 'OR');
1001 + $address_keywords_to_query = is_array($address_input) ? $address_input : array( $address_input );
749 1002
750 - $address_fields_to_query = array(
751 - '_reference_number',
752 - '_address_street',
753 - '_address_two',
754 - '_address_three',
755 - '_address_four',
756 - '_address_postcode'
757 - );
1003 + $address_fields_to_query = array(
1004 + '_reference_number',
1005 + '_address_street',
1006 + '_address_two',
1007 + '_address_three',
1008 + '_address_four',
1009 + '_address_postcode',
1010 + '_address_concatenated',
1011 + );
758 1012
759 - $address_fields_to_query = apply_filters( 'propertyhive_address_fields_to_query', $address_fields_to_query );
1013 + $address_keywords = array();
760 1014
761 - foreach ( $address_keywords as $address_keyword )
762 - {
763 - foreach ( $address_fields_to_query as $address_field )
764 - {
765 - if ( $address_field == '_address_postcode' ) { continue; } // ignore postcode as that is handled differently afterwards
1015 + if ( !empty($address_keywords_to_query) )
1016 + {
1017 + foreach ( $address_keywords_to_query as $address_keyword )
1018 + {
1019 + // Remove country code from end (i.e. ', UK')
1020 + $address_keyword = preg_replace('/\,\s?[A-Z][A-Z]$/', '', $address_keyword);
766 1021
767 - $meta_query[] = array(
768 - 'key' => $address_field,
769 - 'value' => $address_keyword,
770 - 'compare' => get_option( 'propertyhive_address_keyword_compare', '=' )
771 - );
772 - }
773 - }
774 - if ( in_array('_address_postcode', $address_fields_to_query) )
775 - {
776 - if ( strlen($_REQUEST['address_keyword']) <= 4 )
1022 + // Extract postcode and use that if exists
1023 + $postcode_pattern = '/\b([A-Z]{1,2}[0-9][0-9A-Z]? ?[0-9]?[A-Z]{0,2})\b/i';
1024 + if ( preg_match($postcode_pattern, $address_keyword, $matches) )
1025 + {
1026 + $address_keyword = $matches[1];
1027 + }
1028 +
1029 + $address_keyword = trim($address_keyword);
1030 +
1031 + $address_keywords[] = ph_clean($address_keyword);
1032 +
1033 + if ( strpos( $address_keyword, ' ' ) !== FALSE )
1034 + {
1035 + $address_keywords[] = str_replace(" ", "-", ph_clean($address_keyword));
1036 + }
1037 + if ( strpos( $address_keyword, '-' ) !== FALSE )
1038 + {
1039 + $address_keywords[] = str_replace("-", " ", ph_clean($address_keyword));
1040 + }
1041 + if ( strpos( $address_keyword, '.' ) !== FALSE )
1042 + {
1043 + $address_keywords[] = str_replace(".", "", ph_clean($address_keyword));
1044 + }
1045 + if ( stripos( $address_keyword, 'st ' ) !== FALSE )
1046 + {
1047 + $address_keywords[] = str_ireplace("st ", "st. ", ph_clean($address_keyword));
1048 + }
1049 + if ( strpos( $address_keyword, '\'' ) !== FALSE )
1050 + {
1051 + $address_keywords[] = str_replace("'", "", ph_clean($address_keyword));
1052 + }
1053 + }
1054 + }
1055 +
1056 + $address_keywords = apply_filters( 'propertyhive_address_keywords_to_query', $address_keywords );
1057 +
1058 + $meta_query = array('relation' => 'OR');
1059 +
1060 + // add country to list of fields to query if it looks like we're working with an overseas site
1061 + $countries = get_option( 'propertyhive_countries', array() );
1062 + if ( !is_array($countries) ) { $countries = array(); }
1063 + if ( count($countries) > 1 )
777 1064 {
778 - $meta_query[] = array(
779 - 'key' => '_address_postcode',
780 - 'value' => ph_clean( $_REQUEST['address_keyword'] ),
781 - 'compare' => '='
782 - );
783 - $meta_query[] = array(
784 - 'key' => '_address_postcode',
785 - 'value' => '^' . ph_clean( $_REQUEST['address_keyword'] ) . '[ ]',
786 - 'compare' => 'RLIKE'
787 - );
1065 + $address_fields_to_query[] = '_address_country';
788 1066 }
789 - else
1067 +
1068 + $address_fields_to_query = array_unique($address_fields_to_query);
1069 + $address_fields_to_query = apply_filters( 'propertyhive_address_fields_to_query', $address_fields_to_query );
1070 +
1071 + foreach ( $address_keywords as $address_keyword )
790 1072 {
791 - $postcode = ph_clean( $_REQUEST['address_keyword'] );
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
792 1076
793 - 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) )
794 - {
795 - // UK postcode found with no space
1077 + $meta_query[] = array(
1078 + 'key' => $address_field,
1079 + 'value' => $address_keyword,
1080 + 'compare' => get_option( 'propertyhive_address_keyword_compare', '=' )
1081 + );
1082 + }
796 1083
797 - if ( strlen($postcode) == 5 )
798 - {
799 - $first_part = substr($postcode, 0, 2);
800 - $last_part = substr($postcode, 2, 3);
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);
801 1105
802 - $postcode = $first_part . ' ' . $last_part;
803 - }
804 - elseif ( strlen($postcode) == 6 )
805 - {
806 - $first_part = substr($postcode, 0, 3);
807 - $last_part = substr($postcode, 3, 3);
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
808 1109
809 - $postcode = $first_part . ' ' . $last_part;
810 - }
811 - elseif ( strlen($postcode) == 7 )
812 - {
813 - $first_part = substr($postcode, 0, 4);
814 - $last_part = substr($postcode, 4, 3);
1110 + if ( strlen($postcode) == 5 )
1111 + {
1112 + $first_part = substr($postcode, 0, 2);
1113 + $last_part = substr($postcode, 2, 3);
815 1114
816 - $postcode = $first_part . ' ' . $last_part;
817 - }
818 - }
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);
819 1121
820 - $meta_query[] = array(
821 - 'key' => '_address_postcode',
822 - 'value' => ph_clean( $postcode ),
823 - 'compare' => 'LIKE'
824 - );
825 - }
826 - }
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 + }
827 1183 }
828 1184
829 1185 return $meta_query;
830 1186 }
831 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 +
832 1214 /**
833 1215 * Returns a meta query to handle country
834 1216 *
835 1217 * @access public
@@ -838,15 +1220,28 @@
838 1220 public function country_meta_query( ) {
839 1221
840 1222 $meta_query = array();
841 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.
842 1225 if ( isset( $_REQUEST['country'] ) && $_REQUEST['country'] != '' )
843 1226 {
844 1227 $meta_query = array(
845 1228 'key' => '_address_country',
846 - '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'] ) )
847 1231 );
848 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 + }
849 1244
850 1245 return $meta_query;
851 1246 }
852 1247
@@ -856,19 +1251,30 @@
856 1251 * @access public
857 1252 * @return array
858 1253 */
859 1254 public function minimum_price_meta_query( ) {
1255 + $request_department = $this->get_requested_department();
1256 +
860 1257
861 1258 $meta_query = array();
862 1259
863 1260 if (
864 - 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.
865 1263 isset( $_REQUEST['minimum_price'] ) && $_REQUEST['minimum_price'] != ''
866 1264 )
867 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 +
868 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 );
869 1276
870 - $minimum_price = $_REQUEST['minimum_price'];
871 1277 if ( $search_form_currency != 'GBP' )
872 1278 {
873 1279 // Convert $_REQUEST['minimum_price'] to GBP
874 1280 $ph_countries = new PH_Countries();
@@ -893,19 +1299,30 @@
893 1299 * @access public
894 1300 * @return array
895 1301 */
896 1302 public function maximum_price_meta_query( ) {
1303 + $request_department = $this->get_requested_department();
1304 +
897 1305
898 1306 $meta_query = array();
899 1307
900 1308 if (
901 - 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.
902 1311 isset( $_REQUEST['maximum_price'] ) && $_REQUEST['maximum_price'] != ''
903 1312 )
904 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 +
905 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 );
906 1324
907 - $maximum_price = $_REQUEST['maximum_price'];
908 1325 if ( $search_form_currency != 'GBP' )
909 1326 {
910 1327 // Convert $_REQUEST['maximum_price'] to GBP
911 1328 $ph_countries = new PH_Countries();
@@ -930,24 +1347,34 @@
930 1347 * @access public
931 1348 * @return array
932 1349 */
933 1350 public function price_range_meta_query( ) {
1351 + $request_department = $this->get_requested_department();
1352 +
934 1353
935 1354 $meta_query = array();
936 1355
937 1356 if (
938 - isset( $_REQUEST['department'] ) && $_REQUEST['department'] == 'residential-sales' &&
939 - 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'] != ''
940 1360 )
941 1361 {
942 - $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'] ) ));
943 1364
944 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 );
945 1367
946 1368 if ( isset($explode_price_range[0]) && $explode_price_range[0] != '' )
947 1369 {
948 1370 $minimum_price = $explode_price_range[0];
949 1371
1372 + if ( !is_numeric($minimum_price) )
1373 + {
1374 + return $meta_query;
1375 + }
1376 +
950 1377 if ( $search_form_currency != 'GBP' )
951 1378 {
952 1379 // Convert $explode_price_range[0] to GBP
953 1380 $ph_countries = new PH_Countries();
@@ -964,8 +1391,14 @@
964 1391 }
965 1392 if ( isset($explode_price_range[1]) && $explode_price_range[1] != '' )
966 1393 {
967 1394 $maximum_price = $explode_price_range[1];
1395 +
1396 + if ( !is_numeric($maximum_price) )
1397 + {
1398 + return $meta_query;
1399 + }
1400 +
968 1401 if ( $search_form_currency != 'GBP' )
969 1402 {
970 1403 // Convert $explode_price_range[1] to GBP
971 1404 $ph_countries = new PH_Countries();
@@ -991,19 +1424,30 @@
991 1424 * @access public
992 1425 * @return array
993 1426 */
994 1427 public function minimum_rent_meta_query( ) {
1428 + $request_department = $this->get_requested_department();
1429 +
995 1430
996 1431 $meta_query = array();
997 1432
998 1433 if (
999 - 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.
1000 1436 isset( $_REQUEST['minimum_rent'] ) && $_REQUEST['minimum_rent'] != ''
1001 1437 )
1002 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 +
1003 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 );
1004 1449
1005 - $minimum_rent = $_REQUEST['minimum_rent'];
1006 1450 if ( $search_form_currency != 'GBP' )
1007 1451 {
1008 1452 // Convert $_REQUEST['minimum_rent'] to GBP
1009 1453 $ph_countries = new PH_Countries();
@@ -1010,8 +1454,17 @@
1010 1454
1011 1455 $minimum_rent = $ph_countries->convert_price_to_gbp( $minimum_rent, $search_form_currency );
1012 1456 }
1013 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 +
1014 1467 $meta_query = array(
1015 1468 'key' => '_price_actual',
1016 1469 'value' => ph_clean( floor( $minimum_rent ) ),
1017 1470 'compare' => '>=',
@@ -1028,19 +1481,30 @@
1028 1481 * @access public
1029 1482 * @return array
1030 1483 */
1031 1484 public function maximum_rent_meta_query( ) {
1485 + $request_department = $this->get_requested_department();
1486 +
1032 1487
1033 1488 $meta_query = array();
1034 1489
1035 1490 if (
1036 - 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.
1037 1493 isset( $_REQUEST['maximum_rent'] ) && $_REQUEST['maximum_rent'] != ''
1038 1494 )
1039 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 +
1040 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 );
1041 1506
1042 - $maximum_rent = $_REQUEST['maximum_rent'];
1043 1507 if ( $search_form_currency != 'GBP' )
1044 1508 {
1045 1509 // Convert $_REQUEST['maximum_rent'] to GBP
1046 1510 $ph_countries = new PH_Countries();
@@ -1047,8 +1511,17 @@
1047 1511
1048 1512 $maximum_rent = $ph_countries->convert_price_to_gbp( $maximum_rent, $search_form_currency );
1049 1513 }
1050 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 +
1051 1524 $meta_query = array(
1052 1525 'key' => '_price_actual',
1053 1526 'value' => ph_clean( ceil( $maximum_rent ) ),
1054 1527 'compare' => '<=',
@@ -1065,23 +1538,36 @@
1065 1538 * @access public
1066 1539 * @return array
1067 1540 */
1068 1541 public function rent_range_meta_query( ) {
1542 + $request_department = $this->get_requested_department();
1543 +
1069 1544
1070 1545 $meta_query = array();
1071 1546
1072 1547 if (
1073 - isset( $_REQUEST['department'] ) && $_REQUEST['department'] == 'residential-lettings' &&
1074 - 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'] != ''
1075 1551 )
1076 1552 {
1077 - $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'] ) ));
1078 1555
1079 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 );
1080 1558
1559 + $rent_frequency = apply_filters( 'propertyhive_search_form_rent_frequency', 'pcm' );
1560 +
1081 1561 if ( isset($explode_rent_range[0]) && $explode_rent_range[0] != '' )
1082 1562 {
1083 1563 $minimum_rent = $explode_rent_range[0];
1564 +
1565 + if ( !is_numeric($minimum_rent) )
1566 + {
1567 + return $meta_query;
1568 + }
1569 +
1084 1570 if ( $search_form_currency != 'GBP' )
1085 1571 {
1086 1572 // Convert $explode_rent_range[0] to GBP
1087 1573 $ph_countries = new PH_Countries();
@@ -1088,8 +1574,16 @@
1088 1574
1089 1575 $minimum_rent = $ph_countries->convert_price_to_gbp( $minimum_rent, $search_form_currency );
1090 1576 }
1091 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 +
1092 1586 $meta_query[] = array(
1093 1587 'key' => '_price_actual',
1094 1588 'value' => sanitize_text_field( floor( $minimum_rent ) ),
1095 1589 'compare' => '>=',
@@ -1098,8 +1592,14 @@
1098 1592 }
1099 1593 if ( isset($explode_rent_range[1]) && $explode_rent_range[1] != '' )
1100 1594 {
1101 1595 $maximum_rent = $explode_rent_range[1];
1596 +
1597 + if ( !is_numeric($maximum_rent) )
1598 + {
1599 + return $meta_query;
1600 + }
1601 +
1102 1602 if ( $search_form_currency != 'GBP' )
1103 1603 {
1104 1604 // Convert $explode_rent_range[1] to GBP
1105 1605 $ph_countries = new PH_Countries();
@@ -1106,8 +1606,16 @@
1106 1606
1107 1607 $maximum_rent = $ph_countries->convert_price_to_gbp( $maximum_rent, $search_form_currency );
1108 1608 }
1109 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 +
1110 1618 $meta_query[] = array(
1111 1619 'key' => '_price_actual',
1112 1620 'value' => sanitize_text_field( ceil( $maximum_rent ) ),
1113 1621 'compare' => '<=',
@@ -1125,22 +1633,26 @@
1125 1633 * @access public
1126 1634 * @return array
1127 1635 */
1128 1636 public function bedrooms_meta_query( ) {
1637 + $request_department = $this->get_requested_department();
1638 +
1129 1639
1130 1640 $meta_query = array();
1131 1641
1132 1642 if (
1133 1643 (
1134 - (isset( $_REQUEST['department'] ) && $_REQUEST['department'] == 'residential-sales') ||
1135 - (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' ))
1136 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.
1137 1648 isset( $_REQUEST['bedrooms'] ) && $_REQUEST['bedrooms'] != ''
1138 1649 )
1139 1650 {
1140 1651 $meta_query = array(
1141 1652 'key' => '_bedrooms',
1142 - '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'] ) ),
1143 1655 'compare' => '=',
1144 1656 'type' => 'NUMERIC'
1145 1657 );
1146 1658 }
@@ -1154,22 +1666,26 @@
1154 1666 * @access public
1155 1667 * @return array
1156 1668 */
1157 1669 public function minimum_bedrooms_meta_query( ) {
1670 + $request_department = $this->get_requested_department();
1671 +
1158 1672
1159 1673 $meta_query = array();
1160 1674
1161 1675 if (
1162 1676 (
1163 - (isset( $_REQUEST['department'] ) && $_REQUEST['department'] == 'residential-sales') ||
1164 - (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' ))
1165 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.
1166 1681 isset( $_REQUEST['minimum_bedrooms'] ) && $_REQUEST['minimum_bedrooms'] != ''
1167 1682 )
1168 1683 {
1169 1684 $meta_query = array(
1170 1685 'key' => '_bedrooms',
1171 - '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'] ) ),
1172 1688 'compare' => '>=',
1173 1689 'type' => 'NUMERIC'
1174 1690 );
1175 1691 }
@@ -1183,22 +1699,26 @@
1183 1699 * @access public
1184 1700 * @return array
1185 1701 */
1186 1702 public function maximum_bedrooms_meta_query( ) {
1703 + $request_department = $this->get_requested_department();
1704 +
1187 1705
1188 1706 $meta_query = array();
1189 1707
1190 1708 if (
1191 1709 (
1192 - (isset( $_REQUEST['department'] ) && $_REQUEST['department'] == 'residential-sales') ||
1193 - (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' ))
1194 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.
1195 1714 isset( $_REQUEST['maximum_bedrooms'] ) && $_REQUEST['maximum_bedrooms'] != ''
1196 1715 )
1197 1716 {
1198 1717 $meta_query = array(
1199 1718 'key' => '_bedrooms',
1200 - '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'] ) ),
1201 1721 'compare' => '<=',
1202 1722 'type' => 'NUMERIC'
1203 1723 );
1204 1724 }
@@ -1212,22 +1732,26 @@
1212 1732 * @access public
1213 1733 * @return array
1214 1734 */
1215 1735 public function minimum_bathrooms_meta_query( ) {
1736 + $request_department = $this->get_requested_department();
1737 +
1216 1738
1217 1739 $meta_query = array();
1218 1740
1219 1741 if (
1220 1742 (
1221 - (isset( $_REQUEST['department'] ) && $_REQUEST['department'] == 'residential-sales') ||
1222 - (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' ))
1223 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.
1224 1747 isset( $_REQUEST['minimum_bathrooms'] ) && $_REQUEST['minimum_bathrooms'] != ''
1225 1748 )
1226 1749 {
1227 1750 $meta_query = array(
1228 1751 'key' => '_bathrooms',
1229 - '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'] ) ),
1230 1754 'compare' => '>=',
1231 1755 'type' => 'NUMERIC'
1232 1756 );
1233 1757 }
@@ -1241,22 +1765,26 @@
1241 1765 * @access public
1242 1766 * @return array
1243 1767 */
1244 1768 public function maximum_bathrooms_meta_query( ) {
1769 + $request_department = $this->get_requested_department();
1770 +
1245 1771
1246 1772 $meta_query = array();
1247 1773
1248 1774 if (
1249 1775 (
1250 - (isset( $_REQUEST['department'] ) && $_REQUEST['department'] == 'residential-sales') ||
1251 - (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' ))
1252 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.
1253 1780 isset( $_REQUEST['maximum_bathrooms'] ) && $_REQUEST['maximum_bathrooms'] != ''
1254 1781 )
1255 1782 {
1256 1783 $meta_query = array(
1257 1784 'key' => '_bathrooms',
1258 - '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'] ) ),
1259 1787 'compare' => '<=',
1260 1788 'type' => 'NUMERIC'
1261 1789 );
1262 1790 }
@@ -1270,22 +1798,26 @@
1270 1798 * @access public
1271 1799 * @return array
1272 1800 */
1273 1801 public function minimum_reception_rooms_meta_query( ) {
1802 + $request_department = $this->get_requested_department();
1803 +
1274 1804
1275 1805 $meta_query = array();
1276 1806
1277 1807 if (
1278 1808 (
1279 - (isset( $_REQUEST['department'] ) && $_REQUEST['department'] == 'residential-sales') ||
1280 - (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' ))
1281 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.
1282 1813 isset( $_REQUEST['minimum_reception_rooms'] ) && $_REQUEST['minimum_reception_rooms'] != ''
1283 1814 )
1284 1815 {
1285 1816 $meta_query = array(
1286 1817 'key' => '_reception_rooms',
1287 - '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'] ) ),
1288 1820 'compare' => '>=',
1289 1821 'type' => 'NUMERIC'
1290 1822 );
1291 1823 }
@@ -1299,22 +1831,26 @@
1299 1831 * @access public
1300 1832 * @return array
1301 1833 */
1302 1834 public function maximum_reception_rooms_meta_query( ) {
1835 + $request_department = $this->get_requested_department();
1836 +
1303 1837
1304 1838 $meta_query = array();
1305 1839
1306 1840 if (
1307 1841 (
1308 - (isset( $_REQUEST['department'] ) && $_REQUEST['department'] == 'residential-sales') ||
1309 - (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' ))
1310 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.
1311 1846 isset( $_REQUEST['maximum_reception_rooms'] ) && $_REQUEST['maximum_reception_rooms'] != ''
1312 1847 )
1313 1848 {
1314 1849 $meta_query = array(
1315 1850 'key' => '_reception_rooms',
1316 - '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'] ) ),
1317 1853 'compare' => '<=',
1318 1854 'type' => 'NUMERIC'
1319 1855 );
1320 1856 }
@@ -1328,17 +1864,21 @@
1328 1864 * @access public
1329 1865 * @return array
1330 1866 */
1331 1867 public function available_date_from_meta_query( ) {
1868 + $request_department = $this->get_requested_department();
1869 +
1332 1870
1333 1871 $meta_query = array();
1334 1872
1335 1873 if (
1336 - isset( $_REQUEST['department'] ) && $_REQUEST['department'] == 'residential-lettings' &&
1337 - 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'] != ''
1338 1877 )
1339 1878 {
1340 - $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'] ) );
1341 1881 if ( strpos($available_date, '/') !== FALSE )
1342 1882 {
1343 1883 // it's been provided in the format dd/mm/yyyy
1344 1884 $explode_available_date = explode("/", $available_date);
@@ -1363,27 +1903,40 @@
1363 1903 * @access public
1364 1904 * @return array
1365 1905 */
1366 1906 public function minimum_floor_area_meta_query( ) {
1907 + $request_department = $this->get_requested_department();
1908 +
1367 1909
1368 1910 $meta_query = array();
1369 1911
1370 1912 if (
1371 - 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.
1372 1915 isset( $_REQUEST['minimum_floor_area'] ) && $_REQUEST['minimum_floor_area'] != '' &&
1373 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.
1374 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.
1375 1920 ( isset( $_REQUEST['maximum_floor_area'] ) && $_REQUEST['maximum_floor_area'] == '' )
1376 1921 )
1377 1922 )
1378 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 +
1379 1932 $meta_query = array(
1380 - 'key' => '_floor_area_from_sqft',
1381 - 'value' => ph_clean( $_REQUEST['minimum_floor_area'] ),
1933 + 'key' => '_floor_area_to_sqft',
1934 + 'value' => $value,
1382 1935 'compare' => '>=',
1383 1936 'type' => 'NUMERIC'
1384 - );
1385 - }
1937 + );
1938 + }
1386 1939
1387 1940 return $meta_query;
1388 1941 }
1389 1942
@@ -1393,26 +1946,39 @@
1393 1946 * @access public
1394 1947 * @return array
1395 1948 */
1396 1949 public function maximum_floor_area_meta_query( ) {
1950 + $request_department = $this->get_requested_department();
1951 +
1397 1952
1398 1953 $meta_query = array();
1399 1954
1400 1955 if (
1401 - 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.
1402 1958 isset( $_REQUEST['maximum_floor_area'] ) && $_REQUEST['maximum_floor_area'] != '' &&
1403 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.
1404 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.
1405 1963 ( isset( $_REQUEST['minimum_floor_area'] ) && $_REQUEST['minimum_floor_area'] == '' )
1406 1964 )
1407 1965 )
1408 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 +
1409 1975 $meta_query = array(
1410 - 'key' => '_floor_area_to_sqft',
1411 - 'value' => ph_clean( $_REQUEST['maximum_floor_area'] ),
1976 + 'key' => '_floor_area_from_sqft',
1977 + 'value' => $value,
1412 1978 'compare' => '<=',
1413 1979 'type' => 'NUMERIC'
1414 - );
1980 + );
1415 1981 }
1416 1982
1417 1983 return $meta_query;
1418 1984 }
@@ -1423,26 +1989,41 @@
1423 1989 * @access public
1424 1990 * @return array
1425 1991 */
1426 1992 public function minimum_maximum_floor_area_meta_query( ) {
1993 + $request_department = $this->get_requested_department();
1994 +
1427 1995
1428 1996 $meta_query = array();
1429 1997
1430 1998 if (
1431 - 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.
1432 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.
1433 2003 isset( $_REQUEST['maximum_floor_area'] ) && $_REQUEST['maximum_floor_area'] != ''
1434 2004 )
1435 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 +
1436 2017 $meta_query[] = array(
1437 2018 'key' => '_floor_area_from_sqft',
1438 - 'value' => ph_clean( $_REQUEST['maximum_floor_area'] ),
2019 + 'value' => $maximum_floor_area,
1439 2020 'compare' => '<=',
1440 2021 'type' => 'NUMERIC'
1441 2022 );
1442 2023 $meta_query[] = array(
1443 2024 'key' => '_floor_area_to_sqft',
1444 - 'value' => ph_clean( $_REQUEST['minimum_floor_area'] ),
2025 + 'value' => $minimum_floor_area,
1445 2026 'compare' => '>=',
1446 2027 'type' => 'NUMERIC'
1447 2028 );
1448 2029 }
@@ -1457,17 +2038,21 @@
1457 2038 * @access public
1458 2039 * @return array
1459 2040 */
1460 2041 public function floor_area_range_meta_query( ) {
2042 + $request_department = $this->get_requested_department();
2043 +
1461 2044
1462 2045 $meta_query = array();
1463 2046
1464 2047 if (
1465 - isset( $_REQUEST['department'] ) && $_REQUEST['department'] == 'commercial' &&
1466 - 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'] != ''
1467 2051 )
1468 2052 {
1469 - $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'] ) ));
1470 2055
1471 2056 if ( isset($explode_floor_area_range[0]) && $explode_floor_area_range[0] != '' )
1472 2057 {
1473 2058 $meta_query = array(
@@ -1497,13 +2082,16 @@
1497 2082 * @access public
1498 2083 * @return array
1499 2084 */
1500 2085 public function commercial_for_sale_to_rent_meta_query( ) {
2086 + $request_department = $this->get_requested_department();
2087 +
1501 2088
1502 2089 $meta_query = array();
1503 2090
1504 2091 if (
1505 - 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.
1506 2094 isset( $_REQUEST['commercial_for_sale_to_rent'] ) && $_REQUEST['commercial_for_sale_to_rent'] == 'for_sale'
1507 2095 )
1508 2096 {
1509 2097 $meta_query = array(
@@ -1513,9 +2101,10 @@
1513 2101 );
1514 2102 }
1515 2103
1516 2104 if (
1517 - 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.
1518 2107 isset( $_REQUEST['commercial_for_sale_to_rent'] ) && $_REQUEST['commercial_for_sale_to_rent'] == 'to_rent'
1519 2108 )
1520 2109 {
1521 2110 $meta_query = array(
@@ -1534,13 +2123,16 @@
1534 2123 * @access public
1535 2124 * @return array
1536 2125 */
1537 2126 public function commercial_for_sale_meta_query( ) {
2127 + $request_department = $this->get_requested_department();
2128 +
1538 2129
1539 2130 $meta_query = array();
1540 2131
1541 2132 if (
1542 - 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.
1543 2135 isset( $_REQUEST['commercial_for_sale'] ) && $_REQUEST['commercial_for_sale'] == '1'
1544 2136 )
1545 2137 {
1546 2138 $meta_query = array(
@@ -1559,13 +2151,16 @@
1559 2151 * @access public
1560 2152 * @return array
1561 2153 */
1562 2154 public function commercial_to_rent_meta_query( ) {
2155 + $request_department = $this->get_requested_department();
2156 +
1563 2157
1564 2158 $meta_query = array();
1565 2159
1566 2160 if (
1567 - 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.
1568 2163 isset( $_REQUEST['commercial_to_rent'] ) && $_REQUEST['commercial_to_rent'] == '1'
1569 2164 )
1570 2165 {
1571 2166 $meta_query = array(
@@ -1578,8 +2173,228 @@
1578 2173 return $meta_query;
1579 2174 }
1580 2175
1581 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 + /**
1582 2397 * Returns a meta query to handle property negotiator
1583 2398 *
1584 2399 * @access public
1585 2400 * @return array
@@ -1587,12 +2402,14 @@
1587 2402 public function negotiator_meta_query( ) {
1588 2403
1589 2404 $meta_query = array();
1590 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.
1591 2407 if ( isset( $_REQUEST['negotiator_id'] ) && $_REQUEST['negotiator_id'] != '' )
1592 2408 {
1593 2409 $meta_query = array(
1594 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.
1595 2412 'value' => (int)$_REQUEST['negotiator_id'],
1596 2413 'compare' => '='
1597 2414 );
1598 2415 }
@@ -1610,14 +2427,16 @@
1610 2427 public function office_meta_query( ) {
1611 2428
1612 2429 $meta_query = array();
1613 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.
1614 2432 if ( isset( $_REQUEST['officeID'] ) && $_REQUEST['officeID'] != '' )
1615 2433 {
1616 2434 $meta_query = array(
1617 2435 'key' => '_office_id',
1618 - 'value' => (int)$_REQUEST['officeID'],
1619 - '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'
1620 2439 );
1621 2440 }
1622 2441
1623 2442 return $meta_query;
@@ -1622,8 +2441,168 @@
1622 2441
1623 2442 return $meta_query;
1624 2443 }
1625 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 +
1626 2605 /**
1627 2606 * Appends taxonomy queries to an array.
1628 2607 * @access public
1629 2608 * @param array $tax_query
@@ -1632,17 +2611,30 @@
1632 2611 public function get_tax_query( $tax_query = array() ) {
1633 2612 if ( ! is_array( $tax_query ) )
1634 2613 $tax_query = array();
1635 2614
2615 + // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Public read-only taxonomy search; this does not authorize a write.
1636 2616 if ( isset($_REQUEST) && !empty($_REQUEST) )
1637 2617 {
2618 +
2619 + // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Public read-only taxonomy search; each value is validated and sanitized below.
1638 2620 foreach ( $_REQUEST as $key => $value )
1639 2621 {
1640 - 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 ) )
1641 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 +
1642 2633 $tax_query[] = array(
1643 2634 'taxonomy' => $key,
1644 - 'terms' => ph_clean( (is_array($value)) ? $value : array( $value ) )
2635 + 'terms' => ph_clean( wp_unslash( $terms ) ),
2636 + 'operator' => $operator,
1645 2637 );
1646 2638 }
1647 2639 }
1648 2640 }
@@ -1651,15 +2643,22 @@
1651 2643 }
1652 2644
1653 2645 private function taxonomy_allowed_for_department( $taxonomy )
1654 2646 {
1655 - if ( isset( $_REQUEST['department'] ) && $_REQUEST['department'] != '' )
2647 + $request_department = $this->get_requested_department();
2648 +
2649 + if ( isset( $request_department ) && $request_department != '' )
1656 2650 {
1657 - $department = ph_clean($_REQUEST['department']);
2651 + $department = ph_clean($request_department);
1658 2652 }
1659 2653 else
1660 2654 {
1661 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 );
1662 2661 }
1663 2662
1664 2663 switch ( $department )
1665 2664 {