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/ph-form-functions.php +672 -228 1.4.592.3.0 View file →
@@ -1,5 +1,13 @@
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 +
5 +
6 +if ( ! defined( 'ABSPATH' ) ) {
7 + exit;
8 +}
9 +
2 10 /**
3 11 * PropertyHive Form Functions
4 12 *
5 13 * Functions related to drawing forms on the frontend.
@@ -16,8 +24,9 @@
16 24 *
17 25 * @param string $id
18 26 * @return void
19 27 */
28 +// phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedFunctionFound -- Legacy public global helper ph_get_search_form; the established callable name is part of the plugin/extension API and must remain stable.
20 29 function ph_get_search_form( $id = 'default' ) {
21 30
22 31 $form_controls = ph_get_search_form_fields();
23 32
@@ -34,8 +43,9 @@
34 43 $form_controls['department'] = $original_department;
35 44 }
36 45
37 46 // append hidden order and view fields so these are maintained should a new search be performed
47 + // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only public form preferences; these values do not authorize or perform a state change.
38 48 foreach ( $_REQUEST as $key => $value )
39 49 {
40 50 if ( isset($form_controls[$key]) )
41 51 continue;
@@ -45,8 +55,15 @@
45 55
46 56 if ( $key == 'paged' )
47 57 continue;
48 58
59 + if (
60 + ( $key == 'minimum_price' || $key == 'maximum_price' ) && array_key_exists('price_slider', $form_controls) ||
61 + ( $key == 'minimum_rent' || $key == 'maximum_rent' ) && array_key_exists('rent_slider', $form_controls) ||
62 + ( $key == 'minimum_bedrooms' || $key == 'maximum_bedrooms' ) && array_key_exists('bedrooms_slider', $form_controls)
63 + )
64 + continue;
65 +
49 66 // we've received a field that isn't a standard form control so let's store it in a hidden field so it's not lost
50 67 if ( is_array($value) )
51 68 {
52 69 foreach ( $value as $i => $val )
@@ -71,8 +88,9 @@
71 88 * Get default fields to be shown on search forms
72 89 *
73 90 * @return array
74 91 */
92 +// phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedFunctionFound -- Legacy public global helper ph_get_search_form_fields; the established callable name is part of the plugin/extension API and must remain stable.
75 93 function ph_get_search_form_fields()
76 94 {
77 95 $fields = array();
78 96
@@ -93,8 +111,68 @@
93 111 }
94 112 }
95 113 }
96 114
115 + $sales_department_active = false;
116 + if ( array_key_exists('residential-sales', $departments) )
117 + {
118 + $sales_department_active = true;
119 + }
120 + else
121 + {
122 + $custom_departments = ph_get_custom_departments();
123 + if ( !empty($custom_departments) )
124 + {
125 + foreach ( $custom_departments as $key => $department )
126 + {
127 + if ( isset($department['based_on']) && $department['based_on'] == 'residential-sales' )
128 + {
129 + $sales_department_active = true;
130 + }
131 + }
132 + }
133 + }
134 +
135 + $lettings_department_active = false;
136 + if ( array_key_exists('residential-lettings', $departments) )
137 + {
138 + $lettings_department_active = true;
139 + }
140 + else
141 + {
142 + $custom_departments = ph_get_custom_departments();
143 + if ( !empty($custom_departments) )
144 + {
145 + foreach ( $custom_departments as $key => $department )
146 + {
147 + if ( isset($department['based_on']) && $department['based_on'] == 'residential-lettings' )
148 + {
149 + $lettings_department_active = true;
150 + }
151 + }
152 + }
153 + }
154 +
155 + $commercial_department_active = false;
156 + if ( array_key_exists('commercial', $departments) )
157 + {
158 + $commercial_department_active = true;
159 + }
160 + else
161 + {
162 + $custom_departments = ph_get_custom_departments();
163 + if ( !empty($custom_departments) )
164 + {
165 + foreach ( $custom_departments as $key => $department )
166 + {
167 + if ( isset($department['based_on']) && $department['based_on'] == 'commercial' )
168 + {
169 + $commercial_department_active = true;
170 + }
171 + }
172 + }
173 + }
174 +
97 175 $fields['department'] = array(
98 176 'type' => 'radio',
99 177 'options' => $department_options,
100 178 'value' => $default_value
@@ -99,11 +177,11 @@
99 177 'options' => $department_options,
100 178 'value' => $default_value
101 179 );
102 180
103 - if ( array_key_exists('residential-sales', $departments) || array_key_exists('residential-lettings', $departments) )
181 + if ( $sales_department_active || $lettings_department_active )
104 182 {
105 - if ( array_key_exists('residential-sales', $departments) )
183 + if ( $sales_department_active )
106 184 {
107 185 $prices = array(
108 186 '' => __( 'No preference', 'propertyhive' ),
109 187 '100000' => '&pound;100,000',
@@ -132,9 +210,9 @@
132 210 'options' => $prices
133 211 );
134 212 }
135 213
136 - if ( array_key_exists('residential-lettings', $departments) )
214 + if ( $lettings_department_active )
137 215 {
138 216 $prices = array(
139 217 '' => __( 'No preference', 'propertyhive' ),
140 218 '500' => '&pound;500 PCM',
@@ -178,9 +256,9 @@
178 256 'label' => __( 'Type', 'propertyhive' ),
179 257 );
180 258 }
181 259
182 - if ( array_key_exists('commercial', $departments) )
260 + if ( $commercial_department_active )
183 261 {
184 262 $sizes = array(
185 263 '' => __( 'No preference', 'propertyhive' ),
186 264 '250' => '250 sq ft',
@@ -208,45 +286,13 @@
208 286 'before' => '<div class="control control-maximum_floor_area commercial-only">',
209 287 'options' => $sizes
210 288 );
211 289
212 - // Property Type
213 - $options = array( '' => __( 'No preference', 'propertyhive' ) );
214 - $args = array(
215 - 'hide_empty' => false,
216 - 'parent' => 0
217 - );
218 - $terms = get_terms( 'commercial_property_type', $args );
219 -
220 - $selected_value = '';
221 - if ( !empty( $terms ) && !is_wp_error( $terms ) )
222 - {
223 - foreach ($terms as $term)
224 - {
225 - $options[$term->term_id] = $term->name;
226 -
227 - $args = array(
228 - 'hide_empty' => false,
229 - 'parent' => $term->term_id
230 - );
231 - $subterms = get_terms( 'commercial_property_type', $args );
232 -
233 - if ( !empty( $subterms ) && !is_wp_error( $subterms ) )
234 - {
235 - foreach ($subterms as $term)
236 - {
237 - $options[$term->term_id] = '- ' . $term->name;
238 - }
239 - }
240 - }
241 - }
242 -
243 290 $fields['commercial_property_type'] = array(
244 - 'type' => 'select',
291 + 'type' => 'commercial_property_type',
245 292 'show_label' => true,
246 293 'before' => '<div class="control control-commercial_property_type commercial-only">',
247 294 'label' => __( 'Type', 'propertyhive' ),
248 - 'options' => $options
249 295 );
250 296 }
251 297
252 298 return $fields;
@@ -254,17 +300,45 @@
254 300
255 301 /**
256 302 * Main function for drawing property enquiry form.
257 303 *
258 - * @param string $id
304 + * @param string $property_id
259 305 * @return void
260 306 */
261 307 function propertyhive_enquiry_form( $property_id = '' )
262 308 {
309 + global $post;
310 +
263 311 $form_controls = ph_get_property_enquiry_form_fields( $property_id );
264 312
265 - $form_controls = apply_filters( 'propertyhive_property_enquiry_form_fields', $form_controls );
313 + $form_controls = apply_filters( 'propertyhive_property_enquiry_form_fields', $form_controls, $property_id );
266 314
315 + $form_controls['property_id'] = array(
316 + 'type' => 'hidden',
317 + 'value' => ( $property_id != '' ? $property_id : $post->ID )
318 + );
319 +
320 + $utm_fields = array( 'utm_source', 'utm_medium', 'utm_term', 'utm_content', 'utm_campaign', 'gclid', 'fbclid' );
321 + foreach ( $utm_fields as $utm_field )
322 + {
323 + $form_controls[$utm_field] = array(
324 + 'type' => 'hidden',
325 + 'value' =>''
326 + );
327 + }
328 +
329 + if ( get_option( 'propertyhive_property_enquiry_form_disclaimer', '' ) != '' )
330 + {
331 + $disclaimer = wp_kses_post( get_option( 'propertyhive_property_enquiry_form_disclaimer', '' ) );
332 +
333 + $form_controls['disclaimer'] = array(
334 + 'type' => 'checkbox',
335 + 'label' => $disclaimer,
336 + 'label_style' => 'width:100%;',
337 + 'required' => true
338 + );
339 + }
340 +
267 341 ph_get_template( 'global/make-enquiry-form.php',array( 'form_controls' => $form_controls ) );
268 342 }
269 343
270 344 /**
@@ -271,8 +345,9 @@
271 345 * Get default fields to be shown on search forms
272 346 *
273 347 * @return array
274 348 */
349 +// phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedFunctionFound -- Legacy public global helper ph_get_property_enquiry_form_fields; the established callable name is part of the plugin/extension API and must remain stable.
275 350 function ph_get_property_enquiry_form_fields( $property_id = '' )
276 351 {
277 352 global $post;
278 353
@@ -277,16 +352,13 @@
277 352 global $post;
278 353
279 354 $fields = array();
280 355
281 - $fields['property_id'] = array(
282 - 'type' => 'hidden',
283 - 'value' => ( $property_id != '' ? $property_id : $post->ID )
284 - );
285 -
286 356 $fields['name'] = array(
287 357 'type' => 'text',
288 358 'label' => __( 'Full Name', 'propertyhive' ),
359 + 'show_label' => true,
360 + 'before' => '<div class="control control-name">',
289 361 'required' => true
290 362 );
291 363 if ( is_user_logged_in() )
292 364 {
@@ -297,8 +369,10 @@
297 369
298 370 $fields['email_address'] = array(
299 371 'type' => 'email',
300 372 'label' => __( 'Email Address', 'propertyhive' ),
373 + 'show_label' => true,
374 + 'before' => '<div class="control control-email_address">',
301 375 'required' => true
302 376 );
303 377 if ( is_user_logged_in() )
304 378 {
@@ -309,8 +383,10 @@
309 383
310 384 $fields['telephone_number'] = array(
311 385 'type' => 'text',
312 386 'label' => __( 'Number', 'propertyhive' ),
387 + 'show_label' => true,
388 + 'before' => '<div class="control control-telephone_number">',
313 389 'required' => true
314 390 );
315 391
316 392 $fields['message'] = array(
@@ -315,23 +391,13 @@
315 391
316 392 $fields['message'] = array(
317 393 'type' => 'textarea',
318 394 'label' => __( 'Message', 'propertyhive' ),
395 + 'show_label' => true,
396 + 'before' => '<div class="control control-message">',
319 397 'required' => true
320 398 );
321 399
322 - if ( get_option( 'propertyhive_property_enquiry_form_disclaimer', '' ) != '' )
323 - {
324 - $disclaimer = get_option( 'propertyhive_property_enquiry_form_disclaimer', '' );
325 -
326 - $fields['disclaimer'] = array(
327 - 'type' => 'checkbox',
328 - 'label' => $disclaimer,
329 - 'label_style' => 'width:100%;',
330 - 'required' => true
331 - );
332 - }
333 -
334 400 return $fields;
335 401 }
336 402
337 403 /**
@@ -338,8 +404,9 @@
338 404 * Get default fields to be shown on applicant registration forms
339 405 *
340 406 * @return array
341 407 */
408 +// phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedFunctionFound -- Legacy public global helper ph_get_user_details_form_fields; the established callable name is part of the plugin/extension API and must remain stable.
342 409 function ph_get_user_details_form_fields()
343 410 {
344 411 global $post;
345 412
@@ -407,36 +474,13 @@
407 474 * Get default fields to be shown on applicant registration forms
408 475 *
409 476 * @return array
410 477 */
411 -function ph_get_applicant_requirements_form_fields()
478 +// phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedFunctionFound -- Legacy public global helper ph_get_applicant_requirements_form_fields; the established callable name is part of the plugin/extension API and must remain stable.
479 +function ph_get_applicant_requirements_form_fields($applicant_profile = false)
412 480 {
413 481 global $post;
414 482
415 - if ( is_user_logged_in() )
416 - {
417 - $current_user = wp_get_current_user();
418 - $applicant_profile = false;
419 -
420 - if ( $current_user instanceof WP_User )
421 - {
422 - $contact = new PH_Contact( '', $current_user->ID );
423 -
424 - if ( is_array($contact->contact_types) && in_array('applicant', $contact->contact_types) )
425 - {
426 - if (
427 - $contact->applicant_profiles != '' &&
428 - $contact->applicant_profiles > 0 &&
429 - $contact->applicant_profile_0 != '' &&
430 - is_array($contact->applicant_profile_0)
431 - )
432 - {
433 - $applicant_profile = $contact->applicant_profile_0;
434 - }
435 - }
436 - }
437 - }
438 -
439 483 $fields = array();
440 484
441 485 $offices = array();
442 486 $value = '';
@@ -474,34 +518,37 @@
474 518 'value' => $value,
475 519 'options' => $offices
476 520 );
477 521
522 + $value = '';
523 +
524 + $ph_departments = ph_get_departments();
478 525 $departments = array();
479 - $value = '';
480 - if ( get_option( 'propertyhive_active_departments_sales' ) == 'yes' )
526 +
527 + $show_residential_fields = false;
528 + $show_commercial_fields = false;
529 + foreach ( $ph_departments as $key => $department )
481 530 {
482 - $departments['residential-sales'] = __( 'Properties To Buy', 'propertyhive' );
483 - if ($value == '' && (get_option( 'propertyhive_primary_department' ) == 'residential-sales' || get_option( 'propertyhive_primary_department' ) === FALSE) )
531 + if ( get_option( 'propertyhive_active_departments_' . str_replace("residential-", "", $key) ) == 'yes' )
484 532 {
485 - $value = 'residential-sales';
533 + $departments[$key] = $department;
534 + if ($value == '' && (get_option( 'propertyhive_primary_department' ) == $key || get_option( 'propertyhive_primary_department' ) === FALSE) )
535 + {
536 + $value = $key;
537 + }
538 +
539 + if ( in_array($key, array('residential-sales', 'residential-lettings')) || in_array(ph_get_custom_department_based_on($key), array('residential-sales', 'residential-lettings')) )
540 + {
541 + $show_residential_fields = true;
542 + }
543 +
544 + if ( in_array($key, array('commercial')) || in_array(ph_get_custom_department_based_on($key), array('commercial')) )
545 + {
546 + $show_commercial_fields = true;
547 + }
486 548 }
487 549 }
488 - if ( get_option( 'propertyhive_active_departments_lettings' ) == 'yes' )
489 - {
490 - $departments['residential-lettings'] = __( 'Properties For Rent', 'propertyhive' );
491 - if ($value == '' && get_option( 'propertyhive_primary_department' ) == 'residential-lettings')
492 - {
493 - $value = 'residential-lettings';
494 - }
495 - }
496 - if ( get_option( 'propertyhive_active_departments_commercial' ) == 'yes' )
497 - {
498 - $departments['commercial'] = __( 'Commercial Properties', 'propertyhive' );
499 - if ($value == '' && get_option( 'propertyhive_primary_department' ) == 'commercial')
500 - {
501 - $value = 'commercial';
502 - }
503 - }
550 +
504 551 $fields['department'] = array(
505 552 'type' => 'radio',
506 553 'label' => __( 'Looking For', 'propertyhive' ),
507 554 'required' => true,
@@ -517,9 +564,9 @@
517 564 {
518 565 $fields['department']['type'] = 'hidden';
519 566 }
520 567
521 - if ( get_option( 'propertyhive_active_departments_sales' ) == 'yes' || get_option( 'propertyhive_active_departments_lettings' ) == 'yes' )
568 + if ( $show_residential_fields )
522 569 {
523 570 $fields['maximum_price'] = array(
524 571 'type' => 'number',
525 572 'label' => __( 'Maximum Price', 'propertyhive' ),
@@ -559,9 +606,9 @@
559 606 $args = array(
560 607 'hide_empty' => false,
561 608 'parent' => 0
562 609 );
563 - $terms = get_terms( 'property_type', $args );
610 + $terms = get_terms( array_merge( wp_parse_args( $args ), array( 'taxonomy' => 'property_type' ) ) );
564 611
565 612 $options = array();
566 613
567 614 $selected_value = '';
@@ -576,9 +623,9 @@
576 623 $args = array(
577 624 'hide_empty' => false,
578 625 'parent' => $term->term_id
579 626 );
580 - $subterms = get_terms( 'property_type', $args );
627 + $subterms = get_terms( array_merge( wp_parse_args( $args ), array( 'taxonomy' => 'property_type' ) ) );
581 628
582 629 if ( !empty( $subterms ) && !is_wp_error( $subterms ) )
583 630 {
584 631 foreach ($subterms as $term)
@@ -595,19 +642,20 @@
595 642 'type' => 'select',
596 643 'label' => __( 'Property Type', 'propertyhive' ),
597 644 'before' => '<div class="control control-property_type residential-only">',
598 645 'required' => false,
646 + 'multiselect' => true,
599 647 'options' => $options,
600 648 );
601 649
602 650 if ( is_user_logged_in() && isset($applicant_profile['property_types']) && is_array($applicant_profile['property_types']) && !empty($applicant_profile['property_types']) )
603 651 {
604 - $fields['property_type']['value'] = $applicant_profile['property_types'][0];
652 + $fields['property_type']['value'] = $applicant_profile['property_types'];
605 653 }
606 654 }
607 655 }
608 656
609 - if ( get_option( 'propertyhive_active_departments_commercial' ) == 'yes' )
657 + if ( $show_commercial_fields )
610 658 {
611 659 $fields['available_as_sale'] = array(
612 660 'type' => 'checkbox',
613 661 'label' => __( 'For Sale', 'propertyhive' ),
@@ -657,9 +705,9 @@
657 705 $args = array(
658 706 'hide_empty' => false,
659 707 'parent' => 0
660 708 );
661 - $terms = get_terms( 'commercial_property_type', $args );
709 + $terms = get_terms( array_merge( wp_parse_args( $args ), array( 'taxonomy' => 'commercial_property_type' ) ) );
662 710
663 711 $options = array();
664 712
665 713 $selected_value = '';
@@ -674,9 +722,9 @@
674 722 $args = array(
675 723 'hide_empty' => false,
676 724 'parent' => $term->term_id
677 725 );
678 - $subterms = get_terms( 'commercial_property_type', $args );
726 + $subterms = get_terms( array_merge( wp_parse_args( $args ), array( 'taxonomy' => 'commercial_property_type' ) ) );
679 727
680 728 if ( !empty( $subterms ) && !is_wp_error( $subterms ) )
681 729 {
682 730 foreach ($subterms as $term)
@@ -693,63 +741,54 @@
693 741 'type' => 'select',
694 742 'label' => __( 'Property Type', 'propertyhive' ),
695 743 'before' => '<div class="control control-commercial_property_type commercial-only">',
696 744 'required' => false,
745 + 'multiselect' => true,
697 746 'options' => $options,
698 747 );
699 748
700 749 if ( is_user_logged_in() && isset($applicant_profile['commercial_property_types']) && is_array($applicant_profile['commercial_property_types']) && !empty($applicant_profile['commercial_property_types']) )
701 750 {
702 - $fields['commercial_property_type']['value'] = $applicant_profile['commercial_property_types'][0];
751 + $fields['commercial_property_type']['value'] = $applicant_profile['commercial_property_types'];
703 752 }
704 753 }
705 754 }
706 755
707 - $args = array(
708 - 'hide_empty' => false,
709 - 'parent' => 0
710 - );
711 - $terms = get_terms( 'location', $args );
712 -
713 - $options = array();
714 -
715 - $selected_value = '';
716 - if ( !empty( $terms ) && !is_wp_error( $terms ) )
756 + if ( get_option('propertyhive_applicant_locations_type') != 'text' )
717 757 {
718 - $options = array( '' => __( 'All Locations', 'propertyhive' ) );
758 + $args = array(
759 + 'hide_empty' => false,
760 + 'parent' => 0
761 + );
762 + $terms = get_terms( array_merge( wp_parse_args( $args ), array( 'taxonomy' => 'location' ) ) );
719 763
720 - foreach ($terms as $term)
764 + if ( !empty( $terms ) && !is_wp_error( $terms ) )
721 765 {
722 - $options[$term->term_id] = $term->name;
723 -
724 - $args = array(
725 - 'hide_empty' => false,
726 - 'parent' => $term->term_id
766 + $fields['location'] = array(
767 + 'type' => 'location',
768 + 'label' => __( 'Location', 'propertyhive' ),
769 + 'blank_option' => __( 'All Locations', 'propertyhive' ),
770 + 'required' => false,
771 + 'multiselect' => true,
727 772 );
728 - $subterms = get_terms( 'location', $args );
729 773
730 - if ( !empty( $subterms ) && !is_wp_error( $subterms ) )
774 + if ( is_user_logged_in() && isset($applicant_profile['locations']) && is_array($applicant_profile['locations']) && !empty($applicant_profile['locations']) )
731 775 {
732 - foreach ($subterms as $term)
733 - {
734 - $options[$term->term_id] = '- ' . $term->name;
735 - }
776 + $fields['location']['value'] = $applicant_profile['locations'];
736 777 }
737 778 }
738 779 }
739 -
740 - if ( !empty($options) )
780 + else
741 781 {
742 - $fields['location'] = array(
743 - 'type' => 'select',
782 + $fields['location_text'] = array(
783 + 'type' => 'text',
744 784 'label' => __( 'Location', 'propertyhive' ),
745 - 'required' => false,
746 - 'options' => $options,
785 + 'required' => false
747 786 );
748 787
749 - if ( is_user_logged_in() && isset($applicant_profile['locations']) && is_array($applicant_profile['locations']) && !empty($applicant_profile['locations']) )
788 + if ( is_user_logged_in() && isset($applicant_profile['location_text']) && $applicant_profile['location_text'] != '' )
750 789 {
751 - $fields['location']['value'] = $applicant_profile['locations'][0];
790 + $fields['location_text']['value'] = $applicant_profile['location_text'];
752 791 }
753 792 }
754 793
755 794 $fields['additional_requirements'] = array(
@@ -769,8 +808,9 @@
769 808 * Output individual field
770 809 *
771 810 * @return void
772 811 */
812 +// phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedFunctionFound -- Legacy public global helper ph_form_field; the established callable name is part of the plugin/extension API and must remain stable.
773 813 function ph_form_field( $key, $field )
774 814 {
775 815 global $post;
776 816
@@ -783,10 +823,11 @@
783 823 case "date":
784 824 case "number":
785 825 case "password":
786 826 {
827 + $field['id'] = isset( $field['id'] ) ? $field['id'] : $key;
787 828 $field['class'] = isset( $field['class'] ) ? $field['class'] : '';
788 - $field['before'] = isset( $field['before'] ) ? $field['before'] : '<div class="control control-' . $key . '">';
829 + $field['before'] = isset( $field['before'] ) ? $field['before'] : '<div class="control control-' . esc_attr( $key ) . '">';
789 830 $field['after'] = isset( $field['after'] ) ? $field['after'] : '</div>';
790 831 $field['show_label'] = isset( $field['show_label'] ) ? $field['show_label'] : true;
791 832 $field['label'] = isset( $field['label'] ) ? $field['label'] : '';
792 833 $field['placeholder'] = isset( $field['placeholder'] ) ? $field['placeholder'] : ( ( $field['type'] == 'date' ) ? 'dd/mm/yyyy' : '' );
@@ -793,10 +834,12 @@
793 834 $field['required'] = isset( $field['required'] ) ? $field['required'] : false;
794 835 $field['style'] = isset( $field['style'] ) ? $field['style'] : '';
795 836
796 837 $field['value'] = isset( $field['value'] ) ? $field['value'] : '';
838 + // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only public form preferences; these values do not authorize or perform a state change.
797 839 if ( isset( $_GET[$key] ) && ! empty( $_GET[$key] ) )
798 840 {
841 + // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only public form preferences; these values do not authorize or perform a state change.
799 842 $field['value'] = sanitize_text_field( wp_unslash( $_GET[$key] ) );
800 843 }
801 844 else
802 845 {
@@ -824,9 +867,9 @@
824 867
825 868 $output .= '<input
826 869 type="' . esc_attr( $field['type'] ) . '"
827 870 name="' . esc_attr( $key ) . '"
828 - id="' . esc_attr( $key ) . '"
871 + id="' . esc_attr( $field['id'] ) . '"
829 872 value="' . esc_attr( $field['value'] ) . '"
830 873 placeholder="' . esc_attr( $field['placeholder'] ) . '"
831 874 class="' . esc_attr( $field['class'] ) . '"
832 875 style="' . esc_attr( $field['style'] ) . '"
@@ -839,9 +882,9 @@
839 882 }
840 883 case "textarea":
841 884 {
842 885 $field['class'] = isset( $field['class'] ) ? $field['class'] : '';
843 - $field['before'] = isset( $field['before'] ) ? $field['before'] : '<div class="control control-' . $key . '">';
886 + $field['before'] = isset( $field['before'] ) ? $field['before'] : '<div class="control control-' . esc_attr( $key ) . '">';
844 887 $field['after'] = isset( $field['after'] ) ? $field['after'] : '</div>';
845 888 $field['show_label'] = isset( $field['show_label'] ) ? $field['show_label'] : true;
846 889 $field['label'] = isset( $field['label'] ) ? $field['label'] : '';
847 890 $field['placeholder'] = isset( $field['placeholder'] ) ? $field['placeholder'] : '';
@@ -847,10 +890,12 @@
847 890 $field['placeholder'] = isset( $field['placeholder'] ) ? $field['placeholder'] : '';
848 891 $field['required'] = isset( $field['required'] ) ? $field['required'] : false;
849 892
850 893 $field['value'] = isset( $field['value'] ) ? $field['value'] : '';
894 + // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only public form preferences; these values do not authorize or perform a state change.
851 895 if ( isset( $_GET[$key] ) && ! empty( $_GET[$key] ) )
852 896 {
897 + // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only public form preferences; these values do not authorize or perform a state change.
853 898 $field['value'] = sanitize_textarea_field( wp_unslash( $_GET[$key] ) );
854 899 }
855 900 else
856 901 {
@@ -881,9 +926,9 @@
881 926 id="' . esc_attr( $key ) . '"
882 927 placeholder="' . esc_attr( $field['placeholder'] ) . '"
883 928 class="' . esc_attr( $field['class'] ) . '"
884 929 ' . ( ($field['required']) ? 'required' : '' ) . '
885 - >' . esc_attr( $field['value'] ) . '</textarea>';
930 + >' . esc_textarea( $field['value'] ) . '</textarea>';
886 931
887 932 $output .= $field['after'];
888 933
889 934 break;
@@ -890,9 +935,9 @@
890 935 }
891 936 case "checkbox":
892 937 {
893 938 $field['class'] = isset( $field['class'] ) ? $field['class'] : '';
894 - $field['before'] = isset( $field['before'] ) ? $field['before'] : '<div class="control control-' . $key . '">';
939 + $field['before'] = isset( $field['before'] ) ? $field['before'] : '<div class="control control-' . esc_attr( $key ) . '">';
895 940 $field['after'] = isset( $field['after'] ) ? $field['after'] : '</div>';
896 941 $field['show_label'] = isset( $field['show_label'] ) ? $field['show_label'] : true;
897 942 $field['label'] = isset( $field['label'] ) ? $field['label'] : '';
898 943 $field['label_style'] = isset( $field['label_style'] ) ? $field['label_style'] : '';
@@ -897,8 +942,9 @@
897 942 $field['label'] = isset( $field['label'] ) ? $field['label'] : '';
898 943 $field['label_style'] = isset( $field['label_style'] ) ? $field['label_style'] : '';
899 944 $field['value'] = isset( $field['value'] ) ? $field['value'] : 'yes';
900 945 $field['checked'] = isset( $field['checked'] ) ? $field['checked'] : false;
946 + // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only public form preferences; these values do not authorize or perform a state change.
901 947 if ( isset( $_GET[$key] ) && sanitize_text_field(wp_unslash($_GET[$key])) == $field['value'] )
902 948 {
903 949 $field['checked'] = true;
904 950 }
@@ -935,16 +981,23 @@
935 981 }
936 982 case "radio":
937 983 {
938 984 $field['class'] = isset( $field['class'] ) ? $field['class'] : '';
939 - $field['before'] = isset( $field['before'] ) ? $field['before'] : '<div class="control control-' . $key . '">';
985 + $field['before'] = isset( $field['before'] ) ? $field['before'] : '<div class="control control-' . esc_attr( $key ) . '">';
940 986 $field['after'] = isset( $field['after'] ) ? $field['after'] : '</div>';
987 + $field['before_option'] = isset( $field['before_option'] ) ? $field['before_option'] : '<label>';
988 + $field['after_option'] = isset( $field['after_option'] ) ? $field['after_option'] : '</label>';
989 + $field['before_input'] = isset( $field['before_input'] ) ? $field['before_input'] : '';
990 + $field['after_input'] = isset( $field['after_input'] ) ? $field['after_input'] : '';
941 991 $field['show_label'] = isset( $field['show_label'] ) ? $field['show_label'] : false;
942 992 $field['label'] = isset( $field['label'] ) ? $field['label'] : '';
993 + $field['options'] = ( isset( $field['options'] ) && is_array( $field['options'] ) ) ? $field['options'] : array();
943 994
944 995 $field['value'] = isset( $field['value'] ) ? $field['value'] : '';
996 + // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only public form preferences; these values do not authorize or perform a state change.
945 997 if ( isset( $_GET[$key] ) && ! empty( $_GET[$key] ) )
946 998 {
999 + // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only public form preferences; these values do not authorize or perform a state change.
947 1000 $field['value'] = sanitize_text_field(wp_unslash($_GET[$key]));
948 1001 }
949 1002
950 1003 $output .= $field['before'];
@@ -950,20 +1003,33 @@
950 1003 $output .= $field['before'];
951 1004
952 1005 if ($field['show_label'])
953 1006 {
954 - $output .= '<label for="' . esc_attr( $key ) . '">' . $field['label'] . '</label>';
1007 + // get first option as 'for'
1008 + $option_key = '';
1009 + foreach ( $field['options'] as $option_key => $value )
1010 + {
1011 + break;
1012 + }
1013 + $output .= '<label for="' . esc_attr( $key ) . '_' . esc_attr( $option_key ) . '">' . $field['label'] . '</label>';
955 1014 }
956 1015
957 1016 foreach ( $field['options'] as $option_key => $value )
958 1017 {
959 - $output .= '<label><input
1018 + $id = esc_attr( $key ) . '_' . esc_attr( $option_key );
1019 + $output .= str_replace("{id}", $id, $field['before_option']);
1020 + $output .= str_replace("{id}", $id, $field['before_input']);
1021 + $output .= '<input
960 1022 type="' . esc_attr( $field['type'] ) . '"
961 1023 name="' . esc_attr( $key ) . '"
1024 + id="' . $id . '"
962 1025 value="' . esc_attr( $option_key ) . '"
963 1026 class="' . esc_attr( $field['class'] ) . '"
964 1027 ' . checked( esc_attr( $field['value'] ), esc_attr( $option_key ), false ) . '
965 - > ' . esc_html( $value ) . '</label>';
1028 + >';
1029 + $output .= str_replace("{id}", $id, $field['after_input']);
1030 + $output .= ' ' . esc_html( $value );
1031 + $output .= str_replace("{id}", $id, $field['after_option']);
966 1032 }
967 1033
968 1034 $output .= $field['after'];
969 1035
@@ -971,9 +1037,9 @@
971 1037 }
972 1038 case "select":
973 1039 {
974 1040 $field['class'] = isset( $field['class'] ) ? $field['class'] : '';
975 - $field['before'] = isset( $field['before'] ) ? $field['before'] : '<div class="control control-' . $key . '">';
1041 + $field['before'] = isset( $field['before'] ) ? $field['before'] : '<div class="control control-' . esc_attr( $key ) . '">';
976 1042 $field['after'] = isset( $field['after'] ) ? $field['after'] : '</div>';
977 1043 $field['show_label'] = isset( $field['show_label'] ) ? $field['show_label'] : true;
978 1044 $field['label'] = isset( $field['label'] ) ? $field['label'] : '';
979 1045 $field['required'] = isset( $field['required'] ) ? $field['required'] : false;
@@ -985,15 +1051,17 @@
985 1051 wp_enqueue_script( 'multiselect' );
986 1052 }
987 1053
988 1054 $field['value'] = isset( $field['value'] ) ? $field['value'] : '';
1055 + // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only public form preferences; these values do not authorize or perform a state change.
989 1056 if ( isset( $_GET[$key] ) && ! empty( $_GET[$key] ) )
990 1057 {
1058 + // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only public form preferences; these values do not authorize or perform a state change.
991 1059 $field['value'] = sanitize_text_field(wp_unslash($_GET[$key]));
992 1060 }
993 1061 else
994 1062 {
995 - if ( !is_post_type_archive('property') && isset($post->ID) )
1063 + if ( !is_post_type_archive('property') && !is_singular('property') && isset($post->ID) )
996 1064 {
997 1065 $value = get_post_meta( $post->ID, '_' . $key, true );
998 1066 if ( $value != '' )
999 1067 {
@@ -1047,9 +1115,15 @@
1047 1115 $output .= selected( esc_attr( $field['value'] ), esc_attr( $option_key ), false );
1048 1116 }
1049 1117 else
1050 1118 {
1051 - if ( isset($_REQUEST[$key]) && is_array($_REQUEST[$key]) && in_array($option_key, $_REQUEST[$key]) )
1119 + if (
1120 + // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only public form preferences; these values do not authorize or perform a state change.
1121 + ( isset($_REQUEST[$key]) && is_array($_REQUEST[$key]) && in_array($option_key, $_REQUEST[$key]) )
1122 + ||
1123 + // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only public form preferences; these values do not authorize or perform a state change.
1124 + ( !isset($_REQUEST[$key]) && is_array($field['value']) && in_array($option_key, $field['value']) )
1125 + )
1052 1126 {
1053 1127 $output .= ' selected';
1054 1128 }
1055 1129 }
@@ -1066,12 +1140,13 @@
1066 1140 {
1067 1141 $key = 'officeID';
1068 1142
1069 1143 $field['class'] = isset( $field['class'] ) ? $field['class'] : '';
1070 - $field['before'] = isset( $field['before'] ) ? $field['before'] : '<div class="control control-' . $key . '">';
1144 + $field['before'] = isset( $field['before'] ) ? $field['before'] : '<div class="control control-' . esc_attr( $key ) . '">';
1071 1145 $field['after'] = isset( $field['after'] ) ? $field['after'] : '</div>';
1072 1146 $field['show_label'] = isset( $field['show_label'] ) ? $field['show_label'] : true;
1073 1147 $field['label'] = isset( $field['label'] ) ? $field['label'] : '';
1148 + $field['blank_option'] = isset( $field['blank_option'] ) ? $field['blank_option'] : __( 'No preference', 'propertyhive' );
1074 1149 $field['multiselect'] = isset( $field['multiselect'] ) ? $field['multiselect'] : false;
1075 1150
1076 1151 if ( $field['multiselect'] )
1077 1152 {
@@ -1078,10 +1153,12 @@
1078 1153 wp_enqueue_script( 'multiselect' );
1079 1154 }
1080 1155
1081 1156 $field['value'] = isset( $field['value'] ) ? $field['value'] : '';
1157 + // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only public form preferences; these values do not authorize or perform a state change.
1082 1158 if ( isset( $_GET[$key] ) && ! empty( $_GET[$key] ) )
1083 1159 {
1160 + // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only public form preferences; these values do not authorize or perform a state change.
1084 1161 $field['value'] = (int)$_GET[$key];
1085 1162 }
1086 1163
1087 1164 $output .= $field['before'];
@@ -1095,9 +1172,9 @@
1095 1172 name="' . esc_attr( $key ) . ( $field['multiselect'] ? '[]' : '' ) . '"
1096 1173 id="' . esc_attr( $key ) . '"
1097 1174 class="' . esc_attr( $field['class'] ) . ( $field['multiselect'] ? ' ph-form-multiselect' : '' ) . '"
1098 1175 ' . ( $field['multiselect'] ? ' multiple="multiple"' : '' ) . '
1099 - data-blank-option="' . esc_attr( __( 'No preference', 'propertyhive' ) ) . '"
1176 + data-blank-option="' . esc_attr( $field['blank_option'] ) . '"
1100 1177 >';
1101 1178
1102 1179 if ( !$field['multiselect'] )
1103 1180 {
@@ -1103,9 +1180,9 @@
1103 1180 {
1104 1181 $output .= '<option
1105 1182 value=""
1106 1183 ' . selected( esc_attr( $field['value'] ), esc_attr( '' ), false ) . '
1107 - >' . esc_html( __( 'No preference', 'propertyhive' ) ) . '</option>';
1184 + >' . esc_html( $field['blank_option'] ) . '</option>';
1108 1185 }
1109 1186
1110 1187 $args = array(
1111 1188 'post_type' => 'office',
@@ -1128,8 +1205,9 @@
1128 1205 $output .= selected( esc_attr( $field['value'] ), esc_attr( $post->ID ), false );
1129 1206 }
1130 1207 else
1131 1208 {
1209 + // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only public form preferences; these values do not authorize or perform a state change.
1132 1210 if ( isset($_REQUEST[$key]) && is_array($_REQUEST[$key]) && in_array($post->ID, $_REQUEST[$key]) )
1133 1211 {
1134 1212 $output .= ' selected';
1135 1213 }
@@ -1148,16 +1226,18 @@
1148 1226 }
1149 1227 case "country":
1150 1228 {
1151 1229 $field['class'] = isset( $field['class'] ) ? $field['class'] : '';
1152 - $field['before'] = isset( $field['before'] ) ? $field['before'] : '<div class="control control-' . $key . '">';
1230 + $field['before'] = isset( $field['before'] ) ? $field['before'] : '<div class="control control-' . esc_attr( $key ) . '">';
1153 1231 $field['after'] = isset( $field['after'] ) ? $field['after'] : '</div>';
1154 1232 $field['show_label'] = isset( $field['show_label'] ) ? $field['show_label'] : true;
1155 1233 $field['label'] = isset( $field['label'] ) ? $field['label'] : '';
1156 1234
1157 1235 $field['value'] = isset( $field['value'] ) ? $field['value'] : '';
1236 + // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only public form preferences; these values do not authorize or perform a state change.
1158 1237 if ( isset( $_GET[$key] ) && ! empty( $_GET[$key] ) )
1159 1238 {
1239 + // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only public form preferences; these values do not authorize or perform a state change.
1160 1240 $field['value'] = sanitize_text_field(wp_unslash($_GET[$key]));
1161 1241 }
1162 1242
1163 1243 $output .= $field['before'];
@@ -1202,24 +1282,152 @@
1202 1282 $output .= $field['after'];
1203 1283
1204 1284 break;
1205 1285 }
1286 + case "slider":
1287 + {
1288 + wp_enqueue_script('jquery');
1289 + wp_enqueue_script('jquery-ui-core');
1290 + wp_enqueue_script('jquery-ui-slider');
1291 + wp_enqueue_script( 'jquery-touch-punch' );
1292 + wp_enqueue_style( 'jquery-ui-style', PH()->plugin_url() . '/assets/css/jquery-ui/jquery-ui.css', array(), PH_VERSION );
1293 +
1294 + $field['before'] = isset( $field['before'] ) ? $field['before'] : '<div class="control control-' . esc_attr( $key ) . '">';
1295 + $field['after'] = isset( $field['after'] ) ? $field['after'] : '</div>';
1296 + $field['show_label'] = isset( $field['show_label'] ) ? $field['show_label'] : true;
1297 + $field['label'] = isset( $field['label'] ) ? $field['label'] : '';
1298 + $field['min'] = isset( $field['min'] ) ? $field['min'] : '';
1299 + $field['max'] = isset( $field['max'] ) ? $field['max'] : '';
1300 + $field['step'] = isset( $field['step'] ) ? $field['step'] : '1';
1301 +
1302 + $output .= $field['before'];
1303 +
1304 + if ($field['show_label'])
1305 + {
1306 + $output .= '<label for="' . esc_attr( $key ) . '">' . $field['label'];
1307 + $output .= ' - <span id="search-form-slider-value-' . esc_attr( $key ) . '" class="search-form-slider-value search-form-slider-value-' . esc_attr( $key ) . '"></span>';
1308 + $output .= '</label>';
1309 + }
1310 +
1311 + $output .= '<div id="search-form-slider-' . esc_attr( $key ) . '" class="search-form-slider search-form-slider-' . esc_attr( $key ) . '" style="min-width:150px;"></div>';
1312 +
1313 + $field_name = str_replace("_slider", "", $key);
1314 + // Read-only search preferences do not require a nonce.
1315 + // phpcs:ignore WordPress.Security.NonceVerification.Recommended
1316 + $minimum = isset( $_GET['minimum_' . $field_name] ) && is_string( $_GET['minimum_' . $field_name] ) ? sanitize_text_field( wp_unslash( $_GET['minimum_' . $field_name] ) ) : '';
1317 + // phpcs:ignore WordPress.Security.NonceVerification.Recommended
1318 + $maximum = isset( $_GET['maximum_' . $field_name] ) && is_string( $_GET['maximum_' . $field_name] ) ? sanitize_text_field( wp_unslash( $_GET['maximum_' . $field_name] ) ) : '';
1319 + $output .= '<input type="hidden" name="minimum_' . esc_attr( $field_name ) . '" class="min_slider_value-' . esc_attr( $key ) . '" id="min_slider_value-' . esc_attr( $key ) . '" value="' . esc_attr( $minimum ) . '">';
1320 + $output .= '<input type="hidden" name="maximum_' . esc_attr( $field_name ) . '" class="max_slider_value-' . esc_attr( $key ) . '" id="max_slider_value-' . esc_attr( $key ) . '" value="' . esc_attr( $maximum ) . '">';
1321 +
1322 + $output .= $field['after'];
1323 +
1324 + $value = '';
1325 + $prefix = '';
1326 + $suffix = '';
1327 +
1328 + $slider_keys = apply_filters('propertyhive_search_form_currency_slider_keys', [
1329 + 'price_slider',
1330 + 'rent_slider',
1331 + ]);
1332 +
1333 + if ( in_array($key, $slider_keys, true) )
1334 + {
1335 + $prefix = '£';
1336 +
1337 + $search_form_currency = get_option( 'propertyhive_search_form_currency', 'GBP' );
1338 +
1339 + $ph_countries = new PH_Countries();
1340 + $countries = $ph_countries->countries;
1341 +
1342 + foreach ( $countries as $country_code => $country )
1343 + {
1344 + if ( isset($country['currency_code']) && $country['currency_code'] == $search_form_currency )
1345 + {
1346 + if ( $country['currency_prefix'] === true )
1347 + {
1348 + $prefix = $country['currency_symbol'];
1349 + $suffix = '';
1350 + }
1351 + else
1352 + {
1353 + $prefix = '';
1354 + $suffix = $country['currency_symbol'];
1355 + }
1356 + break;
1357 + }
1358 + }
1359 + }
1360 +
1361 + $js_key = wp_json_encode( sanitize_html_class( $key ) );
1362 + $js_prefix = wp_json_encode( html_entity_decode( $prefix, ENT_QUOTES, 'UTF-8' ) );
1363 + $js_suffix = wp_json_encode( html_entity_decode( $suffix, ENT_QUOTES, 'UTF-8' ) );
1364 +
1365 + if ( $field['min'] != '' && $field['max'] != '' )
1366 + {
1367 + $value = 'values: [ ' . ( $minimum !== '' ? (float) $minimum : (float)$field['min'] ) . ', ' . ( $maximum !== '' ? (float) $maximum : (float)$field['max'] ) . ' ],';
1368 + }
1369 +
1370 + $output .= '<script>
1371 + jQuery(document).ready(function()
1372 + {
1373 + var key = ' . $js_key . ';
1374 + var prefix = ' . $js_prefix . ';
1375 + var suffix = ' . $js_suffix . ';
1376 +
1377 + jQuery(".search-form-slider-" + key).each(function(index)
1378 + {
1379 + var $slider = jQuery(this);
1380 +
1381 + $slider.slider({
1382 + range: ' . ( ( $field['min'] != '' && $field['max'] != '' ) ? 'true' : 'false' ) . ',
1383 + step: ' . (float) $field['step'] . ',
1384 + ' . ( $field['min'] != '' ? 'min: ' . (float) $field['min'] . ',' : '' ) . '
1385 + ' . ( $field['max'] != '' ? 'max: ' . (float) $field['max'] . ',' : '' ) . '
1386 + ' . $value . '
1387 + slide: function( event, ui ) {
1388 + var min = ui.values[0].toString().replace(/(\d)(?=(\d{3})+(?!\d))/g, "$1,");
1389 + var max = ui.values[1].toString().replace(/(\d)(?=(\d{3})+(?!\d))/g, "$1,");
1390 +
1391 + $slider.closest("form").find(".search-form-slider-value-" + key).text(
1392 + prefix + min + suffix + " - " + prefix + max + suffix
1393 + );
1394 +
1395 + $slider.closest("form").find(".min_slider_value-" + key).val(ui.values[0]);
1396 + $slider.closest("form").find(".max_slider_value-" + key).val(ui.values[1]);
1397 + }
1398 + });
1399 +
1400 + var initialMin = $slider.slider("values", 0).toString().replace(/(\d)(?=(\d{3})+(?!\d))/g, "$1,");
1401 + var initialMax = $slider.slider("values", 1).toString().replace(/(\d)(?=(\d{3})+(?!\d))/g, "$1,");
1402 +
1403 + $slider.closest("form").find(".search-form-slider-value-" + key).text(
1404 + prefix + initialMin + suffix + " - " + prefix + initialMax + suffix
1405 + );
1406 + });
1407 + });
1408 + </script>';
1409 +
1410 + break;
1411 + }
1206 1412 case "hidden":
1207 1413 {
1208 1414 $field['value'] = isset( $field['value'] ) ? $field['value'] : '';
1209 1415 $field['name'] = isset( $field['name'] ) ? $field['name'] : $key;
1416 + // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only public form preferences; these values do not authorize or perform a state change.
1210 1417 if ( isset( $_GET[$key] ) && ! empty( $_GET[$key] ) )
1211 1418 {
1419 + // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only public form preferences; these values do not authorize or perform a state change.
1212 1420 $field['value'] = sanitize_text_field(wp_unslash($_GET[$key]));
1213 1421 }
1214 1422
1215 - $output .= '<input type="hidden" name="' . esc_attr( $field['name'] ) . '" value="' . $field['value'] . '">';
1423 + $output .= '<input type="hidden" name="' . esc_attr( $field['name'] ) . '" value="' . esc_attr($field['value']) . '">';
1216 1424 break;
1217 1425 }
1218 1426 case "html":
1219 1427 {
1220 1428 $field['html'] = isset( $field['html'] ) ? $field['html'] : '';
1221 - $field['before'] = isset( $field['before'] ) ? $field['before'] : '<div class="control control-' . $key . '">';
1429 + $field['before'] = isset( $field['before'] ) ? $field['before'] : '<div class="control control-' . esc_attr( $key ) . '">';
1222 1430 $field['after'] = isset( $field['after'] ) ? $field['after'] : '</div>';
1223 1431
1224 1432 $output .= $field['before'];
1225 1433 $output .= $field['html'];
@@ -1228,12 +1436,80 @@
1228 1436 break;
1229 1437 }
1230 1438 case "recaptcha":
1231 1439 {
1232 - $field['site_key'] = isset( $field['site_key'] ) ? $field['site_key'] : '';
1440 + $site_key = isset( $field['site_key'] ) && is_string( $field['site_key'] ) ? $field['site_key'] : '';
1441 + // phpcs:ignore PluginCheck.CodeAnalysis.EnqueuedResourceOffloading.OffloadedContent, WordPress.WP.EnqueuedResourceParameters.MissingVersion -- Provider maintains this API endpoint without a plugin version. Required by the configured Google reCAPTCHA service.
1442 + wp_enqueue_script( 'propertyhive-recaptcha', 'https://www.google.com/recaptcha/api.js', array(), null, true );
1443 + $output .= '<div class="g-recaptcha" data-sitekey="' . esc_attr( $site_key ) . '"></div>';
1444 + break;
1445 + }
1446 + case "recaptcha-v3":
1447 + {
1448 + $site_key = isset( $field['site_key'] ) && is_string( $field['site_key'] ) ? $field['site_key'] : '';
1449 + // phpcs:ignore PluginCheck.CodeAnalysis.EnqueuedResourceOffloading.OffloadedContent, WordPress.WP.EnqueuedResourceParameters.MissingVersion -- Provider maintains this API endpoint without a plugin version. Required by the configured Google reCAPTCHA service.
1450 + wp_enqueue_script( 'propertyhive-recaptcha-v3', add_query_arg( 'render', $site_key, 'https://www.google.com/recaptcha/api.js' ), array(), null, true );
1451 + wp_add_inline_script( 'propertyhive-recaptcha-v3',
1452 + 'grecaptcha.ready(function() { grecaptcha.execute(' . wp_json_encode( $site_key, JSON_HEX_TAG | JSON_HEX_AMP | JSON_HEX_APOS | JSON_HEX_QUOT ) . ', {action:"submit"}).then(function(token) { document.querySelectorAll("[name=g-recaptcha-response]").forEach(function(elem) { elem.value = token; }); }); });'
1453 + );
1454 + $output .= '<input type="hidden" id="g-recaptcha-response" name="g-recaptcha-response">';
1455 + break;
1456 + }
1457 + case "hCaptcha":
1458 + {
1459 + $site_key = isset( $field['site_key'] ) && is_string( $field['site_key'] ) ? $field['site_key'] : '';
1460 + // phpcs:ignore PluginCheck.CodeAnalysis.EnqueuedResourceOffloading.OffloadedContent, WordPress.WP.EnqueuedResourceParameters.MissingVersion -- Provider maintains this API endpoint without a plugin version. Required by the configured hCaptcha service.
1461 + wp_enqueue_script( 'propertyhive-hcaptcha', 'https://js.hcaptcha.com/1/api.js', array(), null, true );
1462 + $output .= '<div class="h-captcha" data-sitekey="' . esc_attr( $site_key ) . '"></div>';
1463 + break;
1464 + }
1465 + case "turnstile":
1466 + {
1467 + $site_key = isset( $field['site_key'] ) && is_string( $field['site_key'] ) ? $field['site_key'] : '';
1468 + $output .= '<div class="turnstile" data-sitekey="' . esc_attr( $site_key ) . '"></div>';
1469 + break;
1470 + }
1471 + case "daterange":
1472 + {
1473 + wp_enqueue_script( 'moment' );
1474 + wp_enqueue_script( 'daterangepicker.js', PH()->plugin_url() . '/assets/js/daterangepicker/daterangepicker.js', array( 'jquery', 'moment' ), '3.1.0', true );
1475 + wp_enqueue_style( 'daterangepicker.css', PH()->plugin_url() . '/assets/js/daterangepicker/daterangepicker.css', array(), '3.1.0' );
1233 1476
1234 - $output .= '<script src="https://www.google.com/recaptcha/api.js"></script>
1235 - <div class="g-recaptcha" data-sitekey="' . $field['site_key'] . '"></div>';
1477 + $field['before'] = isset( $field['before'] ) ? $field['before'] : '<div class="control control-' . esc_attr( $key ) . '">';
1478 + $field['after'] = isset( $field['after'] ) ? $field['after'] : '</div>';
1479 +
1480 + $field['show_label'] = isset( $field['show_label'] ) ? $field['show_label'] : true;
1481 + $field['label'] = isset( $field['label'] ) ? $field['label'] : '';
1482 +
1483 + $field['value'] = isset( $field['value'] ) ? $field['value'] : '';
1484 + $field['style'] = isset( $field['style'] ) ? $field['style'] : '';
1485 + $field['class'] = isset( $field['class'] ) ? $field['class'] : '';
1486 + $field['placeholder'] = isset( $field['placeholder'] ) ? $field['placeholder'] : '';
1487 +
1488 + // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only public form preferences; these values do not authorize or perform a state change.
1489 + if ( isset( $_GET[$key] ) && ! empty( $_GET[$key] ) )
1490 + {
1491 + // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only public form preferences; these values do not authorize or perform a state change.
1492 + $field['value'] = sanitize_text_field(wp_unslash($_GET[$key]));
1493 + }
1494 +
1495 + $output .= $field['before'];
1496 +
1497 + if ($field['show_label'])
1498 + {
1499 + $output .= '<label for="' . esc_attr( $key ) . '">' . $field['label'] . '</label>';
1500 + }
1501 +
1502 + $output .= '<input type="text" autocomplete="off"
1503 + name="' . esc_attr( $key ) . '"
1504 + id="' . esc_attr( $key ) . '"
1505 + value="' . esc_attr( $field['value'] ) . '"
1506 + style="' . esc_attr( $field['style'] ) . '"
1507 + class="' . esc_attr( $field['class'] ) . '"
1508 + placeholder="' . esc_attr( $field['placeholder'] ) . '"
1509 + />';
1510 + $output .= $field['after'];
1511 +
1236 1512 break;
1237 1513 }
1238 1514 default:
1239 1515 {
@@ -1239,56 +1515,83 @@
1239 1515 {
1240 1516 if ( taxonomy_exists($field['type']) )
1241 1517 {
1242 1518 $field['class'] = isset( $field['class'] ) ? $field['class'] : '';
1243 - $field['before'] = isset( $field['before'] ) ? $field['before'] : '<div class="control control-' . $key . '">';
1519 + $field['before'] = isset( $field['before'] ) ? $field['before'] : '<div class="control control-' . esc_attr( $key ) . '">';
1244 1520 $field['after'] = isset( $field['after'] ) ? $field['after'] : '</div>';
1245 1521 $field['show_label'] = isset( $field['show_label'] ) ? $field['show_label'] : true;
1246 1522 $field['label'] = isset( $field['label'] ) ? $field['label'] : '';
1247 1523 $field['blank_option'] = isset( $field['blank_option'] ) ? $field['blank_option'] : __( 'No preference', 'propertyhive' );
1248 1524 $field['parent_terms_only'] = isset( $field['parent_terms_only'] ) ? $field['parent_terms_only'] : false;
1525 + $field['hide_empty'] = isset( $field['hide_empty'] ) ? $field['hide_empty'] : false;
1249 1526 $field['multiselect'] = isset( $field['multiselect'] ) ? $field['multiselect'] : false;
1527 + $field['dynamic_population'] = ( isset( $field['dynamic_population'] ) && $field['type'] == 'location' && $field['parent_terms_only'] === false && $field['multiselect'] === false ) ? $field['dynamic_population'] : false; // only applies to location
1250 1528
1251 1529 if ( $field['multiselect'] )
1252 1530 {
1253 1531 wp_enqueue_script( 'multiselect' );
1254 1532 }
1255 -
1256 - $field['value'] = isset( $field['value'] ) ? $field['value'] : '';
1257 - if ( isset( $_GET[$key] ) && ! empty( $_GET[$key] ) )
1258 - {
1259 - $field['value'] = sanitize_text_field(wp_unslash($_GET[$key]));
1260 - }
1261 -
1262 - $output .= $field['before'];
1263 -
1264 - if ($field['show_label'])
1265 - {
1266 - $output .= '<label for="' . esc_attr( $key ) . '">' . $field['label'] . '</label>';
1267 - }
1268 -
1269 - $output .= '<select
1270 - name="' . esc_attr( $key ) . ( $field['multiselect'] ? '[]' : '' ) . '"
1271 - id="' . esc_attr( $key ) . '"
1272 - class="' . esc_attr( $field['class'] ) . ( $field['multiselect'] ? ' ph-form-multiselect' : '' ) . '"
1273 - ' . ( $field['multiselect'] ? ' multiple="multiple"' : '' ) . '
1274 - data-blank-option="' . esc_attr($field['blank_option']) . '"
1275 - >';
1276 -
1277 - $options = array( '' => $field['blank_option'] );
1533 +
1534 + $options = array(
1535 + '' => array(
1536 + 'label' => $field['blank_option'],
1537 + 'parent' => 0
1538 + )
1539 + );
1278 1540 $args = array(
1279 - 'hide_empty' => false,
1541 + 'hide_empty' => $field['hide_empty'],
1280 1542 'parent' => 0
1281 1543 );
1282 - $terms = get_terms( $field['type'], $args );
1544 + $args = apply_filters( 'propertyhive_form_taxonomy_terms_args', $args, $field );
1545 + $terms = get_terms( array_merge( wp_parse_args( $args ), array( 'taxonomy' => $field['type'] ) ) );
1283 1546
1284 - $selected_value = '';
1547 + $levels_of_taxonomy = 1;
1285 1548 if ( !empty( $terms ) && !is_wp_error( $terms ) )
1286 1549 {
1287 1550 foreach ($terms as $term)
1288 1551 {
1289 - $options[$term->term_id] = $term->name;
1552 + if ( isset($field['hide_empty']) && $field['hide_empty'] === true )
1553 + {
1554 + $empty_check_args = array(
1555 + 'post_type' => 'property',
1556 + 'posts_per_page' => 1,
1557 + 'fields' => 'ids',
1558 + 'no_found_rows' => true,
1559 + // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_query -- Existence-only published-property check for one taxonomy term and on-market meta; fetches one ID without row counts, with existing extension query filter retained.
1560 + 'meta_query' => array(
1561 + array(
1562 + 'key' => '_on_market',
1563 + 'value' => 'yes',
1564 + ),
1565 + ),
1566 + // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_tax_query -- Existence-only published-property check for one taxonomy term and on-market meta; fetches one ID without row counts, with existing extension query filter retained.
1567 + 'tax_query' => array(
1568 + array(
1569 + 'taxonomy' => $field['type'],
1570 + 'field' => 'term_id',
1571 + 'terms' => $term->term_id,
1572 + ),
1573 + ),
1574 + );
1290 1575
1576 + $empty_check_args = apply_filters( 'propertyhive_taxonomy_hide_empty_args', $empty_check_args, $field, $term->term_id );
1577 +
1578 + $empty_check_query = new WP_Query( $empty_check_args );
1579 +
1580 + if ( !$empty_check_query->have_posts() )
1581 + {
1582 + continue;
1583 + }
1584 + }
1585 +
1586 + $options[(int)$term->term_id] = array(
1587 + 'label' => $term->name,
1588 + 'parent' => 0
1589 + );
1590 +
1591 + if ($field['dynamic_population'])
1592 + $levels_of_taxonomy = max(1, $levels_of_taxonomy);
1593 +
1291 1594 if (
1292 1595 !isset($field['parent_terms_only'])
1293 1596 ||
1294 1597 (
@@ -1297,30 +1600,114 @@
1297 1600 )
1298 1601 )
1299 1602 {
1300 1603 $args = array(
1301 - 'hide_empty' => false,
1302 - 'parent' => $term->term_id
1604 + 'hide_empty' => $field['hide_empty'],
1605 + 'parent' => $term->term_id,
1303 1606 );
1304 - $subterms = get_terms( $field['type'], $args );
1607 + $args = apply_filters( 'propertyhive_form_taxonomy_terms_args', $args, $field );
1608 + $args = apply_filters( 'propertyhive_form_taxonomy_subterms_args', $args, $field );
1609 + $subterms = get_terms( array_merge( wp_parse_args( $args ), array( 'taxonomy' => $field['type'] ) ) );
1305 1610
1306 1611 if ( !empty( $subterms ) && !is_wp_error( $subterms ) )
1307 1612 {
1308 - foreach ($subterms as $term)
1613 + foreach ($subterms as $subterm)
1309 1614 {
1310 - $options[$term->term_id] = '- ' . $term->name;
1615 + if ( isset($field['hide_empty']) && $field['hide_empty'] === true )
1616 + {
1617 + $empty_check_args = array(
1618 + 'post_type' => 'property',
1619 + 'posts_per_page' => 1,
1620 + 'fields' => 'ids',
1621 + 'no_found_rows' => true,
1622 + // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_query -- Existence-only published-property check for one taxonomy term and on-market meta; fetches one ID without row counts, with existing extension query filter retained.
1623 + 'meta_query' => array(
1624 + array(
1625 + 'key' => '_on_market',
1626 + 'value' => 'yes',
1627 + ),
1628 + ),
1629 + // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_tax_query -- Existence-only published-property check for one taxonomy term and on-market meta; fetches one ID without row counts, with existing extension query filter retained.
1630 + 'tax_query' => array(
1631 + array(
1632 + 'taxonomy' => $field['type'],
1633 + 'field' => 'term_id',
1634 + 'terms' => $subterm->term_id,
1635 + ),
1636 + ),
1637 + );
1311 1638
1639 + $empty_check_args = apply_filters( 'propertyhive_taxonomy_hide_empty_args', $empty_check_args, $field, $subterm->term_id );
1640 +
1641 + $empty_check_query = new WP_Query( $empty_check_args );
1642 +
1643 + if ( !$empty_check_query->have_posts() )
1644 + {
1645 + continue;
1646 + }
1647 + }
1648 +
1649 + $options[(int)$subterm->term_id] = array(
1650 + 'label' => ( !$field['dynamic_population'] ? '- ' : '' ) . $subterm->name,
1651 + 'parent' => (int)$term->term_id,
1652 + );
1653 +
1654 + if ($field['dynamic_population'])
1655 + $levels_of_taxonomy = max(2, $levels_of_taxonomy);
1656 +
1312 1657 $args = array(
1313 - 'hide_empty' => false,
1314 - 'parent' => $term->term_id
1658 + 'hide_empty' => $field['hide_empty'],
1659 + 'parent' => (int)$subterm->term_id
1315 1660 );
1316 - $subsubterms = get_terms( $field['type'], $args );
1661 + $args = apply_filters( 'propertyhive_form_taxonomy_terms_args', $args, $field );
1662 + $args = apply_filters( 'propertyhive_form_taxonomy_subsubterms_args', $args, $field );
1663 + $subsubterms = get_terms( array_merge( wp_parse_args( $args ), array( 'taxonomy' => $field['type'] ) ) );
1317 1664
1318 1665 if ( !empty( $subsubterms ) && !is_wp_error( $subsubterms ) )
1319 1666 {
1320 - foreach ($subsubterms as $term)
1667 + foreach ($subsubterms as $subsubterm)
1321 1668 {
1322 - $options[$term->term_id] = '- ' . $term->name;
1669 + if ( isset($field['hide_empty']) && $field['hide_empty'] === true )
1670 + {
1671 + $empty_check_args = array(
1672 + 'post_type' => 'property',
1673 + 'posts_per_page' => 1,
1674 + 'fields' => 'ids',
1675 + 'no_found_rows' => true,
1676 + // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_query -- Existence-only published-property check for one taxonomy term and on-market meta; fetches one ID without row counts, with existing extension query filter retained.
1677 + 'meta_query' => array(
1678 + array(
1679 + 'key' => '_on_market',
1680 + 'value' => 'yes',
1681 + ),
1682 + ),
1683 + // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_tax_query -- Existence-only published-property check for one taxonomy term and on-market meta; fetches one ID without row counts, with existing extension query filter retained.
1684 + 'tax_query' => array(
1685 + array(
1686 + 'taxonomy' => $field['type'],
1687 + 'field' => 'term_id',
1688 + 'terms' => $subsubterm->term_id,
1689 + ),
1690 + ),
1691 + );
1692 +
1693 + $empty_check_args = apply_filters( 'propertyhive_taxonomy_hide_empty_args', $empty_check_args, $field, $subsubterm->term_id );
1694 +
1695 + $empty_check_query = new WP_Query( $empty_check_args );
1696 +
1697 + if ( !$empty_check_query->have_posts() )
1698 + {
1699 + continue;
1700 + }
1701 + }
1702 +
1703 + $options[(int)$subsubterm->term_id] = array(
1704 + 'label' => ( !$field['dynamic_population'] ? '- - ' : '' ) . $subsubterm->name,
1705 + 'parent' => (int)$subterm->term_id,
1706 + );
1707 +
1708 + if ($field['dynamic_population'])
1709 + $levels_of_taxonomy = max(3, $levels_of_taxonomy);
1323 1710 }
1324 1711 }
1325 1712 }
1326 1713 }
@@ -1327,51 +1714,107 @@
1327 1714 }
1328 1715 }
1329 1716 }
1330 1717
1331 - foreach ( $options as $option_key => $value )
1718 + if ( $field['dynamic_population'] )
1332 1719 {
1333 - if ( $field['multiselect'] && $option_key == '' )
1720 + wp_localize_script( 'propertyhive_dynamic_population', 'propertyhive_dynamic_population_params', array(
1721 + 'options' => $options,
1722 + 'levels_of_taxonomy' => $levels_of_taxonomy,
1723 + // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only public form preferences; these values do not authorize or perform a state change.
1724 + 'value' => isset($_GET[$field['type']]) ? ph_clean( wp_unslash( $_GET[$field['type']] ) ) : '',
1725 + // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only public form preferences; these values do not authorize or perform a state change.
1726 + 'other_values' => ( isset($_GET['other_' . $field['type']]) && is_array($_GET['other_' . $field['type']]) && !empty($_GET['other_' . $field['type']]) ) ? array_filter( array_filter( ph_clean( wp_unslash( $_GET['other_' . $field['type']] ) ) ), 'is_scalar' ) : array(),
1727 + 'taxonomy' => $field['type'],
1728 + ) );
1729 + wp_enqueue_script( 'propertyhive_dynamic_population' );
1730 + }
1731 +
1732 + $field['value'] = isset( $field['value'] ) ? $field['value'] : '';
1733 + // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only public form preferences; these values do not authorize or perform a state change.
1734 + if ( isset( $_GET[$key] ) && ! empty( $_GET[$key] ) )
1735 + {
1736 + // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only public form preferences; these values do not authorize or perform a state change.
1737 + $field['value'] = sanitize_text_field(wp_unslash($_GET[$key]));
1738 + }
1739 +
1740 + for ( $level_i = 1; $level_i <= $levels_of_taxonomy; ++$level_i )
1741 + {
1742 + $output .= $field['before'];
1743 +
1744 + if ($field['show_label'])
1334 1745 {
1335 - // Skip because we don't want a blank option in the multiselect. Instead use $value as the placeholder
1336 - continue;
1746 + $output .= '<label for="' . esc_attr( $key ) . '">' . $field['label'] . '</label>';
1337 1747 }
1338 1748
1339 - $output .= '<option
1340 - value="' . esc_attr( $option_key ) . '"';
1341 - if ( !$field['multiselect'] )
1749 + $output .= '<select
1750 + name="' . esc_attr( $key ) . ( $field['multiselect'] ? '[]' : '' ) . '"
1751 + id="' . esc_attr( $key ) . '"
1752 + class="' . esc_attr( $field['class'] ) . ( $field['multiselect'] ? ' ph-form-multiselect' : '' ) . '"
1753 + ' . ( $field['multiselect'] ? ' multiple="multiple"' : '' ) .
1754 + ( $field['dynamic_population'] ? ' data-dynamic-population-level="' . $level_i . '"' : '' ) .
1755 + ( ( $field['dynamic_population'] && $level_i > 1 ) ? ' disabled' : '' ) . '
1756 + data-blank-option="' . esc_attr($field['blank_option']) . '"
1757 + >';
1758 +
1759 + if ( $level_i == 1 )
1342 1760 {
1343 - $output .= selected( esc_attr( $field['value'] ), esc_attr( $option_key ), false );
1344 - }
1345 - else
1346 - {
1347 - if ( isset($_REQUEST[$key]) && is_array($_REQUEST[$key]) && in_array($option_key, $_REQUEST[$key]) )
1761 + foreach ( $options as $option_key => $value )
1348 1762 {
1349 - $output .= ' selected';
1763 + if ( $field['multiselect'] && $option_key == '' )
1764 + {
1765 + // Skip because we don't want a blank option in the multiselect. Instead use $value as the placeholder
1766 + continue;
1767 + }
1768 +
1769 + if ( $field['dynamic_population'] && $value['parent'] != '0' )
1770 + {
1771 + continue;
1772 + }
1773 +
1774 + $output .= '<option
1775 + value="' . esc_attr( $option_key ) . '"';
1776 + if ( !$field['multiselect'] )
1777 + {
1778 + $output .= selected( esc_attr( $field['value'] ), esc_attr( $option_key ), false );
1779 + }
1780 + else
1781 + {
1782 + // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only public form preferences; these values do not authorize or perform a state change.
1783 + if ( isset($_REQUEST[$key]) && is_array($_REQUEST[$key]) && in_array($option_key, $_REQUEST[$key]) )
1784 + {
1785 + $output .= ' selected';
1786 + }
1787 + elseif ( is_array($field['value']) && in_array($option_key, $field['value']) )
1788 + {
1789 + $output .= ' selected';
1790 + }
1791 + }
1792 + $output .= '>' . esc_html( $value['label'] ) . '</option>';
1350 1793 }
1351 1794 }
1352 - $output .= '>' . esc_html( $value ) . '</option>';
1353 - }
1354 1795
1355 - $output .= '</select>';
1796 + $output .= '</select>';
1356 1797
1357 - $output .= $field['after'];
1798 + $output .= $field['after'];
1358 1799
1359 - if ( $field['type'] == 'availability' )
1360 - {
1361 - $availability_departments = get_option( 'propertyhive_availability_departments', array() );
1362 - if ( !is_array($availability_departments) ) { $availability_departments = array(); }
1800 + if ( $field['type'] == 'availability' )
1801 + {
1802 + $availability_departments = get_option( 'propertyhive_availability_departments', array() );
1803 + if ( !is_array($availability_departments) ) { $availability_departments = array(); }
1363 1804
1364 - if ( !empty($availability_departments) )
1365 - {
1805 + if ( !empty($availability_departments) )
1806 + {
1366 1807 ?>
1367 1808 <script>
1368 -var selected_availability = '<?php echo ( isset($_REQUEST[$key]) && $_REQUEST[$key] != '' ? (int)$_REQUEST[$key] : '' ); ?>';
1369 -var availability_departments = <?php echo json_encode($availability_departments); ?>;
1370 -var availabilities = <?php echo json_encode($options); ?>;
1371 -var availabilities_order = <?php echo json_encode(array_keys($options)); ?>;
1809 +<?php // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only initial availability selection, reduced to an integer before JavaScript output. ?>
1810 +var selected_availability = '<?php echo ( isset($_REQUEST[$key]) && is_scalar( $_REQUEST[$key] ) && $_REQUEST[$key] != '' ? (int)$_REQUEST[$key] : '' ); ?>';
1811 +var availability_departments = <?php echo wp_json_encode( $availability_departments , JSON_HEX_TAG | JSON_HEX_AMP | JSON_HEX_APOS | JSON_HEX_QUOT ); ?>;
1812 +var availabilities = <?php echo wp_json_encode( $options , JSON_HEX_TAG | JSON_HEX_AMP | JSON_HEX_APOS | JSON_HEX_QUOT ); ?>;
1813 +var availabilities_order = <?php echo wp_json_encode( array_keys($options) , JSON_HEX_TAG | JSON_HEX_AMP | JSON_HEX_APOS | JSON_HEX_QUOT ); ?>;
1372 1814 </script>
1373 1815 <?php
1816 + }
1374 1817 }
1375 1818 }
1376 1819 }
1377 1820 }
@@ -1376,6 +1819,7 @@
1376 1819 }
1377 1820 }
1378 1821 }
1379 1822
1823 + // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- Control values and attributes are escaped while assembling the markup above; labels/wrappers and the HTML control are trusted PHP presentation arguments (saved frontend labels are sanitized before extension filters).
1380 1824 echo $output;
1381 -}
1825 +}