PluginProbe
Property Hive / 2.3.0
Property Hive v2.3.0
2.3.1 2.3.0 2.2.6 2.2.5 2.2.4 2.2.3 2.2.2 1.4.46 1.4.47 1.4.48 1.4.49 1.4.5 1.4.50 1.4.51 1.4.52 1.4.53 1.4.54 1.4.55 1.4.56 1.4.57 1.4.58 1.4.59 1.4.6 1.4.60 1.4.61 All 261 releases
← All changes | includes/class-ph-query.php +1517 -179 1.4.62.3.0 View file →
@@ -1,5 +1,8 @@
1 1 <?php
2 +// phpcs:set WordPress.Security.ValidatedSanitizedInput customSanitizingFunctions[] ph_clean
3 +// ph_clean() recursively sanitizes text; presence, shape and unslashing checks remain separate.
4 +
2 5 /**
3 6 * Contains the query functions for PropertyHive which alter the front-end post queries and loops.
4 7 *
5 8 * @class PH_Query
@@ -17,8 +20,18 @@
17 20 * PH_Query Class
18 21 */
19 22 class PH_Query {
20 23
24 + /** Keyword normalized by this request's meta-query builder, shared across query instances. */
25 + private static $normalized_keyword = null;
26 +
27 + /** Read a department slug for this query without changing the shared request. */
28 + private function get_requested_department() {
29 + // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- A public search filter only; it does not authorize a write.
30 + return isset( $_REQUEST['department'] ) && is_string( $_REQUEST['department'] ) ? sanitize_text_field( wp_unslash( $_REQUEST['department'] ) ) : null;
31 + }
32 +
33 +
21 34 /** @public array Query vars to add to wp */
22 35 public $query_vars = array();
23 36
24 37 /** @public array Unfiltered property ids (before layered nav etc) */
@@ -35,8 +48,11 @@
35 48
36 49 /** @public array The meta query for the page */
37 50 public $meta_query = '';
38 51
52 + /** @public array The tax query for the page */
53 + public $tax_query = '';
54 +
39 55 /** @public array Post IDs matching layered nav only */
40 56 public $layered_nav_post__in = array();
41 57
42 58 /** @public array Stores post IDs matching layered nav, so price filter can find max price in view */
@@ -41,8 +57,11 @@
41 57
42 58 /** @public array Stores post IDs matching layered nav, so price filter can find max price in view */
43 59 public $layered_nav_property_ids = array();
44 60
61 + /** @public array Stores post IDs matching layered nav, so price filter can find max price in view */
62 + public $address_keyword_polygon_points = array();
63 +
45 64 /**
46 65 * Constructor for the query class. Hooks in methods.
47 66 *
48 67 * @access public
@@ -48,9 +67,8 @@
48 67 * @access public
49 68 */
50 69 public function __construct() {
51 70
52 - //add_action( 'init', array( $this, 'add_endpoints' ) );
53 71 add_action( 'init', array( $this, 'layered_nav_init' ) );
54 72 add_action( 'init', array( $this, 'price_filter_init' ) );
55 73
56 74 if ( ! is_admin() ) {
@@ -60,13 +78,152 @@
60 78 add_filter( 'pre_get_posts', array( $this, 'pre_get_posts' ) );
61 79 add_filter( 'the_posts', array( $this, 'the_posts' ), 11, 2 );
62 80 add_action( 'wp', array( $this, 'remove_property_query' ) );
63 81 add_action( 'wp', array( $this, 'remove_ordering_args' ) );
82 + add_filter( 'posts_where', array( $this, 'commercial_display_where' ), 10, 2 );
83 + add_filter( 'posts_where', array( $this, 'keyword_excerpt_where' ), 10, 2 );
84 + add_action( 'pre_get_posts', array( $this, 'custom_order_properties_by_availability' ), 10, 2 );
64 85 }
65 86
66 87 $this->init_query_vars();
67 88 }
68 89
90 + public function custom_order_properties_by_availability($query)
91 + {
92 + if ( is_admin() )
93 + {
94 + return;
95 + }
96 +
97 + if ( !$query->is_main_query() )
98 + {
99 + return;
100 + }
101 +
102 + if ( !is_post_type_archive('property') )
103 + {
104 + return;
105 + }
106 +
107 + if ( apply_filters( 'propertyhive_order_by_availability', false ) === false )
108 + {
109 + return;
110 + }
111 +
112 + $availability_order = get_option('propertyhive_taxonomy_terms_order_availability', array());
113 +
114 + if ( empty($availability_order) )
115 + {
116 + return;
117 + }
118 +
119 + // Sanitize and prepare the order
120 + $availability_order = explode("|", $availability_order);
121 + $availability_order = array_map('intval', $availability_order);
122 +
123 + // Modify the main query to join with term relationships and term taxonomy tables using custom aliases
124 + add_filter('posts_join', function ($join, $query)
125 + {
126 + global $wpdb;
127 +
128 + if ($query->is_main_query() && is_post_type_archive('property'))
129 + {
130 + $join .= " LEFT JOIN {$wpdb->term_relationships} AS avstr ON ({$wpdb->posts}.ID = avstr.object_id) ";
131 + $join .= " LEFT JOIN {$wpdb->term_taxonomy} AS avstt ON (avstr.term_taxonomy_id = avstt.term_taxonomy_id) ";
132 + }
133 +
134 + return $join;
135 + }, 10, 2);
136 +
137 + // Add a custom ordering clause
138 + add_filter('posts_orderby', function ($orderby, $query) use ($availability_order)
139 + {
140 + global $wpdb;
141 +
142 + if ($query->is_main_query() && is_post_type_archive('property')) {
143 + // Retrieve the original orderby clause
144 + $original_orderby = $orderby ? $orderby : "{$wpdb->posts}.post_date DESC";
145 +
146 + // Construct the custom order by clause
147 + $order_by_custom = "FIELD(avstt.term_id, " . implode(',', $availability_order) . ")";
148 +
149 + // Combine the custom order by with the original order by
150 + $orderby_combined = "$order_by_custom, $original_orderby";
151 +
152 + return $orderby_combined;
153 + }
154 +
155 + return $orderby;
156 + }, 10, 2);
157 + }
158 +
159 + public function keyword_excerpt_where( $where, $query )
160 + {
161 + if ( ( is_array($query->get('post_type')) && in_array('property', $query->get('post_type')) ) || ( !is_array($query->get('post_type')) && $query->get('post_type') == 'property' ) )
162 + {
163 + global $wpdb;
164 +
165 + // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- The public keyword filter reads the request, sanitizes/SQL-escapes it, and contributes only to the current SQL WHERE clause. This exact annotation covers only NonceVerification.Recommended; retain all sanitizer and SQL-safety checks.
166 + if ( isset($_REQUEST['keyword']) && is_string( $_REQUEST['keyword'] ) && $_REQUEST['keyword'] != '' )
167 + {
168 + // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only search; reuse the already-unslashed value produced by keyword_meta_query when available.
169 + $keyword = isset( self::$normalized_keyword ) && $_REQUEST['keyword'] === self::$normalized_keyword ? self::$normalized_keyword : sanitize_text_field( wp_unslash( $_REQUEST['keyword'] ) );
170 + $ref_pos = strpos($where, '_features_concatenated');
171 + if ( $ref_pos !== FALSE )
172 + {
173 + // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- The public keyword filter reads the request, sanitizes/SQL-escapes it, and contributes only to the current SQL WHERE clause. This exact annotation covers only NonceVerification.Recommended; retain all sanitizer and SQL-safety checks.
174 + $str_to_insert = " $wpdb->posts.post_excerpt LIKE '%" . esc_sql( $keyword ) . "%' OR ";
175 + $where = substr_replace($where, $str_to_insert, $ref_pos - 18, 0);
176 + }
177 + }
178 + }
179 +
180 + return $where;
181 + }
182 +
183 + public function commercial_display_where( $where, $query )
184 + {
185 + if ( $query->get('post_type') == 'property' )
186 + {
187 + global $wpdb;
188 +
189 + $commercial_display = get_option( 'propertyhive_commercial_display', '' );
190 +
191 + switch ( $commercial_display )
192 + {
193 + case "top_level_only":
194 + {
195 + $where .= " AND $wpdb->posts.post_parent=0 ";
196 + break;
197 + }
198 + case "top_level_only_but_units_when_filtered":
199 + {
200 + $unit_filter_parameters = apply_filters( 'propertyhive_unit_filter_parameters', array( 'minimum_floor_area', 'maximum_floor_area' ) );
201 + $unit_filter_parameter_found = false;
202 + foreach ( $unit_filter_parameters as $parameter )
203 + {
204 + // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- The public commercial display filter reads a request flag and contributes only to the current SQL WHERE clause. This exact annotation covers only NonceVerification.Recommended; retain all sanitizer and SQL-safety checks.
205 + if ( isset($_REQUEST[$parameter]) && ph_clean( wp_unslash( $_REQUEST[$parameter] ) ) != '' )
206 + {
207 + $unit_filter_parameter_found = true;
208 + }
209 + }
210 + if ( !$unit_filter_parameter_found )
211 + {
212 + $where .= " AND $wpdb->posts.post_parent=0 ";
213 + }
214 + break;
215 + }
216 + default:
217 + {
218 + // do nothing
219 + }
220 + }
221 + }
222 +
223 + return $where;
224 + }
225 +
69 226 /**
70 227 * Init query vars by loading options.
71 228 */
72 229 public function init_query_vars() {
@@ -79,21 +236,14 @@
79 236 /**
80 237 * Get any errors from querystring
81 238 */
82 239 public function get_errors() {
83 - if ( ! empty( $_GET['ph_error'] ) && ( $error = sanitize_text_field( $_GET['ph_error'] ) ) && ! ph_has_notice( $error, 'error' ) )
240 + // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- The public frontend reads ph_error to add a request-scoped notice; it does not write posts, options, user data, or other persistent state. This exact annotation covers only NonceVerification.Recommended; retain all sanitizer and SQL-safety checks.
241 + if ( ! empty( $_GET['ph_error'] ) && ( $error = sanitize_text_field( wp_unslash( $_GET['ph_error'] ) ) ) && ! ph_has_notice( $error, 'error' ) )
84 242 ph_add_notice( $error, 'error' );
85 243 }
86 244
87 245 /**
88 - * Add endpoints for query vars
89 - */
90 - public function add_endpoints() {
91 - foreach ( $this->query_vars as $key => $var )
92 - add_rewrite_endpoint( $var, EP_PAGES );
93 - }
94 -
95 - /**
96 246 * add_query_vars function.
97 247 *
98 248 * @access public
99 249 * @param array $vars
@@ -121,10 +271,12 @@
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 ] ) ) {
126 - $wp->query_vars[ $key ] = $_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.
278 + $wp->query_vars[ $key ] = sanitize_text_field( wp_unslash( $_GET[ $var ] ) );
127 279 }
128 280
129 281 elseif ( isset( $wp->query_vars[ $var ] ) ) {
130 282 $wp->query_vars[ $key ] = $wp->query_vars[ $var ];
@@ -157,20 +309,23 @@
157 309 $q->is_page = false;
158 310 }
159 311
160 312 // When orderby is set, WordPress shows posts. Get around that here.
161 - /*if ( $q->is_home() && 'page' == get_option('show_on_front') && get_option('page_on_front') == ph_get_page_id('search_results') ) {
313 + if ( ($q->is_home() || $q->get( 'page_id' ) == get_option('page_on_front')) && 'page' == get_option('show_on_front') && get_option('page_on_front') == ph_get_page_id('search_results') )
314 + {
162 315 $_query = wp_parse_args( $q->query );
163 - if ( empty( $_query ) || ! array_diff( array_keys( $_query ), array( 'preview', 'page', 'paged', 'cpage', 'orderby' ) ) ) {
316 + if ( empty( $_query ) || ! array_diff( array_keys( $_query ), array( 'preview', 'page', 'paged', 'cpage', 'orderby' ) ) )
317 + {
318 +
164 319 $q->is_page = true;
165 320 $q->is_home = false;
166 - $q->set( 'page_id', get_option('page_on_front') );
321 + $q->set( 'page_id', (int) get_option('page_on_front') );
167 322 $q->set( 'post_type', 'property' );
168 323 }
169 - }*/
324 + }
170 325
171 326 // Special check for sites with the property search results on front page
172 - /*if ( $q->is_page() && 'page' == get_option( 'show_on_front' ) && $q->get('page_id') == ph_get_page_id('search_results') ) {
327 + if ( $q->is_page() && 'page' == get_option( 'show_on_front' ) && $q->get('page_id') == ph_get_page_id('search_results') ) {
173 328
174 329 // This is a front-page property listings
175 330 $q->set( 'post_type', 'property' );
176 331 $q->set( 'page_id', '' );
@@ -176,9 +331,10 @@
176 331 $q->set( 'page_id', '' );
177 332 if ( isset( $q->query['paged'] ) )
178 333 $q->set( 'paged', $q->query['paged'] );
179 334
180 - // Define a variable so we know this is the front page shop later on
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.
181 337 define( 'SEARCH_RESULTS_IS_ON_FRONT', true );
182 338
183 339 // Get the actual WP page to avoid errors and let us use is_front_page()
184 340 // This is hacky but works. Awaiting http://core.trac.wordpress.org/ticket/21096
@@ -203,15 +359,15 @@
203 359 add_filter( 'wpseo_metadesc', array( $this, 'wpseo_metadesc' ) );
204 360 add_filter( 'wpseo_metakey', array( $this, 'wpseo_metakey' ) );
205 361 }
206 362
207 - } else {*/
363 + } else {
208 364
209 365 // Only apply to property categories, the property post archive, the search results page, and property attribute taxonomies
210 366 if ( ! $q->is_post_type_archive( 'property' ) && ! $q->is_tax( get_object_taxonomies( 'property' ) ) )
211 367 return;
212 368
213 - //}
369 + }
214 370
215 371 $this->property_query( $q );
216 372
217 373 if ( is_search() )
@@ -222,9 +378,9 @@
222 378
223 379 add_filter( 'posts_where', array( $this, 'exclude_protected_properties' ) );
224 380
225 381 // We're on a property search page so queue the propertyhive_get_properties_in_view function
226 - add_action( 'wp', array( $this, 'get_properties_in_view' ), 2);
382 + //add_action( 'wp', array( $this, 'get_properties_in_view' ), 2);
227 383
228 384 // And remove the pre_get_posts hook
229 385 $this->remove_property_query();
230 386 }
@@ -368,9 +524,9 @@
368 524 $post__in = array();
369 525 //$post__in = array_unique( apply_filters( 'loop_shop_post_in', array() ) );
370 526
371 527 // Ordering query vars
372 - $q->set( 'orderby', $ordering['orderby'] );
528 + $q->set( 'orderby', $ordering['orderby'] . ' post_title' );
373 529 $q->set( 'order', $ordering['order'] );
374 530 if ( isset( $ordering['meta_key'] ) )
375 531 $q->set( 'meta_key', $ordering['meta_key'] );
376 532
@@ -378,8 +534,9 @@
378 534 $q->set( 'meta_query', $meta_query );
379 535 $q->set( 'tax_query', $tax_query );
380 536 $q->set( 'date_query', $date_query );
381 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.
382 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' ) ) );
383 540
384 541 // Set a special variable
385 542 $q->set( 'ph_query', true );
@@ -451,8 +608,9 @@
451 608 array(
452 609 'post_type' => 'property',
453 610 'numberposts' => -1,
454 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.
455 613 'meta_query' => $this->meta_query,
456 614 'fields' => 'ids',
457 615 'no_found_rows' => true,
458 616 'update_post_meta_cache' => false,
@@ -487,11 +645,14 @@
487 645 * @access public
488 646 * @return array
489 647 */
490 648 public function get_search_results_ordering_args( $orderby = '', $order = '' ) {
649 + $request_department = $this->get_requested_department();
650 +
491 651 // Get ordering from query string unless defined
492 652 if ( ! $orderby ) {
493 - $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' ) );
494 655
495 656 // Get order + orderby args from string
496 657 $orderby_value = explode( '-', $orderby_value );
497 658 $orderby = esc_attr( $orderby_value[0] );
@@ -504,44 +665,72 @@
504 665 $args = array();
505 666
506 667 // default - menu_order
507 668 if (
508 - ( isset($_REQUEST['department']) && $_REQUEST['department'] != 'commercial' ) ||
509 - ( !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' )
510 673 )
511 674 {
512 675 $args['orderby'] = 'meta_value_num';
513 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.
514 678 $args['meta_key'] = '_price_actual';
515 679 }
516 680 elseif (
517 - ( isset($_REQUEST['department']) && $_REQUEST['department'] == 'commercial' ) ||
518 - ( !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' ) )
519 685 )
520 686 {
521 687 $args['orderby'] = 'meta_value_num';
522 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.
523 690 $args['meta_key'] = '_floor_area_from_sqft';
524 691 }
525 692
526 693 switch ( $orderby ) {
527 - case 'rand' :
528 - $args['orderby'] = 'rand';
529 - break;
530 - case 'date' :
531 - $args['orderby'] = 'date';
532 - $args['order'] = $order == 'ASC' ? 'ASC' : 'DESC';
533 - break;
534 694 case 'price' :
535 695 $args['orderby'] = 'meta_value_num';
536 696 $args['order'] = $order == 'ASC' ? 'ASC' : 'DESC';
537 - $args['meta_key'] = '_price_actual';
697 + if (
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' ) )
702 + )
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.
705 + $args['meta_key'] = '_price_from_actual';
706 + }
707 + else
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.
710 + $args['meta_key'] = '_price_actual';
711 + }
538 712 break;
539 713 case 'floor_area' :
540 714 $args['orderby'] = 'meta_value_num';
541 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.
542 717 $args['meta_key'] = '_floor_area_from_sqft';
543 718 break;
719 + case 'date' :
720 + $args['orderby'] = 'meta_value';
721 + $args['order'] = $order == 'ASC' ? 'ASC' : 'DESC';
722 + // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_key -- Search ordering maps supported price/floor-area/date choices to literal stored meta keys. switch cases and department branches set fixed keys; order direction is constrained to ASC/DESC.
723 + $args['meta_key'] = '_on_market_change_date';
724 + break;
725 + default :
726 + {
727 + if ( $orderby != '' )
728 + {
729 + $args['orderby'] = $orderby;
730 + $args['order'] = $order == 'ASC' ? 'ASC' : 'DESC';
731 + }
732 + }
544 733 }
545 734
546 735 return apply_filters( 'propertyhive_get_search_results_ordering_args', $args );
547 736 }
@@ -555,13 +744,25 @@
555 744 public function get_date_query() {
556 745
557 746 $date_query = array();
558 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.
749 + if ( isset( $_REQUEST['added_from'] ) && $_REQUEST['added_from'] != '' )
750 + {
751 + $date_query = array(
752 + 'column' => 'post_date_gmt',
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'] ) )
755 + );
756 + }
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.
559 759 if ( isset( $_REQUEST['added_from_hours'] ) && $_REQUEST['added_from_hours'] != '' )
560 760 {
561 761 $date_query = array(
562 762 'column' => 'post_date_gmt',
563 - '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'
564 765 );
565 766 }
566 767
567 768 return array_filter( $date_query );
@@ -585,8 +786,10 @@
585 786 $meta_query = array();
586 787
587 788 $meta_query[] = $this->on_market_meta_query();
588 789 $meta_query[] = $this->department_meta_query($q);
790 + $meta_query[] = $this->featured_meta_query();
791 + $meta_query[] = $this->date_added_meta_query();
589 792 $meta_query[] = $this->address_keyword_meta_query();
590 793 $meta_query[] = $this->country_meta_query();
591 794 $meta_query[] = $this->minimum_price_meta_query();
592 795 $meta_query[] = $this->maximum_price_meta_query();
@@ -603,11 +806,21 @@
603 806 $meta_query[] = $this->maximum_reception_rooms_meta_query();
604 807 $meta_query[] = $this->available_date_from_meta_query();
605 808 $meta_query[] = $this->minimum_floor_area_meta_query();
606 809 $meta_query[] = $this->maximum_floor_area_meta_query();
810 + $meta_query[] = $this->minimum_maximum_floor_area_meta_query();
607 811 $meta_query[] = $this->floor_area_range_meta_query();
812 + $meta_query[] = $this->commercial_for_sale_to_rent_meta_query();
813 + $meta_query[] = $this->commercial_for_sale_meta_query();
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();
819 + $meta_query[] = $this->negotiator_meta_query();
608 820 $meta_query[] = $this->office_meta_query();
609 -
821 + $meta_query[] = $this->keyword_meta_query();
822 +
610 823 return array_filter( apply_filters( 'propertyhive_property_query_meta_query', $meta_query, $this ) );
611 824 }
612 825
613 826 /**
@@ -618,8 +831,10 @@
618 831 * @return array
619 832 */
620 833 public function on_market_meta_query( ) {
621 834
835 + $meta_query = array();
836 +
622 837 if ( !is_admin() )
623 838 {
624 839 $meta_query = array(
625 840 'key' => '_on_market',
@@ -637,16 +852,18 @@
637 852 * @access public
638 853 * @return array
639 854 */
640 855 public function department_meta_query( $q ) {
856 + $request_department = $this->get_requested_department();
857 +
641 858
642 859 $meta_query = array();
643 860
644 - if ( isset( $_REQUEST['department'] ) && $_REQUEST['department'] != '' )
861 + if ( isset( $request_department ) && $request_department != '' )
645 862 {
646 863 $meta_query = array(
647 864 'key' => '_department',
648 - 'value' => sanitize_text_field( $_REQUEST['department'] ),
865 + 'value' => sanitize_text_field( $request_department ),
649 866 'compare' => '='
650 867 );
651 868 }
652 869 else
@@ -659,23 +876,23 @@
659 876 $department = get_option( 'propertyhive_primary_department' );
660 877 }
661 878 else
662 879 {
663 - $departments = array();
664 - if ( get_option( 'propertyhive_active_departments_sales' ) == 'yes' )
880 + // No primary department set. Use first active one. Should never get to this scenario
881 + $department = '';
882 +
883 + $departments = ph_get_departments();
884 +
885 + foreach ( $departments as $key => $value )
665 886 {
666 - $departments[] = 'residential-sales';
887 + if ( get_option( 'propertyhive_active_departments_' . str_replace("residential-", "", $key) ) == 'yes' )
888 + {
889 + if ( $department == '' )
890 + {
891 + $department = $key;
892 + }
893 + }
667 894 }
668 - if ( get_option( 'propertyhive_active_departments_lettings' ) == 'yes' )
669 - {
670 - $departments[] = 'residential-lettings';
671 - }
672 - if ( get_option( 'propertyhive_active_departments_commercial' ) == 'yes' )
673 - {
674 - $departments[] = 'commercial';
675 - }
676 -
677 - $department = $departments[0];
678 895 }
679 896 $meta_query = array(
680 897 'key' => '_department',
681 898 'value' => $department,
@@ -687,89 +904,314 @@
687 904 return $meta_query;
688 905 }
689 906
690 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 + /**
691 956 * Returns a meta query to handle searching for a keyword in the address
692 957 *
693 958 * @access public
694 - * @param string $compare (default: 'IN')
695 959 * @return array
696 960 */
697 961 public function address_keyword_meta_query( ) {
698 962
699 963 $meta_query = array();
700 -
701 - 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']) )
702 967 {
703 - $_REQUEST['address_keyword'] = sanitize_text_field( trim( $_REQUEST['address_keyword'] ) );
704 968
705 - $address_keywords = array( $_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;
706 982
707 - if ( strpos( $_REQUEST['address_keyword'], ' ' ) !== FALSE )
983 + $do_address_search = true;
984 + if ( is_string( $address_input ) && get_option( 'propertyhive_address_keyword_compare', '=' ) == 'polygon' )
708 985 {
709 - $address_keywords[] = str_replace(" ", "-", $_REQUEST['address_keyword']);
986 + $address_keyword_polygon = new PH_Address_Keyword_Polygon();
987 +
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 + }
710 996 }
711 - if ( strpos( $_REQUEST['address_keyword'], '-' ) !== FALSE )
997 +
998 + if ( $do_address_search )
712 999 {
713 - $address_keywords[] = str_replace("-", " ", $_REQUEST['address_keyword']);
714 - }
715 1000
716 - $meta_query = array('relation' => 'OR');
1001 + $address_keywords_to_query = is_array($address_input) ? $address_input : array( $address_input );
717 1002
718 - foreach ( $address_keywords as $address_keyword )
719 - {
720 - $meta_query[] = array(
721 - 'key' => '_reference_number',
722 - 'value' => $address_keyword,
723 - 'compare' => get_option( 'propertyhive_address_keyword_compare', '=' )
724 - );
725 - $meta_query[] = array(
726 - 'key' => '_address_street',
727 - 'value' => $address_keyword,
728 - 'compare' => get_option( 'propertyhive_address_keyword_compare', '=' )
729 - );
730 - $meta_query[] = array(
731 - 'key' => '_address_two',
732 - 'value' => $address_keyword,
733 - 'compare' => get_option( 'propertyhive_address_keyword_compare', '=' )
734 - );
735 - $meta_query[] = array(
736 - 'key' => '_address_three',
737 - 'value' => $address_keyword,
738 - 'compare' => get_option( 'propertyhive_address_keyword_compare', '=' )
739 - );
740 - $meta_query[] = array(
741 - 'key' => '_address_four',
742 - 'value' => $address_keyword,
743 - 'compare' => get_option( 'propertyhive_address_keyword_compare', '=' )
744 - );
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 + );
1012 +
1013 + $address_keywords = array();
1014 +
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);
1021 +
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 )
1064 + {
1065 + $address_fields_to_query[] = '_address_country';
1066 + }
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 )
1072 + {
1073 + foreach ( $address_fields_to_query as $address_field )
1074 + {
1075 + if ( in_array( $address_field, array('_address_postcode', '_address_country', '_address_concatenated') ) ) { continue; } // ignore postcode and country as they're handled differently afterwards
1076 +
1077 + $meta_query[] = array(
1078 + 'key' => $address_field,
1079 + 'value' => $address_keyword,
1080 + 'compare' => get_option( 'propertyhive_address_keyword_compare', '=' )
1081 + );
1082 + }
1083 +
1084 + if ( in_array('_address_postcode', $address_fields_to_query) )
1085 + {
1086 + if ( strlen($address_keyword) <= 4 )
1087 + {
1088 + $meta_query[] = array(
1089 + 'key' => '_address_postcode',
1090 + 'value' => ph_clean($address_keyword),
1091 + 'compare' => '='
1092 + );
1093 + // Run regex match where given keyword is at the start of the postcode ^
1094 + // followed by one or zero letters (for WC2E-style postcodes) [a-zA-Z]?
1095 + // then a single space [ ]
1096 + $meta_query[] = array(
1097 + 'key' => '_address_postcode',
1098 + 'value' => '^' . ph_clean($address_keyword) . '[a-zA-Z]?[ ]',
1099 + 'compare' => 'RLIKE'
1100 + );
1101 + }
1102 + else
1103 + {
1104 + $postcode = ph_clean($address_keyword);
1105 +
1106 + if ( preg_match('#^(GIR ?0AA|[A-PR-UWYZ]([0-9]{1,2}|([A-HK-Y][0-9]([0-9ABEHMNPRV-Y])?)|[0-9][A-HJKPS-UW])[0-9][ABD-HJLNP-UW-Z]{2})$#i', $postcode) )
1107 + {
1108 + // UK postcode found with no space
1109 +
1110 + if ( strlen($postcode) == 5 )
1111 + {
1112 + $first_part = substr($postcode, 0, 2);
1113 + $last_part = substr($postcode, 2, 3);
1114 +
1115 + $postcode = $first_part . ' ' . $last_part;
1116 + }
1117 + elseif ( strlen($postcode) == 6 )
1118 + {
1119 + $first_part = substr($postcode, 0, 3);
1120 + $last_part = substr($postcode, 3, 3);
1121 +
1122 + $postcode = $first_part . ' ' . $last_part;
1123 + }
1124 + elseif ( strlen($postcode) == 7 )
1125 + {
1126 + $first_part = substr($postcode, 0, 4);
1127 + $last_part = substr($postcode, 4, 3);
1128 +
1129 + $postcode = $first_part . ' ' . $last_part;
1130 + }
1131 + }
1132 +
1133 + $meta_query[] = array(
1134 + 'key' => '_address_postcode',
1135 + 'value' => ph_clean( $postcode ),
1136 + 'compare' => 'LIKE'
1137 + );
1138 + }
1139 + }
1140 +
1141 + if ( in_array('_address_country', $address_fields_to_query) )
1142 + {
1143 + $meta_query[] = array(
1144 + 'key' => '_address_country',
1145 + 'value' => $address_keyword,
1146 + 'compare' => '='
1147 + );
1148 +
1149 + // get country code for country entered
1150 + $PH_Countries = new PH_Countries();
1151 + $countries = $PH_Countries->countries;
1152 + if ( is_array($countries) && !empty($countries) )
1153 + {
1154 + foreach ( $countries as $country_code => $country )
1155 + {
1156 + if ( strtolower($address_keyword) == strtolower($country['name']) )
1157 + {
1158 + $meta_query[] = array(
1159 + 'key' => '_address_country',
1160 + 'value' => $country_code,
1161 + 'compare' => '='
1162 + );
1163 + break;
1164 + }
1165 + }
1166 + }
1167 + }
1168 +
1169 + if (
1170 + !preg_match('/^(?:[A-Z]{2}\d|[A-Z]\d)/i', $address_keyword) &&
1171 + in_array('_address_concatenated', $address_fields_to_query)
1172 + )
1173 + {
1174 + $meta_query[] = array(
1175 + 'key' => '_address_concatenated',
1176 + 'value' => $address_keyword,
1177 + 'compare' => 'LIKE'
1178 + );
1179 + }
1180 + }
1181 +
745 1182 }
746 - if ( strlen($_REQUEST['address_keyword']) <= 4 )
747 - {
748 - $meta_query[] = array(
749 - 'key' => '_address_postcode',
750 - 'value' => sanitize_text_field( $_REQUEST['address_keyword'] ),
751 - 'compare' => '='
752 - );
753 - $meta_query[] = array(
754 - 'key' => '_address_postcode',
755 - 'value' => sanitize_text_field( $_REQUEST['address_keyword'] ) . '[ ]',
756 - 'compare' => 'RLIKE'
757 - );
758 - }
759 - else
760 - {
761 - $meta_query[] = array(
762 - 'key' => '_address_postcode',
763 - 'value' => sanitize_text_field( $_REQUEST['address_keyword'] ),
764 - 'compare' => 'LIKE'
765 - );
766 - }
767 1183 }
768 1184
769 1185 return $meta_query;
770 1186 }
771 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 +
772 1214 /**
773 1215 * Returns a meta query to handle country
774 1216 *
775 1217 * @access public
@@ -778,15 +1220,28 @@
778 1220 public function country_meta_query( ) {
779 1221
780 1222 $meta_query = array();
781 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.
782 1225 if ( isset( $_REQUEST['country'] ) && $_REQUEST['country'] != '' )
783 1226 {
784 1227 $meta_query = array(
785 1228 'key' => '_address_country',
786 - 'value' => sanitize_text_field( $_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'] ) )
787 1231 );
788 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 + }
789 1244
790 1245 return $meta_query;
791 1246 }
792 1247
@@ -796,19 +1251,41 @@
796 1251 * @access public
797 1252 * @return array
798 1253 */
799 1254 public function minimum_price_meta_query( ) {
1255 + $request_department = $this->get_requested_department();
1256 +
800 1257
801 1258 $meta_query = array();
802 1259
803 1260 if (
804 - 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.
805 1263 isset( $_REQUEST['minimum_price'] ) && $_REQUEST['minimum_price'] != ''
806 1264 )
807 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 +
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 );
1276 +
1277 + if ( $search_form_currency != 'GBP' )
1278 + {
1279 + // Convert $_REQUEST['minimum_price'] to GBP
1280 + $ph_countries = new PH_Countries();
1281 +
1282 + $minimum_price = $ph_countries->convert_price_to_gbp( $minimum_price, $search_form_currency );
1283 + }
1284 +
808 1285 $meta_query = array(
809 1286 'key' => '_price_actual',
810 - 'value' => sanitize_text_field( $_REQUEST['minimum_price'] ),
1287 + 'value' => ph_clean( floor( $minimum_price ) ),
811 1288 'compare' => '>=',
812 1289 'type' => 'NUMERIC'
813 1290 );
814 1291 }
@@ -822,19 +1299,41 @@
822 1299 * @access public
823 1300 * @return array
824 1301 */
825 1302 public function maximum_price_meta_query( ) {
1303 + $request_department = $this->get_requested_department();
1304 +
826 1305
827 1306 $meta_query = array();
828 1307
829 1308 if (
830 - 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.
831 1311 isset( $_REQUEST['maximum_price'] ) && $_REQUEST['maximum_price'] != ''
832 1312 )
833 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 +
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 );
1324 +
1325 + if ( $search_form_currency != 'GBP' )
1326 + {
1327 + // Convert $_REQUEST['maximum_price'] to GBP
1328 + $ph_countries = new PH_Countries();
1329 +
1330 + $maximum_price = $ph_countries->convert_price_to_gbp( $maximum_price, $search_form_currency );
1331 + }
1332 +
834 1333 $meta_query = array(
835 1334 'key' => '_price_actual',
836 - 'value' => sanitize_text_field( $_REQUEST['maximum_price'] ),
1335 + 'value' => ph_clean( ceil( $maximum_price ) ),
837 1336 'compare' => '<=',
838 1337 'type' => 'NUMERIC'
839 1338 );
840 1339 }
@@ -848,23 +1347,45 @@
848 1347 * @access public
849 1348 * @return array
850 1349 */
851 1350 public function price_range_meta_query( ) {
1351 + $request_department = $this->get_requested_department();
1352 +
852 1353
853 1354 $meta_query = array();
854 1355
855 1356 if (
856 - isset( $_REQUEST['department'] ) && $_REQUEST['department'] == 'residential-sales' &&
857 - 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'] != ''
858 1360 )
859 1361 {
860 - $explode_price_range = explode("-", $_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'] ) ));
861 1364
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 );
1367 +
862 1368 if ( isset($explode_price_range[0]) && $explode_price_range[0] != '' )
863 1369 {
864 - $meta_query = array(
1370 + $minimum_price = $explode_price_range[0];
1371 +
1372 + if ( !is_numeric($minimum_price) )
1373 + {
1374 + return $meta_query;
1375 + }
1376 +
1377 + if ( $search_form_currency != 'GBP' )
1378 + {
1379 + // Convert $explode_price_range[0] to GBP
1380 + $ph_countries = new PH_Countries();
1381 +
1382 + $minimum_price = $ph_countries->convert_price_to_gbp( $minimum_price, $search_form_currency );
1383 + }
1384 +
1385 + $meta_query[] = array(
865 1386 'key' => '_price_actual',
866 - 'value' => sanitize_text_field( $explode_price_range[0] ),
1387 + 'value' => sanitize_text_field( floor( $minimum_price ) ),
867 1388 'compare' => '>=',
868 1389 'type' => 'NUMERIC'
869 1390 );
870 1391 }
@@ -869,11 +1390,26 @@
869 1390 );
870 1391 }
871 1392 if ( isset($explode_price_range[1]) && $explode_price_range[1] != '' )
872 1393 {
873 - $meta_query = array(
1394 + $maximum_price = $explode_price_range[1];
1395 +
1396 + if ( !is_numeric($maximum_price) )
1397 + {
1398 + return $meta_query;
1399 + }
1400 +
1401 + if ( $search_form_currency != 'GBP' )
1402 + {
1403 + // Convert $explode_price_range[1] to GBP
1404 + $ph_countries = new PH_Countries();
1405 +
1406 + $maximum_price = $ph_countries->convert_price_to_gbp( $maximum_price, $search_form_currency );
1407 + }
1408 +
1409 + $meta_query[] = array(
874 1410 'key' => '_price_actual',
875 - 'value' => sanitize_text_field( $explode_price_range[1] ),
1411 + 'value' => sanitize_text_field( ceil( $maximum_price ) ),
876 1412 'compare' => '<=',
877 1413 'type' => 'NUMERIC'
878 1414 );
879 1415 }
@@ -888,19 +1424,50 @@
888 1424 * @access public
889 1425 * @return array
890 1426 */
891 1427 public function minimum_rent_meta_query( ) {
1428 + $request_department = $this->get_requested_department();
1429 +
892 1430
893 1431 $meta_query = array();
894 1432
895 1433 if (
896 - 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.
897 1436 isset( $_REQUEST['minimum_rent'] ) && $_REQUEST['minimum_rent'] != ''
898 1437 )
899 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 +
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 );
1449 +
1450 + if ( $search_form_currency != 'GBP' )
1451 + {
1452 + // Convert $_REQUEST['minimum_rent'] to GBP
1453 + $ph_countries = new PH_Countries();
1454 +
1455 + $minimum_rent = $ph_countries->convert_price_to_gbp( $minimum_rent, $search_form_currency );
1456 + }
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 +
900 1467 $meta_query = array(
901 1468 'key' => '_price_actual',
902 - 'value' => sanitize_text_field( $_REQUEST['minimum_rent'] ),
1469 + 'value' => ph_clean( floor( $minimum_rent ) ),
903 1470 'compare' => '>=',
904 1471 'type' => 'NUMERIC'
905 1472 );
906 1473 }
@@ -914,19 +1481,50 @@
914 1481 * @access public
915 1482 * @return array
916 1483 */
917 1484 public function maximum_rent_meta_query( ) {
1485 + $request_department = $this->get_requested_department();
1486 +
918 1487
919 1488 $meta_query = array();
920 1489
921 1490 if (
922 - 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.
923 1493 isset( $_REQUEST['maximum_rent'] ) && $_REQUEST['maximum_rent'] != ''
924 1494 )
925 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 +
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 );
1506 +
1507 + if ( $search_form_currency != 'GBP' )
1508 + {
1509 + // Convert $_REQUEST['maximum_rent'] to GBP
1510 + $ph_countries = new PH_Countries();
1511 +
1512 + $maximum_rent = $ph_countries->convert_price_to_gbp( $maximum_rent, $search_form_currency );
1513 + }
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 +
926 1524 $meta_query = array(
927 1525 'key' => '_price_actual',
928 - 'value' => sanitize_text_field( $_REQUEST['maximum_rent'] ),
1526 + 'value' => ph_clean( ceil( $maximum_rent ) ),
929 1527 'compare' => '<=',
930 1528 'type' => 'NUMERIC'
931 1529 );
932 1530 }
@@ -940,23 +1538,55 @@
940 1538 * @access public
941 1539 * @return array
942 1540 */
943 1541 public function rent_range_meta_query( ) {
1542 + $request_department = $this->get_requested_department();
1543 +
944 1544
945 1545 $meta_query = array();
946 1546
947 1547 if (
948 - isset( $_REQUEST['department'] ) && $_REQUEST['department'] == 'residential-lettings' &&
949 - 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'] != ''
950 1551 )
951 1552 {
952 - $explode_rent_range = explode("-", $_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'] ) ));
953 1555
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 );
1558 +
1559 + $rent_frequency = apply_filters( 'propertyhive_search_form_rent_frequency', 'pcm' );
1560 +
954 1561 if ( isset($explode_rent_range[0]) && $explode_rent_range[0] != '' )
955 1562 {
956 - $meta_query = array(
1563 + $minimum_rent = $explode_rent_range[0];
1564 +
1565 + if ( !is_numeric($minimum_rent) )
1566 + {
1567 + return $meta_query;
1568 + }
1569 +
1570 + if ( $search_form_currency != 'GBP' )
1571 + {
1572 + // Convert $explode_rent_range[0] to GBP
1573 + $ph_countries = new PH_Countries();
1574 +
1575 + $minimum_rent = $ph_countries->convert_price_to_gbp( $minimum_rent, $search_form_currency );
1576 + }
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 +
1586 + $meta_query[] = array(
957 1587 'key' => '_price_actual',
958 - 'value' => sanitize_text_field( $explode_rent_range[0] ),
1588 + 'value' => sanitize_text_field( floor( $minimum_rent ) ),
959 1589 'compare' => '>=',
960 1590 'type' => 'NUMERIC'
961 1591 );
962 1592 }
@@ -961,11 +1591,34 @@
961 1591 );
962 1592 }
963 1593 if ( isset($explode_rent_range[1]) && $explode_rent_range[1] != '' )
964 1594 {
965 - $meta_query = array(
1595 + $maximum_rent = $explode_rent_range[1];
1596 +
1597 + if ( !is_numeric($maximum_rent) )
1598 + {
1599 + return $meta_query;
1600 + }
1601 +
1602 + if ( $search_form_currency != 'GBP' )
1603 + {
1604 + // Convert $explode_rent_range[1] to GBP
1605 + $ph_countries = new PH_Countries();
1606 +
1607 + $maximum_rent = $ph_countries->convert_price_to_gbp( $maximum_rent, $search_form_currency );
1608 + }
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 +
1618 + $meta_query[] = array(
966 1619 'key' => '_price_actual',
967 - 'value' => sanitize_text_field( $explode_rent_range[1] ),
1620 + 'value' => sanitize_text_field( ceil( $maximum_rent ) ),
968 1621 'compare' => '<=',
969 1622 'type' => 'NUMERIC'
970 1623 );
971 1624 }
@@ -980,22 +1633,26 @@
980 1633 * @access public
981 1634 * @return array
982 1635 */
983 1636 public function bedrooms_meta_query( ) {
1637 + $request_department = $this->get_requested_department();
1638 +
984 1639
985 1640 $meta_query = array();
986 1641
987 1642 if (
988 1643 (
989 - (isset( $_REQUEST['department'] ) && $_REQUEST['department'] == 'residential-sales') ||
990 - (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' ))
991 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.
992 1648 isset( $_REQUEST['bedrooms'] ) && $_REQUEST['bedrooms'] != ''
993 1649 )
994 1650 {
995 1651 $meta_query = array(
996 1652 'key' => '_bedrooms',
997 - 'value' => sanitize_text_field( $_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'] ) ),
998 1655 'compare' => '=',
999 1656 'type' => 'NUMERIC'
1000 1657 );
1001 1658 }
@@ -1009,22 +1666,26 @@
1009 1666 * @access public
1010 1667 * @return array
1011 1668 */
1012 1669 public function minimum_bedrooms_meta_query( ) {
1670 + $request_department = $this->get_requested_department();
1671 +
1013 1672
1014 1673 $meta_query = array();
1015 1674
1016 1675 if (
1017 1676 (
1018 - (isset( $_REQUEST['department'] ) && $_REQUEST['department'] == 'residential-sales') ||
1019 - (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' ))
1020 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.
1021 1681 isset( $_REQUEST['minimum_bedrooms'] ) && $_REQUEST['minimum_bedrooms'] != ''
1022 1682 )
1023 1683 {
1024 1684 $meta_query = array(
1025 1685 'key' => '_bedrooms',
1026 - 'value' => sanitize_text_field( $_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'] ) ),
1027 1688 'compare' => '>=',
1028 1689 'type' => 'NUMERIC'
1029 1690 );
1030 1691 }
@@ -1038,22 +1699,26 @@
1038 1699 * @access public
1039 1700 * @return array
1040 1701 */
1041 1702 public function maximum_bedrooms_meta_query( ) {
1703 + $request_department = $this->get_requested_department();
1704 +
1042 1705
1043 1706 $meta_query = array();
1044 1707
1045 1708 if (
1046 1709 (
1047 - (isset( $_REQUEST['department'] ) && $_REQUEST['department'] == 'residential-sales') ||
1048 - (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' ))
1049 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.
1050 1714 isset( $_REQUEST['maximum_bedrooms'] ) && $_REQUEST['maximum_bedrooms'] != ''
1051 1715 )
1052 1716 {
1053 1717 $meta_query = array(
1054 1718 'key' => '_bedrooms',
1055 - 'value' => sanitize_text_field( $_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'] ) ),
1056 1721 'compare' => '<=',
1057 1722 'type' => 'NUMERIC'
1058 1723 );
1059 1724 }
@@ -1067,22 +1732,26 @@
1067 1732 * @access public
1068 1733 * @return array
1069 1734 */
1070 1735 public function minimum_bathrooms_meta_query( ) {
1736 + $request_department = $this->get_requested_department();
1737 +
1071 1738
1072 1739 $meta_query = array();
1073 1740
1074 1741 if (
1075 1742 (
1076 - (isset( $_REQUEST['department'] ) && $_REQUEST['department'] == 'residential-sales') ||
1077 - (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' ))
1078 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.
1079 1747 isset( $_REQUEST['minimum_bathrooms'] ) && $_REQUEST['minimum_bathrooms'] != ''
1080 1748 )
1081 1749 {
1082 1750 $meta_query = array(
1083 1751 'key' => '_bathrooms',
1084 - 'value' => sanitize_text_field( $_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'] ) ),
1085 1754 'compare' => '>=',
1086 1755 'type' => 'NUMERIC'
1087 1756 );
1088 1757 }
@@ -1096,22 +1765,26 @@
1096 1765 * @access public
1097 1766 * @return array
1098 1767 */
1099 1768 public function maximum_bathrooms_meta_query( ) {
1769 + $request_department = $this->get_requested_department();
1770 +
1100 1771
1101 1772 $meta_query = array();
1102 1773
1103 1774 if (
1104 1775 (
1105 - (isset( $_REQUEST['department'] ) && $_REQUEST['department'] == 'residential-sales') ||
1106 - (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' ))
1107 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.
1108 1780 isset( $_REQUEST['maximum_bathrooms'] ) && $_REQUEST['maximum_bathrooms'] != ''
1109 1781 )
1110 1782 {
1111 1783 $meta_query = array(
1112 1784 'key' => '_bathrooms',
1113 - 'value' => sanitize_text_field( $_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'] ) ),
1114 1787 'compare' => '<=',
1115 1788 'type' => 'NUMERIC'
1116 1789 );
1117 1790 }
@@ -1125,22 +1798,26 @@
1125 1798 * @access public
1126 1799 * @return array
1127 1800 */
1128 1801 public function minimum_reception_rooms_meta_query( ) {
1802 + $request_department = $this->get_requested_department();
1803 +
1129 1804
1130 1805 $meta_query = array();
1131 1806
1132 1807 if (
1133 1808 (
1134 - (isset( $_REQUEST['department'] ) && $_REQUEST['department'] == 'residential-sales') ||
1135 - (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' ))
1136 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.
1137 1813 isset( $_REQUEST['minimum_reception_rooms'] ) && $_REQUEST['minimum_reception_rooms'] != ''
1138 1814 )
1139 1815 {
1140 1816 $meta_query = array(
1141 1817 'key' => '_reception_rooms',
1142 - 'value' => sanitize_text_field( $_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'] ) ),
1143 1820 'compare' => '>=',
1144 1821 'type' => 'NUMERIC'
1145 1822 );
1146 1823 }
@@ -1154,22 +1831,26 @@
1154 1831 * @access public
1155 1832 * @return array
1156 1833 */
1157 1834 public function maximum_reception_rooms_meta_query( ) {
1835 + $request_department = $this->get_requested_department();
1836 +
1158 1837
1159 1838 $meta_query = array();
1160 1839
1161 1840 if (
1162 1841 (
1163 - (isset( $_REQUEST['department'] ) && $_REQUEST['department'] == 'residential-sales') ||
1164 - (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' ))
1165 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.
1166 1846 isset( $_REQUEST['maximum_reception_rooms'] ) && $_REQUEST['maximum_reception_rooms'] != ''
1167 1847 )
1168 1848 {
1169 1849 $meta_query = array(
1170 1850 'key' => '_reception_rooms',
1171 - 'value' => sanitize_text_field( $_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'] ) ),
1172 1853 'compare' => '<=',
1173 1854 'type' => 'NUMERIC'
1174 1855 );
1175 1856 }
@@ -1183,17 +1864,21 @@
1183 1864 * @access public
1184 1865 * @return array
1185 1866 */
1186 1867 public function available_date_from_meta_query( ) {
1868 + $request_department = $this->get_requested_department();
1869 +
1187 1870
1188 1871 $meta_query = array();
1189 1872
1190 1873 if (
1191 - isset( $_REQUEST['department'] ) && $_REQUEST['department'] == 'residential-lettings' &&
1192 - 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'] != ''
1193 1877 )
1194 1878 {
1195 - $available_date = $_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'] ) );
1196 1881 if ( strpos($available_date, '/') !== FALSE )
1197 1882 {
1198 1883 // it's been provided in the format dd/mm/yyyy
1199 1884 $explode_available_date = explode("/", $available_date);
@@ -1203,9 +1888,9 @@
1203 1888 }
1204 1889 }
1205 1890 $meta_query = array(
1206 1891 'key' => '_available_date',
1207 - 'value' => sanitize_text_field( $available_date ),
1892 + 'value' => ph_clean( $available_date ),
1208 1893 'compare' => '<=',
1209 1894 );
1210 1895 }
1211 1896
@@ -1218,23 +1903,40 @@
1218 1903 * @access public
1219 1904 * @return array
1220 1905 */
1221 1906 public function minimum_floor_area_meta_query( ) {
1907 + $request_department = $this->get_requested_department();
1908 +
1222 1909
1223 1910 $meta_query = array();
1224 1911
1225 1912 if (
1226 - isset( $_REQUEST['department'] ) && $_REQUEST['department'] == 'commercial' &&
1227 - isset( $_REQUEST['minimum_floor_area'] ) && $_REQUEST['minimum_floor_area'] != ''
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.
1915 + isset( $_REQUEST['minimum_floor_area'] ) && $_REQUEST['minimum_floor_area'] != '' &&
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.
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.
1920 + ( isset( $_REQUEST['maximum_floor_area'] ) && $_REQUEST['maximum_floor_area'] == '' )
1921 + )
1228 1922 )
1229 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 +
1230 1932 $meta_query = array(
1231 - 'key' => '_floor_area_from_sqft',
1232 - 'value' => sanitize_text_field( $_REQUEST['minimum_floor_area'] ),
1933 + 'key' => '_floor_area_to_sqft',
1934 + 'value' => $value,
1233 1935 'compare' => '>=',
1234 1936 'type' => 'NUMERIC'
1235 - );
1236 - }
1937 + );
1938 + }
1237 1939
1238 1940 return $meta_query;
1239 1941 }
1240 1942
@@ -1244,20 +1946,85 @@
1244 1946 * @access public
1245 1947 * @return array
1246 1948 */
1247 1949 public function maximum_floor_area_meta_query( ) {
1950 + $request_department = $this->get_requested_department();
1951 +
1248 1952
1249 1953 $meta_query = array();
1250 1954
1251 1955 if (
1252 - isset( $_REQUEST['department'] ) && $_REQUEST['department'] == 'commercial' &&
1253 - isset( $_REQUEST['maximum_floor_area'] ) && $_REQUEST['maximum_floor_area'] != ''
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.
1958 + isset( $_REQUEST['maximum_floor_area'] ) && $_REQUEST['maximum_floor_area'] != '' &&
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.
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.
1963 + ( isset( $_REQUEST['minimum_floor_area'] ) && $_REQUEST['minimum_floor_area'] == '' )
1964 + )
1254 1965 )
1255 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 +
1256 1975 $meta_query = array(
1976 + 'key' => '_floor_area_from_sqft',
1977 + 'value' => $value,
1978 + 'compare' => '<=',
1979 + 'type' => 'NUMERIC'
1980 + );
1981 + }
1982 +
1983 + return $meta_query;
1984 + }
1985 +
1986 + /**
1987 + * Returns a meta query to handle minimum AND maximum floor area
1988 + *
1989 + * @access public
1990 + * @return array
1991 + */
1992 + public function minimum_maximum_floor_area_meta_query( ) {
1993 + $request_department = $this->get_requested_department();
1994 +
1995 +
1996 + $meta_query = array();
1997 +
1998 + if (
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.
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.
2003 + isset( $_REQUEST['maximum_floor_area'] ) && $_REQUEST['maximum_floor_area'] != ''
2004 + )
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 +
2017 + $meta_query[] = array(
2018 + 'key' => '_floor_area_from_sqft',
2019 + 'value' => $maximum_floor_area,
2020 + 'compare' => '<=',
2021 + 'type' => 'NUMERIC'
2022 + );
2023 + $meta_query[] = array(
1257 2024 'key' => '_floor_area_to_sqft',
1258 - 'value' => sanitize_text_field( $_REQUEST['maximum_floor_area'] ),
1259 - 'compare' => '<=',
2025 + 'value' => $minimum_floor_area,
2026 + 'compare' => '>=',
1260 2027 'type' => 'NUMERIC'
1261 2028 );
1262 2029 }
1263 2030
@@ -1262,8 +2029,9 @@
1262 2029 }
1263 2030
1264 2031 return $meta_query;
1265 2032 }
2033 +
1266 2034
1267 2035 /**
1268 2036 * Returns a meta query to handle floor area range
1269 2037 *
@@ -1270,23 +2038,27 @@
1270 2038 * @access public
1271 2039 * @return array
1272 2040 */
1273 2041 public function floor_area_range_meta_query( ) {
2042 + $request_department = $this->get_requested_department();
2043 +
1274 2044
1275 2045 $meta_query = array();
1276 2046
1277 2047 if (
1278 - isset( $_REQUEST['department'] ) && $_REQUEST['department'] == 'commercial' &&
1279 - 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'] != ''
1280 2051 )
1281 2052 {
1282 - $explode_floor_area_range = explode("-", $_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'] ) ));
1283 2055
1284 2056 if ( isset($explode_floor_area_range[0]) && $explode_floor_area_range[0] != '' )
1285 2057 {
1286 2058 $meta_query = array(
1287 2059 'key' => '_floor_area_from_sqft',
1288 - 'value' => sanitize_text_field( $explode_floor_area_range[0] ),
2060 + 'value' => ph_clean( $explode_floor_area_range[0] ),
1289 2061 'compare' => '>=',
1290 2062 'type' => 'NUMERIC'
1291 2063 );
1292 2064 }
@@ -1293,9 +2065,9 @@
1293 2065 if ( isset($explode_floor_area_range[1]) && $explode_floor_area_range[1] != '' )
1294 2066 {
1295 2067 $meta_query = array(
1296 2068 'key' => '_floor_area_to_sqft',
1297 - 'value' => sanitize_text_field( $explode_floor_area_range[1] ),
2069 + 'value' => ph_clean( $explode_floor_area_range[1] ),
1298 2070 'compare' => '<=',
1299 2071 'type' => 'NUMERIC'
1300 2072 );
1301 2073 }
@@ -1304,8 +2076,349 @@
1304 2076 return $meta_query;
1305 2077 }
1306 2078
1307 2079 /**
2080 + * Returns a meta query to handle commercial for sale or to rent
2081 + *
2082 + * @access public
2083 + * @return array
2084 + */
2085 + public function commercial_for_sale_to_rent_meta_query( ) {
2086 + $request_department = $this->get_requested_department();
2087 +
2088 +
2089 + $meta_query = array();
2090 +
2091 + if (
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.
2094 + isset( $_REQUEST['commercial_for_sale_to_rent'] ) && $_REQUEST['commercial_for_sale_to_rent'] == 'for_sale'
2095 + )
2096 + {
2097 + $meta_query = array(
2098 + 'key' => '_for_sale',
2099 + 'value' => 'yes',
2100 + 'compare' => '=',
2101 + );
2102 + }
2103 +
2104 + if (
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.
2107 + isset( $_REQUEST['commercial_for_sale_to_rent'] ) && $_REQUEST['commercial_for_sale_to_rent'] == 'to_rent'
2108 + )
2109 + {
2110 + $meta_query = array(
2111 + 'key' => '_to_rent',
2112 + 'value' => 'yes',
2113 + 'compare' => '=',
2114 + );
2115 + }
2116 +
2117 + return $meta_query;
2118 + }
2119 +
2120 + /**
2121 + * Returns a meta query to handle commercial for sale
2122 + *
2123 + * @access public
2124 + * @return array
2125 + */
2126 + public function commercial_for_sale_meta_query( ) {
2127 + $request_department = $this->get_requested_department();
2128 +
2129 +
2130 + $meta_query = array();
2131 +
2132 + if (
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.
2135 + isset( $_REQUEST['commercial_for_sale'] ) && $_REQUEST['commercial_for_sale'] == '1'
2136 + )
2137 + {
2138 + $meta_query = array(
2139 + 'key' => '_for_sale',
2140 + 'value' => 'yes',
2141 + 'compare' => '=',
2142 + );
2143 + }
2144 +
2145 + return $meta_query;
2146 + }
2147 +
2148 + /**
2149 + * Returns a meta query to handle commercial to rent
2150 + *
2151 + * @access public
2152 + * @return array
2153 + */
2154 + public function commercial_to_rent_meta_query( ) {
2155 + $request_department = $this->get_requested_department();
2156 +
2157 +
2158 + $meta_query = array();
2159 +
2160 + if (
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.
2163 + isset( $_REQUEST['commercial_to_rent'] ) && $_REQUEST['commercial_to_rent'] == '1'
2164 + )
2165 + {
2166 + $meta_query = array(
2167 + 'key' => '_to_rent',
2168 + 'value' => 'yes',
2169 + 'compare' => '=',
2170 + );
2171 + }
2172 +
2173 + return $meta_query;
2174 + }
2175 +
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 + /**
2397 + * Returns a meta query to handle property negotiator
2398 + *
2399 + * @access public
2400 + * @return array
2401 + */
2402 + public function negotiator_meta_query( ) {
2403 +
2404 + $meta_query = array();
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.
2407 + if ( isset( $_REQUEST['negotiator_id'] ) && $_REQUEST['negotiator_id'] != '' )
2408 + {
2409 + $meta_query = array(
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.
2412 + 'value' => (int)$_REQUEST['negotiator_id'],
2413 + 'compare' => '='
2414 + );
2415 + }
2416 +
2417 + return $meta_query;
2418 + }
2419 +
2420 + /**
1308 2421 * Returns a meta query to handle property office
1309 2422 *
1310 2423 * @access public
1311 2424 * @param string $compare (default: 'IN')
@@ -1314,14 +2427,16 @@
1314 2427 public function office_meta_query( ) {
1315 2428
1316 2429 $meta_query = array();
1317 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.
1318 2432 if ( isset( $_REQUEST['officeID'] ) && $_REQUEST['officeID'] != '' )
1319 2433 {
1320 2434 $meta_query = array(
1321 2435 'key' => '_office_id',
1322 - 'value' => $_REQUEST['officeID'],
1323 - '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'
1324 2439 );
1325 2440 }
1326 2441
1327 2442 return $meta_query;
@@ -1326,8 +2441,168 @@
1326 2441
1327 2442 return $meta_query;
1328 2443 }
1329 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 +
1330 2605 /**
1331 2606 * Appends taxonomy queries to an array.
1332 2607 * @access public
1333 2608 * @param array $tax_query
@@ -1336,23 +2611,86 @@
1336 2611 public function get_tax_query( $tax_query = array() ) {
1337 2612 if ( ! is_array( $tax_query ) )
1338 2613 $tax_query = array();
1339 2614
2615 + // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Public read-only taxonomy search; this does not authorize a write.
1340 2616 if ( isset($_REQUEST) && !empty($_REQUEST) )
1341 2617 {
2618 +
2619 + // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Public read-only taxonomy search; each value is validated and sanitized below.
1342 2620 foreach ( $_REQUEST as $key => $value )
1343 2621 {
1344 - if ( taxonomy_exists($key) && isset( $_REQUEST[$key] ) && !empty($_REQUEST[$key]) )
2622 +
2623 + if ( taxonomy_exists($key) && !empty($value) && $this->taxonomy_allowed_for_department( $key ) )
1345 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 +
1346 2633 $tax_query[] = array(
1347 2634 'taxonomy' => $key,
1348 - 'terms' => ( (is_array($value)) ? $value : array( $value ) )
2635 + 'terms' => ph_clean( wp_unslash( $terms ) ),
2636 + 'operator' => $operator,
1349 2637 );
1350 2638 }
1351 2639 }
1352 2640 }
1353 -
1354 - return array_filter( $tax_query );
2641 +
2642 + return array_filter( apply_filters( 'propertyhive_property_query_tax_query', $tax_query, $this ) );
2643 + }
2644 +
2645 + private function taxonomy_allowed_for_department( $taxonomy )
2646 + {
2647 + $request_department = $this->get_requested_department();
2648 +
2649 + if ( isset( $request_department ) && $request_department != '' )
2650 + {
2651 + $department = ph_clean($request_department);
2652 + }
2653 + else
2654 + {
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 );
2661 + }
2662 +
2663 + switch ( $department )
2664 + {
2665 + case 'residential-sales':
2666 + {
2667 + if ( in_array( $taxonomy, array('commercial_property_type', 'commercial_tenure', 'furnished') ) )
2668 + {
2669 + return false;
2670 + }
2671 + break;
2672 + }
2673 + case 'residential-lettings':
2674 + {
2675 + if ( in_array( $taxonomy, array('commercial_property_type', 'commercial_tenure', 'tenure', 'sale_by', 'price_qualifier') ) )
2676 + {
2677 + return false;
2678 + }
2679 + break;
2680 + }
2681 + case 'commercial':
2682 + {
2683 + if ( in_array( $taxonomy, array('property_type', 'tenure', 'parking', 'outside_space', 'furnished') ) )
2684 + {
2685 + return false;
2686 + }
2687 + break;
2688 + }
2689 + }
2690 +
2691 +
2692 + return true;
1355 2693 }
1356 2694
1357 2695 /**
1358 2696 * Layered Nav Init