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 +1122 -166 1.4.602.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() ) {
@@ -61,13 +79,108 @@
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' ) );
64 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 );
65 85 }
66 86
67 87 $this->init_query_vars();
68 88 }
69 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 +
70 183 public function commercial_display_where( $where, $query )
71 184 {
72 185 if ( $query->get('post_type') == 'property' )
73 186 {
@@ -87,9 +200,10 @@
87 200 $unit_filter_parameters = apply_filters( 'propertyhive_unit_filter_parameters', array( 'minimum_floor_area', 'maximum_floor_area' ) );
88 201 $unit_filter_parameter_found = false;
89 202 foreach ( $unit_filter_parameters as $parameter )
90 203 {
91 - if ( isset($_REQUEST[$parameter]) && ph_clean($_REQUEST[$parameter]) != '' )
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] ) ) != '' )
92 206 {
93 207 $unit_filter_parameter_found = true;
94 208 }
95 209 }
@@ -122,21 +236,14 @@
122 236 /**
123 237 * Get any errors from querystring
124 238 */
125 239 public function get_errors() {
126 - 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' ) )
127 242 ph_add_notice( $error, 'error' );
128 243 }
129 244
130 245 /**
131 - * Add endpoints for query vars
132 - */
133 - public function add_endpoints() {
134 - foreach ( $this->query_vars as $key => $var )
135 - add_rewrite_endpoint( $var, EP_PAGES );
136 - }
137 -
138 - /**
139 246 * add_query_vars function.
140 247 *
141 248 * @access public
142 249 * @param array $vars
@@ -164,9 +271,11 @@
164 271 global $wp;
165 272
166 273 // Map query vars to their keys, or get them if endpoints are not supported
167 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.
168 276 if ( isset( $_GET[ $var ] ) ) {
277 + // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- The public frontend normalizes a URL query variable into the current WP request query_vars; this is request/query state only and has no persistent write. This exact annotation covers only NonceVerification.Recommended; retain all sanitizer and SQL-safety checks.
169 278 $wp->query_vars[ $key ] = sanitize_text_field( wp_unslash( $_GET[ $var ] ) );
170 279 }
171 280
172 281 elseif ( isset( $wp->query_vars[ $var ] ) ) {
@@ -223,8 +332,9 @@
223 332 if ( isset( $q->query['paged'] ) )
224 333 $q->set( 'paged', $q->query['paged'] );
225 334
226 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.
227 337 define( 'SEARCH_RESULTS_IS_ON_FRONT', true );
228 338
229 339 // Get the actual WP page to avoid errors and let us use is_front_page()
230 340 // This is hacky but works. Awaiting http://core.trac.wordpress.org/ticket/21096
@@ -414,9 +524,9 @@
414 524 $post__in = array();
415 525 //$post__in = array_unique( apply_filters( 'loop_shop_post_in', array() ) );
416 526
417 527 // Ordering query vars
418 - $q->set( 'orderby', $ordering['orderby'] );
528 + $q->set( 'orderby', $ordering['orderby'] . ' post_title' );
419 529 $q->set( 'order', $ordering['order'] );
420 530 if ( isset( $ordering['meta_key'] ) )
421 531 $q->set( 'meta_key', $ordering['meta_key'] );
422 532
@@ -424,8 +534,9 @@
424 534 $q->set( 'meta_query', $meta_query );
425 535 $q->set( 'tax_query', $tax_query );
426 536 $q->set( 'date_query', $date_query );
427 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.
428 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' ) ) );
429 540
430 541 // Set a special variable
431 542 $q->set( 'ph_query', true );
@@ -497,8 +608,9 @@
497 608 array(
498 609 'post_type' => 'property',
499 610 'numberposts' => -1,
500 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.
501 613 'meta_query' => $this->meta_query,
502 614 'fields' => 'ids',
503 615 'no_found_rows' => true,
504 616 'update_post_meta_cache' => false,
@@ -533,11 +645,14 @@
533 645 * @access public
534 646 * @return array
535 647 */
536 648 public function get_search_results_ordering_args( $orderby = '', $order = '' ) {
649 + $request_department = $this->get_requested_department();
650 +
537 651 // Get ordering from query string unless defined
538 652 if ( ! $orderby ) {
539 - $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' ) );
540 655
541 656 // Get order + orderby args from string
542 657 $orderby_value = explode( '-', $orderby_value );
543 658 $orderby = esc_attr( $orderby_value[0] );
@@ -550,23 +665,29 @@
550 665 $args = array();
551 666
552 667 // default - menu_order
553 668 if (
554 - ( isset($_REQUEST['department']) && $_REQUEST['department'] != 'commercial' ) ||
555 - ( !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' )
556 673 )
557 674 {
558 675 $args['orderby'] = 'meta_value_num';
559 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.
560 678 $args['meta_key'] = '_price_actual';
561 679 }
562 680 elseif (
563 - ( isset($_REQUEST['department']) && $_REQUEST['department'] == 'commercial' ) ||
564 - ( !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' ) )
565 685 )
566 686 {
567 687 $args['orderby'] = 'meta_value_num';
568 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.
569 690 $args['meta_key'] = '_floor_area_from_sqft';
570 691 }
571 692
572 693 switch ( $orderby ) {
@@ -573,16 +694,20 @@
573 694 case 'price' :
574 695 $args['orderby'] = 'meta_value_num';
575 696 $args['order'] = $order == 'ASC' ? 'ASC' : 'DESC';
576 697 if (
577 - ( isset($_REQUEST['department']) && $_REQUEST['department'] == 'commercial' ) ||
578 - ( !isset($_REQUEST['department']) && get_option( 'propertyhive_primary_department' ) == 'commercial' )
698 + // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- The public property search reads ordering/filter inputs and returns ordering arguments for the current query only. This exact annotation covers only NonceVerification.Recommended; retain all sanitizer and SQL-safety checks.
699 + ( isset($request_department) && ( $request_department == 'commercial' || ph_get_custom_department_based_on($request_department) == 'commercial' ) ) ||
700 + // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- The public property search reads ordering/filter inputs and returns ordering arguments for the current query only. This exact annotation covers only NonceVerification.Recommended; retain all sanitizer and SQL-safety checks.
701 + ( !isset($request_department) && ( get_option( 'propertyhive_primary_department' ) == 'commercial' || ph_get_custom_department_based_on(get_option( 'propertyhive_primary_department' )) == 'commercial' ) )
579 702 )
580 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.
581 705 $args['meta_key'] = '_price_from_actual';
582 706 }
583 707 else
584 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.
585 710 $args['meta_key'] = '_price_actual';
586 711 }
587 712 break;
588 713 case 'floor_area' :
@@ -587,10 +712,17 @@
587 712 break;
588 713 case 'floor_area' :
589 714 $args['orderby'] = 'meta_value_num';
590 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.
591 717 $args['meta_key'] = '_floor_area_from_sqft';
592 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;
593 725 default :
594 726 {
595 727 if ( $orderby != '' )
596 728 {
@@ -612,21 +744,25 @@
612 744 public function get_date_query() {
613 745
614 746 $date_query = array();
615 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.
616 749 if ( isset( $_REQUEST['added_from'] ) && $_REQUEST['added_from'] != '' )
617 750 {
618 751 $date_query = array(
619 752 'column' => 'post_date_gmt',
620 - 'after' => sanitize_text_field( $_REQUEST['added_from'] )
753 + // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- The public property search reads a filter value and contributes query/meta/taxonomy arguments for the current request only; no persistent state-changing operation is reached. This exact annotation covers only NonceVerification.Recommended; retain all sanitizer and SQL-safety checks.
754 + 'after' => sanitize_text_field( wp_unslash( $_REQUEST['added_from'] ) )
621 755 );
622 756 }
623 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.
624 759 if ( isset( $_REQUEST['added_from_hours'] ) && $_REQUEST['added_from_hours'] != '' )
625 760 {
626 761 $date_query = array(
627 762 'column' => 'post_date_gmt',
628 - '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'
629 765 );
630 766 }
631 767
632 768 return array_filter( $date_query );
@@ -650,8 +786,10 @@
650 786 $meta_query = array();
651 787
652 788 $meta_query[] = $this->on_market_meta_query();
653 789 $meta_query[] = $this->department_meta_query($q);
790 + $meta_query[] = $this->featured_meta_query();
791 + $meta_query[] = $this->date_added_meta_query();
654 792 $meta_query[] = $this->address_keyword_meta_query();
655 793 $meta_query[] = $this->country_meta_query();
656 794 $meta_query[] = $this->minimum_price_meta_query();
657 795 $meta_query[] = $this->maximum_price_meta_query();
@@ -673,11 +811,16 @@
673 811 $meta_query[] = $this->floor_area_range_meta_query();
674 812 $meta_query[] = $this->commercial_for_sale_to_rent_meta_query();
675 813 $meta_query[] = $this->commercial_for_sale_meta_query();
676 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();
677 819 $meta_query[] = $this->negotiator_meta_query();
678 820 $meta_query[] = $this->office_meta_query();
679 -
821 + $meta_query[] = $this->keyword_meta_query();
822 +
680 823 return array_filter( apply_filters( 'propertyhive_property_query_meta_query', $meta_query, $this ) );
681 824 }
682 825
683 826 /**
@@ -709,16 +852,18 @@
709 852 * @access public
710 853 * @return array
711 854 */
712 855 public function department_meta_query( $q ) {
856 + $request_department = $this->get_requested_department();
857 +
713 858
714 859 $meta_query = array();
715 860
716 - if ( isset( $_REQUEST['department'] ) && $_REQUEST['department'] != '' )
861 + if ( isset( $request_department ) && $request_department != '' )
717 862 {
718 863 $meta_query = array(
719 864 'key' => '_department',
720 - 'value' => sanitize_text_field( $_REQUEST['department'] ),
865 + 'value' => sanitize_text_field( $request_department ),
721 866 'compare' => '='
722 867 );
723 868 }
724 869 else
@@ -759,120 +904,314 @@
759 904 return $meta_query;
760 905 }
761 906
762 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 + /**
763 956 * Returns a meta query to handle searching for a keyword in the address
764 957 *
765 958 * @access public
766 - * @param string $compare (default: 'IN')
767 959 * @return array
768 960 */
769 961 public function address_keyword_meta_query( ) {
770 962
771 963 $meta_query = array();
772 -
773 - 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']) )
774 967 {
775 - $_REQUEST['address_keyword'] = ph_clean( wp_unslash( $_REQUEST['address_keyword'] ) );
776 968
777 - // Remove country code from end (i.e. ', UK')
778 - $_REQUEST['address_keyword'] = preg_replace('/\,\s?[A-Z][A-Z]$/', '', $_REQUEST['address_keyword']);
969 + // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Public read-only address search; values are validated below before query construction.
970 + $address_input = ph_clean( wp_unslash( $_REQUEST['address_keyword'] ) );
971 + if ( ! is_string( $address_input ) && ! is_array( $address_input ) ) {
972 + return $meta_query;
973 + }
974 + foreach ( (array) $address_input as $address_value ) {
975 + if ( ! is_string( $address_value ) ) {
976 + return $meta_query;
977 + }
978 + }
979 + $address_input = ph_clean( $address_input );
980 + // Preserve the normalized request value consumed by existing extensions.
981 + $_REQUEST['address_keyword'] = $address_input;
779 982
780 - $address_keywords = array( $_REQUEST['address_keyword'] );
983 + $do_address_search = true;
984 + if ( is_string( $address_input ) && get_option( 'propertyhive_address_keyword_compare', '=' ) == 'polygon' )
985 + {
986 + $address_keyword_polygon = new PH_Address_Keyword_Polygon();
781 987
782 - if ( strpos( $_REQUEST['address_keyword'], ' ' ) !== FALSE )
783 - {
784 - $address_keywords[] = str_replace(" ", "-", ph_clean($_REQUEST['address_keyword']));
988 + $polygon_coordinates = $address_keyword_polygon->get_address_keyword_polygon_coordinates( $address_input . ', UK' );
989 +
990 + if ( $polygon_coordinates !== FALSE )
991 + {
992 + $this->address_keyword_polygon_points = $polygon_coordinates;
993 + add_filter( 'posts_where' , array( $this, 'where_properties_in_polygon' ), 1, 2 );
994 + $do_address_search = false;
995 + }
785 996 }
786 - if ( strpos( $_REQUEST['address_keyword'], '-' ) !== FALSE )
997 +
998 + if ( $do_address_search )
787 999 {
788 - $address_keywords[] = str_replace("-", " ", ph_clean($_REQUEST['address_keyword']));
789 - }
790 1000
791 - $meta_query = array('relation' => 'OR');
1001 + $address_keywords_to_query = is_array($address_input) ? $address_input : array( $address_input );
792 1002
793 - $address_fields_to_query = array(
794 - '_reference_number',
795 - '_address_street',
796 - '_address_two',
797 - '_address_three',
798 - '_address_four',
799 - '_address_postcode'
800 - );
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 + );
801 1012
802 - $address_fields_to_query = apply_filters( 'propertyhive_address_fields_to_query', $address_fields_to_query );
1013 + $address_keywords = array();
803 1014
804 - foreach ( $address_keywords as $address_keyword )
805 - {
806 - foreach ( $address_fields_to_query as $address_field )
807 - {
808 - if ( $address_field == '_address_postcode' ) { continue; } // ignore postcode as that is handled differently afterwards
1015 + if ( !empty($address_keywords_to_query) )
1016 + {
1017 + foreach ( $address_keywords_to_query as $address_keyword )
1018 + {
1019 + // Remove country code from end (i.e. ', UK')
1020 + $address_keyword = preg_replace('/\,\s?[A-Z][A-Z]$/', '', $address_keyword);
809 1021
810 - $meta_query[] = array(
811 - 'key' => $address_field,
812 - 'value' => $address_keyword,
813 - 'compare' => get_option( 'propertyhive_address_keyword_compare', '=' )
814 - );
815 - }
816 - }
817 - if ( in_array('_address_postcode', $address_fields_to_query) )
818 - {
819 - if ( strlen($_REQUEST['address_keyword']) <= 4 )
1022 + // Extract postcode and use that if exists
1023 + $postcode_pattern = '/\b([A-Z]{1,2}[0-9][0-9A-Z]? ?[0-9]?[A-Z]{0,2})\b/i';
1024 + if ( preg_match($postcode_pattern, $address_keyword, $matches) )
1025 + {
1026 + $address_keyword = $matches[1];
1027 + }
1028 +
1029 + $address_keyword = trim($address_keyword);
1030 +
1031 + $address_keywords[] = ph_clean($address_keyword);
1032 +
1033 + if ( strpos( $address_keyword, ' ' ) !== FALSE )
1034 + {
1035 + $address_keywords[] = str_replace(" ", "-", ph_clean($address_keyword));
1036 + }
1037 + if ( strpos( $address_keyword, '-' ) !== FALSE )
1038 + {
1039 + $address_keywords[] = str_replace("-", " ", ph_clean($address_keyword));
1040 + }
1041 + if ( strpos( $address_keyword, '.' ) !== FALSE )
1042 + {
1043 + $address_keywords[] = str_replace(".", "", ph_clean($address_keyword));
1044 + }
1045 + if ( stripos( $address_keyword, 'st ' ) !== FALSE )
1046 + {
1047 + $address_keywords[] = str_ireplace("st ", "st. ", ph_clean($address_keyword));
1048 + }
1049 + if ( strpos( $address_keyword, '\'' ) !== FALSE )
1050 + {
1051 + $address_keywords[] = str_replace("'", "", ph_clean($address_keyword));
1052 + }
1053 + }
1054 + }
1055 +
1056 + $address_keywords = apply_filters( 'propertyhive_address_keywords_to_query', $address_keywords );
1057 +
1058 + $meta_query = array('relation' => 'OR');
1059 +
1060 + // add country to list of fields to query if it looks like we're working with an overseas site
1061 + $countries = get_option( 'propertyhive_countries', array() );
1062 + if ( !is_array($countries) ) { $countries = array(); }
1063 + if ( count($countries) > 1 )
820 1064 {
821 - $meta_query[] = array(
822 - 'key' => '_address_postcode',
823 - 'value' => ph_clean( $_REQUEST['address_keyword'] ),
824 - 'compare' => '='
825 - );
826 - $meta_query[] = array(
827 - 'key' => '_address_postcode',
828 - 'value' => '^' . ph_clean( $_REQUEST['address_keyword'] ) . '[ ]',
829 - 'compare' => 'RLIKE'
830 - );
1065 + $address_fields_to_query[] = '_address_country';
831 1066 }
832 - else
1067 +
1068 + $address_fields_to_query = array_unique($address_fields_to_query);
1069 + $address_fields_to_query = apply_filters( 'propertyhive_address_fields_to_query', $address_fields_to_query );
1070 +
1071 + foreach ( $address_keywords as $address_keyword )
833 1072 {
834 - $postcode = ph_clean( $_REQUEST['address_keyword'] );
1073 + foreach ( $address_fields_to_query as $address_field )
1074 + {
1075 + if ( in_array( $address_field, array('_address_postcode', '_address_country', '_address_concatenated') ) ) { continue; } // ignore postcode and country as they're handled differently afterwards
835 1076
836 - 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) )
837 - {
838 - // UK postcode found with no space
1077 + $meta_query[] = array(
1078 + 'key' => $address_field,
1079 + 'value' => $address_keyword,
1080 + 'compare' => get_option( 'propertyhive_address_keyword_compare', '=' )
1081 + );
1082 + }
839 1083
840 - if ( strlen($postcode) == 5 )
841 - {
842 - $first_part = substr($postcode, 0, 2);
843 - $last_part = substr($postcode, 2, 3);
1084 + if ( in_array('_address_postcode', $address_fields_to_query) )
1085 + {
1086 + if ( strlen($address_keyword) <= 4 )
1087 + {
1088 + $meta_query[] = array(
1089 + 'key' => '_address_postcode',
1090 + 'value' => ph_clean($address_keyword),
1091 + 'compare' => '='
1092 + );
1093 + // Run regex match where given keyword is at the start of the postcode ^
1094 + // followed by one or zero letters (for WC2E-style postcodes) [a-zA-Z]?
1095 + // then a single space [ ]
1096 + $meta_query[] = array(
1097 + 'key' => '_address_postcode',
1098 + 'value' => '^' . ph_clean($address_keyword) . '[a-zA-Z]?[ ]',
1099 + 'compare' => 'RLIKE'
1100 + );
1101 + }
1102 + else
1103 + {
1104 + $postcode = ph_clean($address_keyword);
844 1105
845 - $postcode = $first_part . ' ' . $last_part;
846 - }
847 - elseif ( strlen($postcode) == 6 )
848 - {
849 - $first_part = substr($postcode, 0, 3);
850 - $last_part = substr($postcode, 3, 3);
1106 + if ( preg_match('#^(GIR ?0AA|[A-PR-UWYZ]([0-9]{1,2}|([A-HK-Y][0-9]([0-9ABEHMNPRV-Y])?)|[0-9][A-HJKPS-UW])[0-9][ABD-HJLNP-UW-Z]{2})$#i', $postcode) )
1107 + {
1108 + // UK postcode found with no space
851 1109
852 - $postcode = $first_part . ' ' . $last_part;
853 - }
854 - elseif ( strlen($postcode) == 7 )
855 - {
856 - $first_part = substr($postcode, 0, 4);
857 - $last_part = substr($postcode, 4, 3);
1110 + if ( strlen($postcode) == 5 )
1111 + {
1112 + $first_part = substr($postcode, 0, 2);
1113 + $last_part = substr($postcode, 2, 3);
858 1114
859 - $postcode = $first_part . ' ' . $last_part;
860 - }
861 - }
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);
862 1121
863 - $meta_query[] = array(
864 - 'key' => '_address_postcode',
865 - 'value' => ph_clean( $postcode ),
866 - 'compare' => 'LIKE'
867 - );
868 - }
869 - }
1122 + $postcode = $first_part . ' ' . $last_part;
1123 + }
1124 + elseif ( strlen($postcode) == 7 )
1125 + {
1126 + $first_part = substr($postcode, 0, 4);
1127 + $last_part = substr($postcode, 4, 3);
1128 +
1129 + $postcode = $first_part . ' ' . $last_part;
1130 + }
1131 + }
1132 +
1133 + $meta_query[] = array(
1134 + 'key' => '_address_postcode',
1135 + 'value' => ph_clean( $postcode ),
1136 + 'compare' => 'LIKE'
1137 + );
1138 + }
1139 + }
1140 +
1141 + if ( in_array('_address_country', $address_fields_to_query) )
1142 + {
1143 + $meta_query[] = array(
1144 + 'key' => '_address_country',
1145 + 'value' => $address_keyword,
1146 + 'compare' => '='
1147 + );
1148 +
1149 + // get country code for country entered
1150 + $PH_Countries = new PH_Countries();
1151 + $countries = $PH_Countries->countries;
1152 + if ( is_array($countries) && !empty($countries) )
1153 + {
1154 + foreach ( $countries as $country_code => $country )
1155 + {
1156 + if ( strtolower($address_keyword) == strtolower($country['name']) )
1157 + {
1158 + $meta_query[] = array(
1159 + 'key' => '_address_country',
1160 + 'value' => $country_code,
1161 + 'compare' => '='
1162 + );
1163 + break;
1164 + }
1165 + }
1166 + }
1167 + }
1168 +
1169 + if (
1170 + !preg_match('/^(?:[A-Z]{2}\d|[A-Z]\d)/i', $address_keyword) &&
1171 + in_array('_address_concatenated', $address_fields_to_query)
1172 + )
1173 + {
1174 + $meta_query[] = array(
1175 + 'key' => '_address_concatenated',
1176 + 'value' => $address_keyword,
1177 + 'compare' => 'LIKE'
1178 + );
1179 + }
1180 + }
1181 +
1182 + }
870 1183 }
871 1184
872 1185 return $meta_query;
873 1186 }
874 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 +
875 1214 /**
876 1215 * Returns a meta query to handle country
877 1216 *
878 1217 * @access public
@@ -881,15 +1220,28 @@
881 1220 public function country_meta_query( ) {
882 1221
883 1222 $meta_query = array();
884 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.
885 1225 if ( isset( $_REQUEST['country'] ) && $_REQUEST['country'] != '' )
886 1226 {
887 1227 $meta_query = array(
888 1228 'key' => '_address_country',
889 - 'value' => ph_clean( $_REQUEST['country'] )
1229 + // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- The public property search reads a filter value and contributes query/meta/taxonomy arguments for the current request only; no persistent state-changing operation is reached. This exact annotation covers only NonceVerification.Recommended; retain all sanitizer and SQL-safety checks.
1230 + 'value' => ph_clean( wp_unslash( $_REQUEST['country'] ) )
890 1231 );
891 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 + }
892 1244
893 1245 return $meta_query;
894 1246 }
895 1247
@@ -899,19 +1251,30 @@
899 1251 * @access public
900 1252 * @return array
901 1253 */
902 1254 public function minimum_price_meta_query( ) {
1255 + $request_department = $this->get_requested_department();
1256 +
903 1257
904 1258 $meta_query = array();
905 1259
906 1260 if (
907 - 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.
908 1263 isset( $_REQUEST['minimum_price'] ) && $_REQUEST['minimum_price'] != ''
909 1264 )
910 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 +
911 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 );
912 1276
913 - $minimum_price = $_REQUEST['minimum_price'];
914 1277 if ( $search_form_currency != 'GBP' )
915 1278 {
916 1279 // Convert $_REQUEST['minimum_price'] to GBP
917 1280 $ph_countries = new PH_Countries();
@@ -936,19 +1299,30 @@
936 1299 * @access public
937 1300 * @return array
938 1301 */
939 1302 public function maximum_price_meta_query( ) {
1303 + $request_department = $this->get_requested_department();
1304 +
940 1305
941 1306 $meta_query = array();
942 1307
943 1308 if (
944 - 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.
945 1311 isset( $_REQUEST['maximum_price'] ) && $_REQUEST['maximum_price'] != ''
946 1312 )
947 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 +
948 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 );
949 1324
950 - $maximum_price = $_REQUEST['maximum_price'];
951 1325 if ( $search_form_currency != 'GBP' )
952 1326 {
953 1327 // Convert $_REQUEST['maximum_price'] to GBP
954 1328 $ph_countries = new PH_Countries();
@@ -973,24 +1347,34 @@
973 1347 * @access public
974 1348 * @return array
975 1349 */
976 1350 public function price_range_meta_query( ) {
1351 + $request_department = $this->get_requested_department();
1352 +
977 1353
978 1354 $meta_query = array();
979 1355
980 1356 if (
981 - isset( $_REQUEST['department'] ) && $_REQUEST['department'] == 'residential-sales' &&
982 - 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'] != ''
983 1360 )
984 1361 {
985 - $explode_price_range = explode("-", ph_clean($_REQUEST['price_range']));
1362 + // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- The public property search reads a filter value and contributes query/meta/taxonomy arguments for the current request only; no persistent state-changing operation is reached. This exact annotation covers only NonceVerification.Recommended; retain all sanitizer and SQL-safety checks.
1363 + $explode_price_range = explode("-", ph_clean( wp_unslash( $_REQUEST['price_range'] ) ));
986 1364
987 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 );
988 1367
989 1368 if ( isset($explode_price_range[0]) && $explode_price_range[0] != '' )
990 1369 {
991 1370 $minimum_price = $explode_price_range[0];
992 1371
1372 + if ( !is_numeric($minimum_price) )
1373 + {
1374 + return $meta_query;
1375 + }
1376 +
993 1377 if ( $search_form_currency != 'GBP' )
994 1378 {
995 1379 // Convert $explode_price_range[0] to GBP
996 1380 $ph_countries = new PH_Countries();
@@ -1007,8 +1391,14 @@
1007 1391 }
1008 1392 if ( isset($explode_price_range[1]) && $explode_price_range[1] != '' )
1009 1393 {
1010 1394 $maximum_price = $explode_price_range[1];
1395 +
1396 + if ( !is_numeric($maximum_price) )
1397 + {
1398 + return $meta_query;
1399 + }
1400 +
1011 1401 if ( $search_form_currency != 'GBP' )
1012 1402 {
1013 1403 // Convert $explode_price_range[1] to GBP
1014 1404 $ph_countries = new PH_Countries();
@@ -1034,19 +1424,30 @@
1034 1424 * @access public
1035 1425 * @return array
1036 1426 */
1037 1427 public function minimum_rent_meta_query( ) {
1428 + $request_department = $this->get_requested_department();
1429 +
1038 1430
1039 1431 $meta_query = array();
1040 1432
1041 1433 if (
1042 - 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.
1043 1436 isset( $_REQUEST['minimum_rent'] ) && $_REQUEST['minimum_rent'] != ''
1044 1437 )
1045 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 +
1046 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 );
1047 1449
1048 - $minimum_rent = $_REQUEST['minimum_rent'];
1049 1450 if ( $search_form_currency != 'GBP' )
1050 1451 {
1051 1452 // Convert $_REQUEST['minimum_rent'] to GBP
1052 1453 $ph_countries = new PH_Countries();
@@ -1053,8 +1454,17 @@
1053 1454
1054 1455 $minimum_rent = $ph_countries->convert_price_to_gbp( $minimum_rent, $search_form_currency );
1055 1456 }
1056 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 +
1057 1467 $meta_query = array(
1058 1468 'key' => '_price_actual',
1059 1469 'value' => ph_clean( floor( $minimum_rent ) ),
1060 1470 'compare' => '>=',
@@ -1071,19 +1481,30 @@
1071 1481 * @access public
1072 1482 * @return array
1073 1483 */
1074 1484 public function maximum_rent_meta_query( ) {
1485 + $request_department = $this->get_requested_department();
1486 +
1075 1487
1076 1488 $meta_query = array();
1077 1489
1078 1490 if (
1079 - 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.
1080 1493 isset( $_REQUEST['maximum_rent'] ) && $_REQUEST['maximum_rent'] != ''
1081 1494 )
1082 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 +
1083 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 );
1084 1506
1085 - $maximum_rent = $_REQUEST['maximum_rent'];
1086 1507 if ( $search_form_currency != 'GBP' )
1087 1508 {
1088 1509 // Convert $_REQUEST['maximum_rent'] to GBP
1089 1510 $ph_countries = new PH_Countries();
@@ -1090,8 +1511,17 @@
1090 1511
1091 1512 $maximum_rent = $ph_countries->convert_price_to_gbp( $maximum_rent, $search_form_currency );
1092 1513 }
1093 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 +
1094 1524 $meta_query = array(
1095 1525 'key' => '_price_actual',
1096 1526 'value' => ph_clean( ceil( $maximum_rent ) ),
1097 1527 'compare' => '<=',
@@ -1108,23 +1538,36 @@
1108 1538 * @access public
1109 1539 * @return array
1110 1540 */
1111 1541 public function rent_range_meta_query( ) {
1542 + $request_department = $this->get_requested_department();
1543 +
1112 1544
1113 1545 $meta_query = array();
1114 1546
1115 1547 if (
1116 - isset( $_REQUEST['department'] ) && $_REQUEST['department'] == 'residential-lettings' &&
1117 - 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'] != ''
1118 1551 )
1119 1552 {
1120 - $explode_rent_range = explode("-", ph_clean($_REQUEST['rent_range']));
1553 + // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- The public property search reads a filter value and contributes query/meta/taxonomy arguments for the current request only; no persistent state-changing operation is reached. This exact annotation covers only NonceVerification.Recommended; retain all sanitizer and SQL-safety checks.
1554 + $explode_rent_range = explode("-", ph_clean( wp_unslash( $_REQUEST['rent_range'] ) ));
1121 1555
1122 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 );
1123 1558
1559 + $rent_frequency = apply_filters( 'propertyhive_search_form_rent_frequency', 'pcm' );
1560 +
1124 1561 if ( isset($explode_rent_range[0]) && $explode_rent_range[0] != '' )
1125 1562 {
1126 1563 $minimum_rent = $explode_rent_range[0];
1564 +
1565 + if ( !is_numeric($minimum_rent) )
1566 + {
1567 + return $meta_query;
1568 + }
1569 +
1127 1570 if ( $search_form_currency != 'GBP' )
1128 1571 {
1129 1572 // Convert $explode_rent_range[0] to GBP
1130 1573 $ph_countries = new PH_Countries();
@@ -1131,8 +1574,16 @@
1131 1574
1132 1575 $minimum_rent = $ph_countries->convert_price_to_gbp( $minimum_rent, $search_form_currency );
1133 1576 }
1134 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 +
1135 1586 $meta_query[] = array(
1136 1587 'key' => '_price_actual',
1137 1588 'value' => sanitize_text_field( floor( $minimum_rent ) ),
1138 1589 'compare' => '>=',
@@ -1141,8 +1592,14 @@
1141 1592 }
1142 1593 if ( isset($explode_rent_range[1]) && $explode_rent_range[1] != '' )
1143 1594 {
1144 1595 $maximum_rent = $explode_rent_range[1];
1596 +
1597 + if ( !is_numeric($maximum_rent) )
1598 + {
1599 + return $meta_query;
1600 + }
1601 +
1145 1602 if ( $search_form_currency != 'GBP' )
1146 1603 {
1147 1604 // Convert $explode_rent_range[1] to GBP
1148 1605 $ph_countries = new PH_Countries();
@@ -1149,8 +1606,16 @@
1149 1606
1150 1607 $maximum_rent = $ph_countries->convert_price_to_gbp( $maximum_rent, $search_form_currency );
1151 1608 }
1152 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 +
1153 1618 $meta_query[] = array(
1154 1619 'key' => '_price_actual',
1155 1620 'value' => sanitize_text_field( ceil( $maximum_rent ) ),
1156 1621 'compare' => '<=',
@@ -1168,22 +1633,26 @@
1168 1633 * @access public
1169 1634 * @return array
1170 1635 */
1171 1636 public function bedrooms_meta_query( ) {
1637 + $request_department = $this->get_requested_department();
1638 +
1172 1639
1173 1640 $meta_query = array();
1174 1641
1175 1642 if (
1176 1643 (
1177 - (isset( $_REQUEST['department'] ) && $_REQUEST['department'] == 'residential-sales') ||
1178 - (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' ))
1179 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.
1180 1648 isset( $_REQUEST['bedrooms'] ) && $_REQUEST['bedrooms'] != ''
1181 1649 )
1182 1650 {
1183 1651 $meta_query = array(
1184 1652 'key' => '_bedrooms',
1185 - 'value' => ph_clean( $_REQUEST['bedrooms'] ),
1653 + // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- The public property search reads a filter value and contributes query/meta/taxonomy arguments for the current request only; no persistent state-changing operation is reached. This exact annotation covers only NonceVerification.Recommended; retain all sanitizer and SQL-safety checks.
1654 + 'value' => ph_clean( wp_unslash( $_REQUEST['bedrooms'] ) ),
1186 1655 'compare' => '=',
1187 1656 'type' => 'NUMERIC'
1188 1657 );
1189 1658 }
@@ -1197,22 +1666,26 @@
1197 1666 * @access public
1198 1667 * @return array
1199 1668 */
1200 1669 public function minimum_bedrooms_meta_query( ) {
1670 + $request_department = $this->get_requested_department();
1671 +
1201 1672
1202 1673 $meta_query = array();
1203 1674
1204 1675 if (
1205 1676 (
1206 - (isset( $_REQUEST['department'] ) && $_REQUEST['department'] == 'residential-sales') ||
1207 - (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' ))
1208 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.
1209 1681 isset( $_REQUEST['minimum_bedrooms'] ) && $_REQUEST['minimum_bedrooms'] != ''
1210 1682 )
1211 1683 {
1212 1684 $meta_query = array(
1213 1685 'key' => '_bedrooms',
1214 - 'value' => ph_clean( $_REQUEST['minimum_bedrooms'] ),
1686 + // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- The public property search reads a filter value and contributes query/meta/taxonomy arguments for the current request only; no persistent state-changing operation is reached. This exact annotation covers only NonceVerification.Recommended; retain all sanitizer and SQL-safety checks.
1687 + 'value' => ph_clean( wp_unslash( $_REQUEST['minimum_bedrooms'] ) ),
1215 1688 'compare' => '>=',
1216 1689 'type' => 'NUMERIC'
1217 1690 );
1218 1691 }
@@ -1226,22 +1699,26 @@
1226 1699 * @access public
1227 1700 * @return array
1228 1701 */
1229 1702 public function maximum_bedrooms_meta_query( ) {
1703 + $request_department = $this->get_requested_department();
1704 +
1230 1705
1231 1706 $meta_query = array();
1232 1707
1233 1708 if (
1234 1709 (
1235 - (isset( $_REQUEST['department'] ) && $_REQUEST['department'] == 'residential-sales') ||
1236 - (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' ))
1237 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.
1238 1714 isset( $_REQUEST['maximum_bedrooms'] ) && $_REQUEST['maximum_bedrooms'] != ''
1239 1715 )
1240 1716 {
1241 1717 $meta_query = array(
1242 1718 'key' => '_bedrooms',
1243 - 'value' => ph_clean( $_REQUEST['maximum_bedrooms'] ),
1719 + // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- The public property search reads a filter value and contributes query/meta/taxonomy arguments for the current request only; no persistent state-changing operation is reached. This exact annotation covers only NonceVerification.Recommended; retain all sanitizer and SQL-safety checks.
1720 + 'value' => ph_clean( wp_unslash( $_REQUEST['maximum_bedrooms'] ) ),
1244 1721 'compare' => '<=',
1245 1722 'type' => 'NUMERIC'
1246 1723 );
1247 1724 }
@@ -1255,22 +1732,26 @@
1255 1732 * @access public
1256 1733 * @return array
1257 1734 */
1258 1735 public function minimum_bathrooms_meta_query( ) {
1736 + $request_department = $this->get_requested_department();
1737 +
1259 1738
1260 1739 $meta_query = array();
1261 1740
1262 1741 if (
1263 1742 (
1264 - (isset( $_REQUEST['department'] ) && $_REQUEST['department'] == 'residential-sales') ||
1265 - (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' ))
1266 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.
1267 1747 isset( $_REQUEST['minimum_bathrooms'] ) && $_REQUEST['minimum_bathrooms'] != ''
1268 1748 )
1269 1749 {
1270 1750 $meta_query = array(
1271 1751 'key' => '_bathrooms',
1272 - 'value' => ph_clean( $_REQUEST['minimum_bathrooms'] ),
1752 + // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- The public property search reads a filter value and contributes query/meta/taxonomy arguments for the current request only; no persistent state-changing operation is reached. This exact annotation covers only NonceVerification.Recommended; retain all sanitizer and SQL-safety checks.
1753 + 'value' => ph_clean( wp_unslash( $_REQUEST['minimum_bathrooms'] ) ),
1273 1754 'compare' => '>=',
1274 1755 'type' => 'NUMERIC'
1275 1756 );
1276 1757 }
@@ -1284,22 +1765,26 @@
1284 1765 * @access public
1285 1766 * @return array
1286 1767 */
1287 1768 public function maximum_bathrooms_meta_query( ) {
1769 + $request_department = $this->get_requested_department();
1770 +
1288 1771
1289 1772 $meta_query = array();
1290 1773
1291 1774 if (
1292 1775 (
1293 - (isset( $_REQUEST['department'] ) && $_REQUEST['department'] == 'residential-sales') ||
1294 - (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' ))
1295 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.
1296 1780 isset( $_REQUEST['maximum_bathrooms'] ) && $_REQUEST['maximum_bathrooms'] != ''
1297 1781 )
1298 1782 {
1299 1783 $meta_query = array(
1300 1784 'key' => '_bathrooms',
1301 - 'value' => ph_clean( $_REQUEST['maximum_bathrooms'] ),
1785 + // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- The public property search reads a filter value and contributes query/meta/taxonomy arguments for the current request only; no persistent state-changing operation is reached. This exact annotation covers only NonceVerification.Recommended; retain all sanitizer and SQL-safety checks.
1786 + 'value' => ph_clean( wp_unslash( $_REQUEST['maximum_bathrooms'] ) ),
1302 1787 'compare' => '<=',
1303 1788 'type' => 'NUMERIC'
1304 1789 );
1305 1790 }
@@ -1313,22 +1798,26 @@
1313 1798 * @access public
1314 1799 * @return array
1315 1800 */
1316 1801 public function minimum_reception_rooms_meta_query( ) {
1802 + $request_department = $this->get_requested_department();
1803 +
1317 1804
1318 1805 $meta_query = array();
1319 1806
1320 1807 if (
1321 1808 (
1322 - (isset( $_REQUEST['department'] ) && $_REQUEST['department'] == 'residential-sales') ||
1323 - (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' ))
1324 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.
1325 1813 isset( $_REQUEST['minimum_reception_rooms'] ) && $_REQUEST['minimum_reception_rooms'] != ''
1326 1814 )
1327 1815 {
1328 1816 $meta_query = array(
1329 1817 'key' => '_reception_rooms',
1330 - 'value' => ph_clean( $_REQUEST['minimum_reception_rooms'] ),
1818 + // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- The public property search reads a filter value and contributes query/meta/taxonomy arguments for the current request only; no persistent state-changing operation is reached. This exact annotation covers only NonceVerification.Recommended; retain all sanitizer and SQL-safety checks.
1819 + 'value' => ph_clean( wp_unslash( $_REQUEST['minimum_reception_rooms'] ) ),
1331 1820 'compare' => '>=',
1332 1821 'type' => 'NUMERIC'
1333 1822 );
1334 1823 }
@@ -1342,22 +1831,26 @@
1342 1831 * @access public
1343 1832 * @return array
1344 1833 */
1345 1834 public function maximum_reception_rooms_meta_query( ) {
1835 + $request_department = $this->get_requested_department();
1836 +
1346 1837
1347 1838 $meta_query = array();
1348 1839
1349 1840 if (
1350 1841 (
1351 - (isset( $_REQUEST['department'] ) && $_REQUEST['department'] == 'residential-sales') ||
1352 - (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' ))
1353 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.
1354 1846 isset( $_REQUEST['maximum_reception_rooms'] ) && $_REQUEST['maximum_reception_rooms'] != ''
1355 1847 )
1356 1848 {
1357 1849 $meta_query = array(
1358 1850 'key' => '_reception_rooms',
1359 - 'value' => ph_clean( $_REQUEST['maximum_reception_rooms'] ),
1851 + // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- The public property search reads a filter value and contributes query/meta/taxonomy arguments for the current request only; no persistent state-changing operation is reached. This exact annotation covers only NonceVerification.Recommended; retain all sanitizer and SQL-safety checks.
1852 + 'value' => ph_clean( wp_unslash( $_REQUEST['maximum_reception_rooms'] ) ),
1360 1853 'compare' => '<=',
1361 1854 'type' => 'NUMERIC'
1362 1855 );
1363 1856 }
@@ -1371,17 +1864,21 @@
1371 1864 * @access public
1372 1865 * @return array
1373 1866 */
1374 1867 public function available_date_from_meta_query( ) {
1868 + $request_department = $this->get_requested_department();
1869 +
1375 1870
1376 1871 $meta_query = array();
1377 1872
1378 1873 if (
1379 - isset( $_REQUEST['department'] ) && $_REQUEST['department'] == 'residential-lettings' &&
1380 - 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'] != ''
1381 1877 )
1382 1878 {
1383 - $available_date = ph_clean($_REQUEST['available_date_from']);
1879 + // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- The public property search reads a filter value and contributes query/meta/taxonomy arguments for the current request only; no persistent state-changing operation is reached. This exact annotation covers only NonceVerification.Recommended; retain all sanitizer and SQL-safety checks.
1880 + $available_date = ph_clean( wp_unslash( $_REQUEST['available_date_from'] ) );
1384 1881 if ( strpos($available_date, '/') !== FALSE )
1385 1882 {
1386 1883 // it's been provided in the format dd/mm/yyyy
1387 1884 $explode_available_date = explode("/", $available_date);
@@ -1406,27 +1903,40 @@
1406 1903 * @access public
1407 1904 * @return array
1408 1905 */
1409 1906 public function minimum_floor_area_meta_query( ) {
1907 + $request_department = $this->get_requested_department();
1908 +
1410 1909
1411 1910 $meta_query = array();
1412 1911
1413 1912 if (
1414 - isset( $_REQUEST['department'] ) && $_REQUEST['department'] == 'commercial' &&
1913 + isset( $request_department ) && ( $request_department == 'commercial' || ph_get_custom_department_based_on($request_department) == 'commercial' ) &&
1914 + // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- The public property search reads a filter value and contributes query/meta/taxonomy arguments for the current request only; no persistent state-changing operation is reached. This exact annotation covers only NonceVerification.Recommended; retain all sanitizer and SQL-safety checks.
1415 1915 isset( $_REQUEST['minimum_floor_area'] ) && $_REQUEST['minimum_floor_area'] != '' &&
1416 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.
1417 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.
1418 1920 ( isset( $_REQUEST['maximum_floor_area'] ) && $_REQUEST['maximum_floor_area'] == '' )
1419 1921 )
1420 1922 )
1421 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 +
1422 1932 $meta_query = array(
1423 - 'key' => '_floor_area_from_sqft',
1424 - 'value' => ph_clean( $_REQUEST['minimum_floor_area'] ),
1933 + 'key' => '_floor_area_to_sqft',
1934 + 'value' => $value,
1425 1935 'compare' => '>=',
1426 1936 'type' => 'NUMERIC'
1427 - );
1428 - }
1937 + );
1938 + }
1429 1939
1430 1940 return $meta_query;
1431 1941 }
1432 1942
@@ -1436,26 +1946,39 @@
1436 1946 * @access public
1437 1947 * @return array
1438 1948 */
1439 1949 public function maximum_floor_area_meta_query( ) {
1950 + $request_department = $this->get_requested_department();
1951 +
1440 1952
1441 1953 $meta_query = array();
1442 1954
1443 1955 if (
1444 - isset( $_REQUEST['department'] ) && $_REQUEST['department'] == 'commercial' &&
1956 + isset( $request_department ) && ( $request_department == 'commercial' || ph_get_custom_department_based_on($request_department) == 'commercial' ) &&
1957 + // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- The public property search reads a filter value and contributes query/meta/taxonomy arguments for the current request only; no persistent state-changing operation is reached. This exact annotation covers only NonceVerification.Recommended; retain all sanitizer and SQL-safety checks.
1445 1958 isset( $_REQUEST['maximum_floor_area'] ) && $_REQUEST['maximum_floor_area'] != '' &&
1446 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.
1447 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.
1448 1963 ( isset( $_REQUEST['minimum_floor_area'] ) && $_REQUEST['minimum_floor_area'] == '' )
1449 1964 )
1450 1965 )
1451 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 +
1452 1975 $meta_query = array(
1453 - 'key' => '_floor_area_to_sqft',
1454 - 'value' => ph_clean( $_REQUEST['maximum_floor_area'] ),
1976 + 'key' => '_floor_area_from_sqft',
1977 + 'value' => $value,
1455 1978 'compare' => '<=',
1456 1979 'type' => 'NUMERIC'
1457 - );
1980 + );
1458 1981 }
1459 1982
1460 1983 return $meta_query;
1461 1984 }
@@ -1466,26 +1989,41 @@
1466 1989 * @access public
1467 1990 * @return array
1468 1991 */
1469 1992 public function minimum_maximum_floor_area_meta_query( ) {
1993 + $request_department = $this->get_requested_department();
1994 +
1470 1995
1471 1996 $meta_query = array();
1472 1997
1473 1998 if (
1474 - isset( $_REQUEST['department'] ) && $_REQUEST['department'] == 'commercial' &&
1999 + isset( $request_department ) && ( $request_department == 'commercial' || ph_get_custom_department_based_on($request_department) == 'commercial' ) &&
2000 + // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- The public property search reads a filter value and contributes query/meta/taxonomy arguments for the current request only; no persistent state-changing operation is reached. This exact annotation covers only NonceVerification.Recommended; retain all sanitizer and SQL-safety checks.
1475 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.
1476 2003 isset( $_REQUEST['maximum_floor_area'] ) && $_REQUEST['maximum_floor_area'] != ''
1477 2004 )
1478 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 +
1479 2017 $meta_query[] = array(
1480 2018 'key' => '_floor_area_from_sqft',
1481 - 'value' => ph_clean( $_REQUEST['maximum_floor_area'] ),
2019 + 'value' => $maximum_floor_area,
1482 2020 'compare' => '<=',
1483 2021 'type' => 'NUMERIC'
1484 2022 );
1485 2023 $meta_query[] = array(
1486 2024 'key' => '_floor_area_to_sqft',
1487 - 'value' => ph_clean( $_REQUEST['minimum_floor_area'] ),
2025 + 'value' => $minimum_floor_area,
1488 2026 'compare' => '>=',
1489 2027 'type' => 'NUMERIC'
1490 2028 );
1491 2029 }
@@ -1500,17 +2038,21 @@
1500 2038 * @access public
1501 2039 * @return array
1502 2040 */
1503 2041 public function floor_area_range_meta_query( ) {
2042 + $request_department = $this->get_requested_department();
2043 +
1504 2044
1505 2045 $meta_query = array();
1506 2046
1507 2047 if (
1508 - isset( $_REQUEST['department'] ) && $_REQUEST['department'] == 'commercial' &&
1509 - 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'] != ''
1510 2051 )
1511 2052 {
1512 - $explode_floor_area_range = explode("-", ph_clean($_REQUEST['floor_area_range']));
2053 + // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- The public property search reads a filter value and contributes query/meta/taxonomy arguments for the current request only; no persistent state-changing operation is reached. This exact annotation covers only NonceVerification.Recommended; retain all sanitizer and SQL-safety checks.
2054 + $explode_floor_area_range = explode("-", ph_clean( wp_unslash( $_REQUEST['floor_area_range'] ) ));
1513 2055
1514 2056 if ( isset($explode_floor_area_range[0]) && $explode_floor_area_range[0] != '' )
1515 2057 {
1516 2058 $meta_query = array(
@@ -1540,13 +2082,16 @@
1540 2082 * @access public
1541 2083 * @return array
1542 2084 */
1543 2085 public function commercial_for_sale_to_rent_meta_query( ) {
2086 + $request_department = $this->get_requested_department();
2087 +
1544 2088
1545 2089 $meta_query = array();
1546 2090
1547 2091 if (
1548 - isset( $_REQUEST['department'] ) && $_REQUEST['department'] == 'commercial' &&
2092 + isset( $request_department ) && ( $request_department == 'commercial' || ph_get_custom_department_based_on($request_department) == 'commercial' ) &&
2093 + // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- The public property search reads a filter value and contributes query/meta/taxonomy arguments for the current request only; no persistent state-changing operation is reached. This exact annotation covers only NonceVerification.Recommended; retain all sanitizer and SQL-safety checks.
1549 2094 isset( $_REQUEST['commercial_for_sale_to_rent'] ) && $_REQUEST['commercial_for_sale_to_rent'] == 'for_sale'
1550 2095 )
1551 2096 {
1552 2097 $meta_query = array(
@@ -1556,9 +2101,10 @@
1556 2101 );
1557 2102 }
1558 2103
1559 2104 if (
1560 - isset( $_REQUEST['department'] ) && $_REQUEST['department'] == 'commercial' &&
2105 + isset( $request_department ) && ( $request_department == 'commercial' || ph_get_custom_department_based_on($request_department) == 'commercial' ) &&
2106 + // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- The public property search reads a filter value and contributes query/meta/taxonomy arguments for the current request only; no persistent state-changing operation is reached. This exact annotation covers only NonceVerification.Recommended; retain all sanitizer and SQL-safety checks.
1561 2107 isset( $_REQUEST['commercial_for_sale_to_rent'] ) && $_REQUEST['commercial_for_sale_to_rent'] == 'to_rent'
1562 2108 )
1563 2109 {
1564 2110 $meta_query = array(
@@ -1577,13 +2123,16 @@
1577 2123 * @access public
1578 2124 * @return array
1579 2125 */
1580 2126 public function commercial_for_sale_meta_query( ) {
2127 + $request_department = $this->get_requested_department();
2128 +
1581 2129
1582 2130 $meta_query = array();
1583 2131
1584 2132 if (
1585 - isset( $_REQUEST['department'] ) && $_REQUEST['department'] == 'commercial' &&
2133 + isset( $request_department ) && ( $request_department == 'commercial' || ph_get_custom_department_based_on($request_department) == 'commercial' ) &&
2134 + // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- The public property search reads a filter value and contributes query/meta/taxonomy arguments for the current request only; no persistent state-changing operation is reached. This exact annotation covers only NonceVerification.Recommended; retain all sanitizer and SQL-safety checks.
1586 2135 isset( $_REQUEST['commercial_for_sale'] ) && $_REQUEST['commercial_for_sale'] == '1'
1587 2136 )
1588 2137 {
1589 2138 $meta_query = array(
@@ -1602,13 +2151,16 @@
1602 2151 * @access public
1603 2152 * @return array
1604 2153 */
1605 2154 public function commercial_to_rent_meta_query( ) {
2155 + $request_department = $this->get_requested_department();
2156 +
1606 2157
1607 2158 $meta_query = array();
1608 2159
1609 2160 if (
1610 - isset( $_REQUEST['department'] ) && $_REQUEST['department'] == 'commercial' &&
2161 + isset( $request_department ) && ( $request_department == 'commercial' || ph_get_custom_department_based_on($request_department) == 'commercial' ) &&
2162 + // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- The public property search reads a filter value and contributes query/meta/taxonomy arguments for the current request only; no persistent state-changing operation is reached. This exact annotation covers only NonceVerification.Recommended; retain all sanitizer and SQL-safety checks.
1611 2163 isset( $_REQUEST['commercial_to_rent'] ) && $_REQUEST['commercial_to_rent'] == '1'
1612 2164 )
1613 2165 {
1614 2166 $meta_query = array(
@@ -1621,8 +2173,228 @@
1621 2173 return $meta_query;
1622 2174 }
1623 2175
1624 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 + /**
1625 2397 * Returns a meta query to handle property negotiator
1626 2398 *
1627 2399 * @access public
1628 2400 * @return array
@@ -1630,12 +2402,14 @@
1630 2402 public function negotiator_meta_query( ) {
1631 2403
1632 2404 $meta_query = array();
1633 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.
1634 2407 if ( isset( $_REQUEST['negotiator_id'] ) && $_REQUEST['negotiator_id'] != '' )
1635 2408 {
1636 2409 $meta_query = array(
1637 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.
1638 2412 'value' => (int)$_REQUEST['negotiator_id'],
1639 2413 'compare' => '='
1640 2414 );
1641 2415 }
@@ -1653,13 +2427,15 @@
1653 2427 public function office_meta_query( ) {
1654 2428
1655 2429 $meta_query = array();
1656 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.
1657 2432 if ( isset( $_REQUEST['officeID'] ) && $_REQUEST['officeID'] != '' )
1658 2433 {
1659 2434 $meta_query = array(
1660 2435 'key' => '_office_id',
1661 - 'value' => ph_clean( (is_array($_REQUEST['officeID'])) ? $_REQUEST['officeID'] : array( $_REQUEST['officeID'] ) ),
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'] ) ),
1662 2438 'compare' => 'IN'
1663 2439 );
1664 2440 }
1665 2441
@@ -1665,8 +2441,168 @@
1665 2441
1666 2442 return $meta_query;
1667 2443 }
1668 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 +
1669 2605 /**
1670 2606 * Appends taxonomy queries to an array.
1671 2607 * @access public
1672 2608 * @param array $tax_query
@@ -1675,17 +2611,30 @@
1675 2611 public function get_tax_query( $tax_query = array() ) {
1676 2612 if ( ! is_array( $tax_query ) )
1677 2613 $tax_query = array();
1678 2614
2615 + // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Public read-only taxonomy search; this does not authorize a write.
1679 2616 if ( isset($_REQUEST) && !empty($_REQUEST) )
1680 2617 {
2618 +
2619 + // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Public read-only taxonomy search; each value is validated and sanitized below.
1681 2620 foreach ( $_REQUEST as $key => $value )
1682 2621 {
1683 - if ( taxonomy_exists($key) && isset( $_REQUEST[$key] ) && !empty($_REQUEST[$key]) && $this->taxonomy_allowed_for_department( $key ) )
2622 +
2623 + if ( taxonomy_exists($key) && !empty($value) && $this->taxonomy_allowed_for_department( $key ) )
1684 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 +
1685 2633 $tax_query[] = array(
1686 2634 'taxonomy' => $key,
1687 - 'terms' => ph_clean( (is_array($value)) ? $value : array( $value ) )
2635 + 'terms' => ph_clean( wp_unslash( $terms ) ),
2636 + 'operator' => $operator,
1688 2637 );
1689 2638 }
1690 2639 }
1691 2640 }
@@ -1694,15 +2643,22 @@
1694 2643 }
1695 2644
1696 2645 private function taxonomy_allowed_for_department( $taxonomy )
1697 2646 {
1698 - if ( isset( $_REQUEST['department'] ) && $_REQUEST['department'] != '' )
2647 + $request_department = $this->get_requested_department();
2648 +
2649 + if ( isset( $request_department ) && $request_department != '' )
1699 2650 {
1700 - $department = ph_clean($_REQUEST['department']);
2651 + $department = ph_clean($request_department);
1701 2652 }
1702 2653 else
1703 2654 {
1704 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 );
1705 2661 }
1706 2662
1707 2663 switch ( $department )
1708 2664 {