PluginProbe
Property Hive / 2.3.1
Property Hive v2.3.1
2.3.1 2.3.0 2.2.6 2.2.5 2.2.4 2.2.3 2.2.2 1.4.46 1.4.47 1.4.48 1.4.49 1.4.5 1.4.50 1.4.51 1.4.52 1.4.53 1.4.54 1.4.55 1.4.56 1.4.57 1.4.58 1.4.59 1.4.6 1.4.60 1.4.61 All 261 releases
← All changes | includes/admin/class-ph-admin-post-types.php +302 -143 2.2.22.3.1 View file →
@@ -1,5 +1,8 @@
1 1 <?php
2 +// phpcs:set WordPress.Security.ValidatedSanitizedInput customSanitizingFunctions[] ph_clean
3 +// ph_clean() recursively sanitizes text; presence, shape and unslashing checks remain separate.
4 +
2 5 /**
3 6 * Post Types Admin
4 7 *
5 8 * @author PropertyHive
@@ -14,8 +17,9 @@
14 17
15 18 /**
16 19 * PH_Admin_Post_Types Class
17 20 */
21 +// phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedClassFound -- Legacy public global class PH_Admin_Post_Types; preserving the existing PH_* class name is required for plugin and extension compatibility.
18 22 class PH_Admin_Post_Types {
19 23
20 24 /**
21 25 * Constructor
@@ -53,8 +57,32 @@
53 57
54 58 add_filter( 'post_row_actions', array( $this, 'modify_post_row_actions_for_archived' ), 10, 2 );
55 59 }
56 60
61 + /**
62 + * Read one scalar admin query value after WordPress unslashes and sanitizes it.
63 + *
64 + * Admin list filters are read-only, but their values still flow into markup and
65 + * query arguments. Returning an empty value for arrays keeps scalar filters
66 + * from accidentally accepting a malformed request while preserving the
67 + * existing empty-filter behaviour.
68 + *
69 + * @param string $key Query-string key.
70 + * @return string
71 + */
72 + private function get_admin_query_value( $key ) {
73 + // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only admin list display or query; no state change.
74 + if ( ! isset( $_GET[ $key ] ) || ! is_scalar( $_GET[ $key ] ) ) {
75 + return '';
76 + }
77 +
78 + // phpcs:ignore WordPress.Security.NonceVerification.Recommended, WordPress.Security.ValidatedSanitizedInput.MissingUnslash, WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- Read-only admin list value is copied, unslashed immediately below, and sanitized before use; the sniffer reports the source assignment instead of the sanitization boundary.
79 + $raw_value = $_GET[ $key ];
80 + $raw_value = wp_unslash( (string) $raw_value );
81 +
82 + return sanitize_text_field( $raw_value );
83 + }
84 +
57 85 public function handle_bulk_action_archive_and_unarchive($redirect_to, $doaction, $post_ids)
58 86 {
59 87 if ($doaction === 'move_to_archive')
60 88 {
@@ -202,9 +230,9 @@
202 230
203 231 $post_id = isset($_GET['post']) ? intval($_GET['post']) : 0;
204 232 $post_type = get_post_type($post_id);
205 233
206 - if ( !wp_verify_nonce($_GET['_wpnonce'], 'archive-post_' . $post_id) )
234 + if ( !wp_verify_nonce( ( isset( $_GET['_wpnonce'] ) && is_string( $_GET['_wpnonce'] ) ) ? sanitize_text_field( wp_unslash( $_GET['_wpnonce'] ) ) : '', 'archive-post_' . $post_id) )
207 235 {
208 236 wp_die(esc_html(__('Security check failed.', 'propertyhive')));
209 237 }
210 238
@@ -226,9 +254,9 @@
226 254 wp_die(esc_html(__('An error occurred while archiving the post.', 'propertyhive')));
227 255 }
228 256
229 257 // Redirect to the main list of contacts
230 - wp_redirect(admin_url('edit.php?post_type=' . $post_type));
258 + wp_safe_redirect(admin_url('edit.php?post_type=' . $post_type));
231 259 exit;
232 260 }
233 261
234 262 public function handle_unarchive_action()
@@ -239,9 +267,9 @@
239 267
240 268 $post_id = isset($_GET['post']) ? intval($_GET['post']) : 0;
241 269 $post_type = get_post_type($post_id);
242 270
243 - if ( !wp_verify_nonce($_GET['_wpnonce'], 'unarchive-post_' . $post_id) )
271 + if ( !wp_verify_nonce( ( isset( $_GET['_wpnonce'] ) && is_string( $_GET['_wpnonce'] ) ) ? sanitize_text_field( wp_unslash( $_GET['_wpnonce'] ) ) : '', 'unarchive-post_' . $post_id) )
244 272 {
245 273 wp_die(esc_html(__('Security check failed.', 'propertyhive')));
246 274 }
247 275
@@ -265,13 +293,13 @@
265 293
266 294 // Redirect to the main list of contacts
267 295 if ( isset($_GET['return']) && $_GET['return'] === 'archive' )
268 296 {
269 - wp_redirect(admin_url('edit.php?post_status=archive&post_type=' . get_post_type($post_id)));
297 + wp_safe_redirect(admin_url('edit.php?post_status=archive&post_type=' . get_post_type($post_id)));
270 298 }
271 299 else
272 300 {
273 - wp_redirect(admin_url('edit.php?post_type=' . get_post_type($post_id)));
301 + wp_safe_redirect(admin_url('edit.php?post_type=' . get_post_type($post_id)));
274 302 }
275 303 exit;
276 304 }
277 305
@@ -304,19 +332,24 @@
304 332 global $post, $post_ID;
305 333
306 334 $messages['property'] = array(
307 335 0 => '', // Unused. Messages start at index 1.
308 - 1 => sprintf( __( 'Property updated. <a href="%s">View Property</a>', 'propertyhive' ), esc_url( get_permalink($post_ID) ) ),
336 + /* translators: %s: URL to view the property */
337 + 1 => sprintf( __( 'Property updated. <a href="%s">View property</a>', 'propertyhive' ), esc_url( get_permalink($post_ID) ) ),
309 338 2 => __( 'Custom field updated.', 'propertyhive' ),
310 339 3 => __( 'Custom field deleted.', 'propertyhive' ),
311 340 4 => __( 'Property updated.', 'propertyhive' ),
312 - 5 => isset($_GET['revision']) ? sprintf( __( 'Property restored to revision from %s', 'propertyhive' ), wp_post_revision_title( (int) $_GET['revision'], false ) ) : false,
313 - 6 => sprintf( __( 'Property published. <a href="%s">View Property</a>', 'propertyhive' ), esc_url( get_permalink($post_ID) ) ),
341 + 5 => __( 'Revision restored.', 'propertyhive' ),
342 + /* translators: %s: URL to view the property */
343 + 6 => sprintf( __( 'Property published. <a href="%s">View property</a>', 'propertyhive' ), esc_url( get_permalink($post_ID) ) ),
314 344 7 => __( 'Property saved.', 'propertyhive' ),
315 - 8 => sprintf( __( 'Property submitted. <a target="_blank" href="%s">Preview Property</a>', 'propertyhive' ), esc_url( add_query_arg( 'preview', 'true', get_permalink($post_ID) ) ) ),
316 - 9 => sprintf( __( 'Property scheduled for: <strong>%1$s</strong>. <a target="_blank" href="%2$s">Preview Property</a>', 'propertyhive' ),
345 + /* translators: %s: URL to preview the property */
346 + 8 => sprintf( __( 'Property submitted. <a target="_blank" href="%s">Preview property</a>', 'propertyhive' ), esc_url( add_query_arg( 'preview', 'true', get_permalink($post_ID) ) ) ),
347 + /* translators: 1: formatted date, 2: URL to preview the property */
348 + 9 => sprintf( __( 'Property scheduled for: <strong>%1$s</strong>. <a target="_blank" href="%2$s">Preview property</a>', 'propertyhive' ),
317 349 date_i18n( __( 'M j, Y @ G:i', 'propertyhive' ), strtotime( $post->post_date ) ), esc_url( get_permalink($post_ID) ) ),
318 - 10 => sprintf( __( 'Property draft updated. <a target="_blank" href="%s">Preview Property</a>', 'propertyhive' ), esc_url( add_query_arg( 'preview', 'true', get_permalink($post_ID) ) ) ),
350 + /* translators: %s: URL to preview the property */
351 + 10 => sprintf( __( 'Property draft updated. <a target="_blank" href="%s">Preview property</a>', 'propertyhive' ), esc_url( add_query_arg( 'preview', 'true', get_permalink($post_ID) ) ) ),
319 352 );
320 353
321 354 $messages['contact'] = array(
322 355 0 => '', // Unused. Messages start at index 1.
@@ -323,12 +356,13 @@
323 356 1 => __( 'Contact updated.', 'propertyhive' ),
324 357 2 => __( 'Custom field updated.', 'propertyhive' ),
325 358 3 => __( 'Custom field deleted.', 'propertyhive' ),
326 359 4 => __( 'Contact updated.', 'propertyhive' ),
327 - 5 => isset($_GET['revision']) ? sprintf( __( 'Contact restored to revision from %s', 'propertyhive' ), wp_post_revision_title( (int) $_GET['revision'], false ) ) : false,
360 + 5 => __( 'Revision restored.', 'propertyhive' ),
328 361 6 => __( 'Contact published.', 'propertyhive' ),
329 362 7 => __( 'Contact saved.', 'propertyhive' ),
330 363 8 => __( 'Contact submitted.', 'propertyhive' ),
364 + /* translators: 1: formatted date */
331 365 9 => sprintf( __( 'Contact scheduled for: <strong>%1$s</strong>.', 'propertyhive' ), date_i18n( __( 'M j, Y @ G:i', 'propertyhive' ), strtotime( $post->post_date ) )),
332 366 10 => __( 'Contact draft updated.', 'propertyhive' ),
333 367 );
334 368
@@ -337,12 +371,13 @@
337 371 1 => __( 'Office updated.', 'propertyhive' ),
338 372 2 => __( 'Custom field updated.', 'propertyhive' ),
339 373 3 => __( 'Custom field deleted.', 'propertyhive' ),
340 374 4 => __( 'Office updated.', 'propertyhive' ),
341 - 5 => isset($_GET['revision']) ? sprintf( __( 'Office restored to revision from %s', 'propertyhive' ), wp_post_revision_title( (int) $_GET['revision'], false ) ) : false,
375 + 5 => __( 'Revision restored.', 'propertyhive' ),
342 376 6 => sprintf( __( 'Office published.', 'propertyhive' ), esc_url( get_permalink($post_ID) ) ),
343 377 7 => __( 'Office saved.', 'propertyhive' ),
344 378 8 => sprintf( __( 'Office submitted.', 'propertyhive' ), esc_url( add_query_arg( 'preview', 'true', get_permalink($post_ID) ) ) ),
379 + /* translators: 1: formatted date */
345 380 9 => sprintf( __( 'Office scheduled for: <strong>%1$s</strong>.', 'propertyhive' ),
346 381 date_i18n( __( 'M j, Y @ G:i', 'propertyhive' ), strtotime( $post->post_date ) ), esc_url( get_permalink($post_ID) ) ),
347 382 10 => sprintf( __( 'Office draft updated. ', 'propertyhive' ), esc_url( add_query_arg( 'preview', 'true', get_permalink($post_ID) ) ) ),
348 383 );
@@ -352,12 +387,13 @@
352 387 1 => sprintf( __( 'Enquiry updated.', 'propertyhive' ), esc_url( get_permalink($post_ID) ) ),
353 388 2 => __( 'Custom field updated.', 'propertyhive' ),
354 389 3 => __( 'Custom field deleted.', 'propertyhive' ),
355 390 4 => __( 'Enquiry updated.', 'propertyhive' ),
356 - 5 => isset($_GET['revision']) ? sprintf( __( 'Enquiry restored to revision from %s', 'propertyhive' ), wp_post_revision_title( (int) $_GET['revision'], false ) ) : false,
391 + 5 => __( 'Revision restored.', 'propertyhive' ),
357 392 6 => sprintf( __( 'Enquiry published.', 'propertyhive' ), esc_url( get_permalink($post_ID) ) ),
358 393 7 => __( 'Enquiry saved.', 'propertyhive' ),
359 394 8 => sprintf( __( 'Enquiry submitted.', 'propertyhive' ), esc_url( add_query_arg( 'preview', 'true', get_permalink($post_ID) ) ) ),
395 + /* translators: 1: formatted date */
360 396 9 => sprintf( __( 'Enquiry scheduled for: <strong>%1$s</strong>.', 'propertyhive' ),
361 397 date_i18n( __( 'M j, Y @ G:i', 'propertyhive' ), strtotime( $post->post_date ) ), esc_url( get_permalink($post_ID) ) ),
362 398 10 => sprintf( __( 'Enquiry draft updated.', 'propertyhive' ), esc_url( add_query_arg( 'preview', 'true', get_permalink($post_ID) ) ) ),
363 399 );
@@ -448,8 +484,9 @@
448 484 $output .= $this->property_location_filter();
449 485 $output .= $this->property_office_filter();
450 486 $output .= $this->negotiator_filter();
451 487
488 + // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- Built-in controls escape their text and attributes before this trusted PHP filter adds complete HTML controls.
452 489 echo apply_filters( 'propertyhive_property_filters', $output );
453 490 }
454 491
455 492 /**
@@ -459,14 +496,16 @@
459 496 global $wp_query;
460 497
461 498 $departments = ph_get_departments();
462 499
463 - $selected_department = isset( $_GET['_department'] ) && in_array( $_GET['_department'], array_keys($departments) ) ? $_GET['_department'] : '';
500 + // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only admin list display or query; no state change.
501 + $requested_value = isset( $_GET['_department'] ) && is_string( $_GET['_department'] ) ? sanitize_text_field( wp_unslash( $_GET['_department'] ) ) : '';
502 + $selected_department = array_key_exists( $requested_value, $departments ) ? $requested_value : '';
464 503
465 504 // Department filtering
466 505 $output = '<select name="_department" id="dropdown_property_department">';
467 506
468 - $output .= '<option value="">' . __( 'All Departments', 'propertyhive' ) . '</option>';
507 + $output .= '<option value="">' . esc_html__( 'All Departments', 'propertyhive' ) . '</option>';
469 508
470 509 foreach ( $departments as $key => $value )
471 510 {
472 511 if ( get_option( 'propertyhive_active_departments_' . str_replace("residential-", "", $key) ) == 'yes' )
@@ -490,9 +529,9 @@
490 529
491 530 // Department filtering
492 531 $output = '<select name="_office_id" id="dropdown_property_office_id">';
493 532
494 - $output .= '<option value="">' . __( 'All Offices', 'propertyhive' ) . '</option>';
533 + $output .= '<option value="">' . esc_html__( 'All Offices', 'propertyhive' ) . '</option>';
495 534
496 535 $args = array(
497 536 'post_type' => 'office',
498 537 'nopaging' => true,
@@ -507,10 +546,12 @@
507 546 {
508 547 $office_query->the_post();
509 548
510 549 $output .= '<option value="' . esc_attr($post->ID) . '"';
550 + // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only admin list display or query; no state change.
511 551 if ( isset( $_GET['_office_id'] ) && ! empty( $_GET['_office_id'] ) )
512 552 {
553 + // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only admin list display or query; no state change.
513 554 $output .= selected( $post->ID, (int)$_GET['_office_id'], false );
514 555 }
515 556 $output .= '>' . esc_html(get_the_title()) . '</option>';
516 557 }
@@ -530,11 +571,13 @@
530 571
531 572 return wp_dropdown_users(array(
532 573 'name' => '_negotiator_id',
533 574 'id' => 'dropdown_property_negotiator_id',
534 - 'show_option_all' => __( 'All Negotiators', 'propertyhive' ),
575 + 'show_option_all' => esc_html__( 'All Negotiators', 'propertyhive' ),
576 + // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only admin list display or query; no state change.
535 577 'selected' => empty( $_GET['_negotiator_id'] ) ? '' : (int)$_GET['_negotiator_id'],
536 578 'echo' => false,
579 + // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound -- Legacy Property Negotiator compatibility filter; existing role filters depend on this exact public hook name.
537 580 'role__not_in' => apply_filters( 'property_negotiator_exclude_roles', array('property_hive_contact', 'subscriber') )
538 581 ));
539 582 }
540 583
@@ -542,9 +585,10 @@
542 585 * Show a date range selector
543 586 */
544 587 public function date_range_filter() {
545 588
546 - $date_range_label = empty( $_GET['_date_range_label'] ) ? __( 'Any Time', 'propertyhive' ) : $_GET['_date_range_label'];
589 + $date_range_label = $this->get_admin_query_value( '_date_range_label' );
590 + $date_range_label = empty( $date_range_label ) ? __( 'Any Time', 'propertyhive' ) : $date_range_label;
547 591
548 592 // The date picker doesn't have a concept of 'Any Time', so valid dates must be used
549 593 // I've used the last and first date of the month (reversed) as it's a range that is not selectable, but is within the current month
550 594 // If I used an already labelled date range (e.g. 'Today'), it would show as 'Today' when selected
@@ -549,10 +593,12 @@
549 593 // I've used the last and first date of the month (reversed) as it's a range that is not selectable, but is within the current month
550 594 // If I used an already labelled date range (e.g. 'Today'), it would show as 'Today' when selected
551 595 // If I use a nearby date range (e.g. 'Yesterday'), if someone actually selected that range it would show as 'Any Time'
552 596 // If I use a unlikely date range (e.g. 01-01-1970 - 31-12-2070), the custom date range picker would open showing Jan 1970.
553 - $date_range_from = empty( $_GET['_date_range_from'] ) ? date('Y-m-d', strtotime('last day of this month')) : $_GET['_date_range_from'];
554 - $date_range_to = empty( $_GET['_date_range_to'] ) ? date('Y-m-d', strtotime('first day of this month')) : $_GET['_date_range_to'];
597 + $date_range_from = $this->get_admin_query_value( '_date_range_from' );
598 + $date_range_from = empty( $date_range_from ) ? gmdate('Y-m-d', strtotime('last day of this month')) : $date_range_from;
599 + $date_range_to = $this->get_admin_query_value( '_date_range_to' );
600 + $date_range_to = empty( $date_range_to ) ? gmdate('Y-m-d', strtotime('first day of this month')) : $date_range_to;
555 601
556 602 return "
557 603 <select name='_date_range_label' id='date_range' style='max-width:25rem;'>
558 604 <option selected>" . esc_html($date_range_label) . "</option>
@@ -575,9 +621,9 @@
575 621 $args = array(
576 622 'hide_empty' => false,
577 623 'parent' => 0
578 624 );
579 - $terms = get_terms( 'location', $args );
625 + $terms = get_terms( array_merge( wp_parse_args( $args ), array( 'taxonomy' => 'location' ) ) );
580 626
581 627 if ( !empty( $terms ) && !is_wp_error( $terms ) )
582 628 {
583 629 foreach ($terms as $term)
@@ -587,9 +633,9 @@
587 633 $args = array(
588 634 'hide_empty' => false,
589 635 'parent' => $term->term_id
590 636 );
591 - $subterms = get_terms( 'location', $args );
637 + $subterms = get_terms( array_merge( wp_parse_args( $args ), array( 'taxonomy' => 'location' ) ) );
592 638
593 639 if ( !empty( $subterms ) && !is_wp_error( $subterms ) )
594 640 {
595 641 foreach ($subterms as $term)
@@ -599,9 +645,9 @@
599 645 $args = array(
600 646 'hide_empty' => false,
601 647 'parent' => $term->term_id
602 648 );
603 - $subsubterms = get_terms( 'location', $args );
649 + $subsubterms = get_terms( array_merge( wp_parse_args( $args ), array( 'taxonomy' => 'location' ) ) );
604 650
605 651 if ( !empty( $subsubterms ) && !is_wp_error( $subsubterms ) )
606 652 {
607 653 foreach ($subsubterms as $term)
@@ -620,10 +666,12 @@
620 666 {
621 667 foreach ( $options as $value => $label )
622 668 {
623 669 $output .= '<option value="' . esc_attr($value) . '"';
670 + // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only admin list display or query; no state change.
624 671 if ( isset( $_GET['_location_id'] ) && ! empty( $_GET['_location_id'] ) )
625 672 {
673 + // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only admin list display or query; no state change.
626 674 $output .= selected( $value, (int)$_GET['_location_id'], false );
627 675 }
628 676 $output .= '>' . esc_html($label) . '</option>';
629 677 }
@@ -647,9 +695,9 @@
647 695 $args = array(
648 696 'hide_empty' => false,
649 697 'parent' => 0
650 698 );
651 - $terms = get_terms( 'availability', $args );
699 + $terms = get_terms( array_merge( wp_parse_args( $args ), array( 'taxonomy' => 'availability' ) ) );
652 700
653 701 if ( !empty( $terms ) && !is_wp_error( $terms ) )
654 702 {
655 703 foreach ($terms as $term)
@@ -664,10 +712,12 @@
664 712 {
665 713 foreach ( $options as $value => $label )
666 714 {
667 715 $output .= '<option value="' . esc_attr($value) . '"';
716 + // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only admin list display or query; no state change.
668 717 if ( isset( $_GET['_availability_id'] ) && ! empty( $_GET['_availability_id'] ) )
669 718 {
719 + // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only admin list display or query; no state change.
670 720 $output .= selected( $value, (int)$_GET['_availability_id'], false );
671 721 }
672 722 $output .= '>' . esc_html($label) . '</option>';
673 723 }
@@ -686,9 +736,9 @@
686 736
687 737 // Availability filtering
688 738 $output = '<select name="_marketing" id="dropdown_property_marketing">';
689 739
690 - $output .= '<option value="">' . __( 'All Marketing Statuses', 'propertyhive' ) . '</option>';
740 + $output .= '<option value="">' . esc_html__( 'All Marketing Statuses', 'propertyhive' ) . '</option>';
691 741
692 742 $options = array(
693 743 'on_market' => __( 'On Market Only', 'propertyhive' ),
694 744 'off_market' => __( 'Not On Market Only', 'propertyhive' ),
@@ -698,9 +748,9 @@
698 748 $args = array(
699 749 'hide_empty' => false,
700 750 'parent' => 0
701 751 );
702 - $terms = get_terms( 'marketing_flag', $args );
752 + $terms = get_terms( array_merge( wp_parse_args( $args ), array( 'taxonomy' => 'marketing_flag' ) ) );
703 753
704 754 if ( !empty( $terms ) && !is_wp_error( $terms ) )
705 755 {
706 756 foreach ($terms as $term)
@@ -709,15 +759,16 @@
709 759 }
710 760 }
711 761
712 762 $options = apply_filters( 'propertyhive_property_filter_marketing_options', $options );
763 + $selected_marketing = $this->get_admin_query_value( '_marketing' );
713 764
714 765 foreach ( $options as $key => $value )
715 766 {
716 767 $output .= '<option value="' . esc_attr($key) . '"';
717 - if ( isset( $_GET['_marketing'] ) && ! empty( $_GET['_marketing'] ) )
768 + if ( ! empty( $selected_marketing ) )
718 769 {
719 - $output .= selected( $key, sanitize_text_field($_GET['_marketing']), false );
770 + $output .= selected( $key, $selected_marketing, false );
720 771 }
721 772 $output .= '>' . esc_html($value) . '</option>';
722 773 }
723 774
@@ -731,9 +782,11 @@
731 782 */
732 783 public function contact_filters() {
733 784 global $wp_query;
734 785
735 - $selected_contact_type = isset( $_GET['_contact_type'] ) && in_array( $_GET['_contact_type'], array( 'owner', 'potentialowner', 'applicant', 'hotapplicant', 'thirdparty' ) ) ? ph_clean($_GET['_contact_type']) : '';
786 + // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only admin list display or query; no state change.
787 + $requested_value = isset( $_GET['_contact_type'] ) && is_string( $_GET['_contact_type'] ) ? sanitize_text_field( wp_unslash( $_GET['_contact_type'] ) ) : '';
788 + $selected_contact_type = in_array( $requested_value, array( 'owner', 'potentialowner', 'applicant', 'hotapplicant', 'thirdparty' ), true ) ? $requested_value : '';
736 789
737 790 // Type filtering
738 791 $options = array();
739 792
@@ -787,8 +840,9 @@
787 840 }
788 841
789 842 $output .= $this->date_range_filter('Date Created');
790 843
844 + // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- Built-in controls escape their text and attributes before this trusted PHP filter adds complete HTML controls.
791 845 echo apply_filters( 'propertyhive_contact_filters', $output );
792 846 }
793 847
794 848 /**
@@ -805,8 +859,9 @@
805 859 $output .= $this->enquiry_source_filter();
806 860 $output .= $this->enquiry_office_filter();
807 861 $output .= $this->enquiry_negotiator_filter();
808 862
863 + // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- Built-in controls escape their text and attributes before this trusted PHP filter adds complete HTML controls.
809 864 echo apply_filters( 'propertyhive_enquiry_filters', $output );
810 865 }
811 866
812 867 /**
@@ -814,9 +869,11 @@
814 869 */
815 870 public function enquiry_status_filter() {
816 871 global $wp_query;
817 872
818 - $selected_status = isset( $_GET['_status'] ) && in_array( $_GET['_status'], array( 'all', 'open', 'closed' ) ) ? $_GET['_status'] : '';
873 + // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only admin list display or query; no state change.
874 + $requested_value = isset( $_GET['_status'] ) && is_string( $_GET['_status'] ) ? sanitize_text_field( wp_unslash( $_GET['_status'] ) ) : '';
875 + $selected_status = in_array( $requested_value, array( 'all', 'open', 'closed' ), true ) ? $requested_value : '';
819 876
820 877 // Status filtering
821 878 $output = '<select name="_status" id="dropdown_enquiry_status">
822 879 <option value="all"' . selected( 'all', $selected_status, false ) . '>All</option>';
@@ -825,8 +882,9 @@
825 882
826 883 foreach ( $enquiry_statuses as $status => $display_status )
827 884 {
828 885 $output .= '<option value="' . esc_attr($status) . '"';
886 + // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only admin list display or query; no state change.
829 887 if ( $status == $selected_status || ( $status == 'open' && ( !isset($_GET['_status']) || empty($_GET['_status']) ) ) )
830 888 {
831 889 $output .= ' selected';
832 890 }
@@ -855,19 +913,20 @@
855 913 asort($sources);
856 914
857 915 // Status filtering
858 916 $output = '<select name="_source" id="dropdown_enquiry_source">';
917 + $selected_source = $this->get_admin_query_value( '_source' );
859 918
860 - $output .= '<option value="">' . __( 'Show all sources', 'propertyhive' ) . '</option>';
919 + $output .= '<option value="">' . esc_html__( 'Show all sources', 'propertyhive' ) . '</option>';
861 920
862 921 foreach ( $sources as $key => $value )
863 922 {
864 923 $output .= '<option value="' . esc_attr($key) . '"';
865 - if ( isset( $_GET['_source'] ) && ! empty( $_GET['_source'] ) )
924 + if ( ! empty( $selected_source ) )
866 925 {
867 - $output .= selected( $key, sanitize_text_field($_GET['_source']), false );
926 + $output .= selected( $key, $selected_source, false );
868 927 }
869 - $output .= '>' . esc_html(__( $value, 'propertyhive' )) . '</option>';
928 + $output .= '>' . esc_html( $value ) . '</option>';
870 929 }
871 930
872 931 $output .= '</select>';
873 932
@@ -882,9 +941,9 @@
882 941
883 942 // Department filtering
884 943 $output = '<select name="_office_id" id="dropdown_enquiry_office_id">';
885 944
886 - $output .= '<option value="">' . __( 'All Offices', 'propertyhive' ) . '</option>';
945 + $output .= '<option value="">' . esc_html__( 'All Offices', 'propertyhive' ) . '</option>';
887 946
888 947 $args = array(
889 948 'post_type' => 'office',
890 949 'nopaging' => true,
@@ -899,10 +958,12 @@
899 958 {
900 959 $office_query->the_post();
901 960
902 961 $output .= '<option value="' . esc_attr($post->ID) . '"';
962 + // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only admin list display or query; no state change.
903 963 if ( isset( $_GET['_office_id'] ) && ! empty( $_GET['_office_id'] ) )
904 964 {
965 + // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only admin list display or query; no state change.
905 966 $output .= selected( $post->ID, (int)$_GET['_office_id'], false );
906 967 }
907 968 $output .= '>' . esc_html(get_the_title()) . '</option>';
908 969 }
@@ -921,11 +982,13 @@
921 982 public function enquiry_negotiator_filter() {
922 983 return wp_dropdown_users(array(
923 984 'name' => '_negotiator_id',
924 985 'id' => 'dropdown_enquiry_negotiator_id',
925 - 'show_option_all' => __( 'All Negotiators', 'propertyhive' ),
986 + 'show_option_all' => esc_html__( 'All Negotiators', 'propertyhive' ),
987 + // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only admin list display or query; no state change.
926 988 'selected' => empty( $_GET['_negotiator_id'] ) ? '' : (int)$_GET['_negotiator_id'],
927 989 'echo' => false,
990 + // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound -- Legacy Property Negotiator compatibility filter; existing role filters depend on this exact public hook name.
928 991 'role__not_in' => apply_filters( 'property_negotiator_exclude_roles', array('property_hive_contact', 'subscriber') )
929 992 ));
930 993 }
931 994
@@ -940,8 +1003,9 @@
940 1003 $output .= $this->appraisal_status_filter();
941 1004 $output .= $this->negotiator_filter();
942 1005 $output .= $this->date_range_filter();
943 1006
1007 + // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- Built-in controls escape their text and attributes before this trusted PHP filter adds complete HTML controls.
944 1008 echo apply_filters( 'propertyhive_appraisal_filters', $output );
945 1009 }
946 1010
947 1011 /**
@@ -949,14 +1013,16 @@
949 1013 */
950 1014 public function appraisal_status_filter() {
951 1015 global $wp_query;
952 1016
953 - $selected_status = isset( $_GET['_status'] ) && in_array( $_GET['_status'], array( 'pending', 'carried_out', 'won', 'lost', 'instructed', 'cancelled' ) ) ? ph_clean($_GET['_status']) : '';
1017 + // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only admin list display or query; no state change.
1018 + $requested_value = isset( $_GET['_status'] ) && is_string( $_GET['_status'] ) ? sanitize_text_field( wp_unslash( $_GET['_status'] ) ) : '';
1019 + $selected_status = in_array( $requested_value, array( 'pending', 'carried_out', 'won', 'lost', 'instructed', 'cancelled' ), true ) ? $requested_value : '';
954 1020
955 1021 // Status filtering
956 1022 $output = '<select name="_status" id="dropdown_appraisal_status">';
957 1023
958 - $output .= '<option value="">' . __( 'All Statuses', 'propertyhive' ) . '</option>';
1024 + $output .= '<option value="">' . esc_html__( 'All Statuses', 'propertyhive' ) . '</option>';
959 1025
960 1026 $output .= '<option value="pending"';
961 1027 $output .= selected( 'pending', $selected_status, false );
962 1028 $output .= '>' . esc_html(__( 'Pending', 'propertyhive' )) . '</option>';
@@ -999,8 +1065,9 @@
999 1065 $output .= $this->property_office_filter();
1000 1066 $output .= $this->negotiator_filter();
1001 1067 $output .= $this->date_range_filter();
1002 1068
1069 + // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- Built-in controls escape their text and attributes before this trusted PHP filter adds complete HTML controls.
1003 1070 echo apply_filters( 'propertyhive_viewing_filters', $output );
1004 1071 }
1005 1072
1006 1073 /**
@@ -1008,14 +1075,16 @@
1008 1075 */
1009 1076 public function viewing_status_filter() {
1010 1077 global $wp_query;
1011 1078
1012 - $selected_status = isset( $_GET['_status'] ) && in_array( $_GET['_status'], array( 'pending', 'confirmed', 'unconfirmed', 'carried_out', 'awaiting_feedback', 'feedback_passed_on', 'feedback_not_passed_on', 'cancelled', 'no_show' ) ) ? ph_clean($_GET['_status']) : '';
1079 + // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only admin list display or query; no state change.
1080 + $requested_value = isset( $_GET['_status'] ) && is_string( $_GET['_status'] ) ? sanitize_text_field( wp_unslash( $_GET['_status'] ) ) : '';
1081 + $selected_status = in_array( $requested_value, array( 'pending', 'confirmed', 'unconfirmed', 'carried_out', 'awaiting_feedback', 'feedback_passed_on', 'feedback_not_passed_on', 'cancelled', 'no_show' ), true ) ? $requested_value : '';
1013 1082
1014 1083 // Status filtering
1015 1084 $output = '<select name="_status" id="dropdown_viewing_status">';
1016 1085
1017 - $output .= '<option value="">' . __( 'All Statuses', 'propertyhive' ) . '</option>';
1086 + $output .= '<option value="">' . esc_html__( 'All Statuses', 'propertyhive' ) . '</option>';
1018 1087
1019 1088 $viewing_statuses = ph_get_viewing_statuses();
1020 1089
1021 1090 foreach ( $viewing_statuses as $status => $display_status )
@@ -1033,8 +1102,9 @@
1033 1102
1034 1103 public function refresh_property_office_filtering( $query ) {
1035 1104 remove_filter('posts_join', array( $this, 'filter_by_property_office') );
1036 1105
1106 + // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only admin list display or query; no state change.
1037 1107 if ( ! empty( $_GET['_office_id'] ) && in_array( $query->query['post_type'], array(
1038 1108 'viewing',
1039 1109 'offer',
1040 1110 'sale',
@@ -1046,12 +1116,15 @@
1046 1116
1047 1117 public function filter_by_property_office($query) {
1048 1118 global $wpdb;
1049 1119
1120 + // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only office filtering; no state change.
1121 + $office_id = isset( $_GET['_office_id'] ) && is_scalar( $_GET['_office_id'] ) ? absint( $_GET['_office_id'] ) : 0;
1122 +
1050 1123 return $query . '
1051 1124 INNER JOIN ' . $wpdb->postmeta . ' AS property_meta ON property_meta.post_id = ' . $wpdb->posts . '.ID AND property_meta.meta_key = "_property_id"
1052 1125 INNER JOIN ' . $wpdb->postmeta . ' AS property_office_meta ON property_office_meta.post_id = property_meta.meta_value AND property_office_meta.meta_key = "_office_id"
1053 - AND property_office_meta.meta_value = ' . (int)$_GET['_office_id'];
1126 + AND property_office_meta.meta_value = ' . $office_id;
1054 1127 }
1055 1128
1056 1129 /**
1057 1130 * Show an offer filter box
@@ -1064,8 +1137,9 @@
1064 1137 $output .= $this->offer_status_filter();
1065 1138 $output .= $this->property_office_filter();
1066 1139 $output .= $this->date_range_filter();
1067 1140
1141 + // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- Built-in controls escape their text and attributes before this trusted PHP filter adds complete HTML controls.
1068 1142 echo apply_filters( 'propertyhive_offer_filters', $output );
1069 1143 }
1070 1144
1071 1145 /**
@@ -1073,9 +1147,11 @@
1073 1147 */
1074 1148 public function offer_status_filter() {
1075 1149 global $wp_query;
1076 1150
1077 - $selected_status = isset( $_GET['_status'] ) && in_array( $_GET['_status'], array( 'pending', 'accepted', 'declined' ) ) ? ph_clean($_GET['_status']) : '';
1151 + // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only admin list display or query; no state change.
1152 + $requested_value = isset( $_GET['_status'] ) && is_string( $_GET['_status'] ) ? sanitize_text_field( wp_unslash( $_GET['_status'] ) ) : '';
1153 + $selected_status = in_array( $requested_value, array( 'pending', 'accepted', 'declined' ), true ) ? $requested_value : '';
1078 1154
1079 1155 // Status filtering
1080 1156 $output = '<select name="_status" id="dropdown_offer_status">';
1081 1157
@@ -1106,8 +1182,9 @@
1106 1182 $output .= $this->sale_status_filter();
1107 1183 $output .= $this->property_office_filter();
1108 1184 $output .= $this->date_range_filter();
1109 1185
1186 + // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- Built-in controls escape their text and attributes before this trusted PHP filter adds complete HTML controls.
1110 1187 echo apply_filters( 'propertyhive_sale_filters', $output );
1111 1188 }
1112 1189
1113 1190 /**
@@ -1115,14 +1192,16 @@
1115 1192 */
1116 1193 public function sale_status_filter() {
1117 1194 global $wp_query;
1118 1195
1119 - $selected_status = isset( $_GET['_status'] ) && in_array( $_GET['_status'], array( 'current', 'exchanged', 'completed', 'fallen_through' ) ) ? ph_clean($_GET['_status']) : '';
1196 + // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only admin list display or query; no state change.
1197 + $requested_value = isset( $_GET['_status'] ) && is_string( $_GET['_status'] ) ? sanitize_text_field( wp_unslash( $_GET['_status'] ) ) : '';
1198 + $selected_status = in_array( $requested_value, array( 'current', 'exchanged', 'completed', 'fallen_through' ), true ) ? $requested_value : '';
1120 1199
1121 1200 // Status filtering
1122 1201 $output = '<select name="_status" id="dropdown_sale_status">';
1123 1202
1124 - $output .= '<option value="">' . __( 'All Statuses', 'propertyhive' ) . '</option>';
1203 + $output .= '<option value="">' . esc_html__( 'All Statuses', 'propertyhive' ) . '</option>';
1125 1204
1126 1205 $sale_statuses = ph_get_sale_statuses();
1127 1206
1128 1207 foreach ( $sale_statuses as $status => $display_status )
@@ -1147,8 +1226,9 @@
1147 1226
1148 1227 $output .= $this->tenancy_status_filter();
1149 1228 $output .= $this->tenancy_management_type_filter();
1150 1229
1230 + // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- Built-in controls escape their text and attributes before this trusted PHP filter adds complete HTML controls.
1151 1231 echo apply_filters( 'propertyhive_tenancy_filters', $output );
1152 1232 }
1153 1233
1154 1234 /**
@@ -1156,9 +1236,11 @@
1156 1236 */
1157 1237 public function tenancy_status_filter() {
1158 1238 global $wp_query;
1159 1239
1160 - $selected_status = isset( $_GET['_status'] ) && in_array( $_GET['_status'], array( 'pending', 'current', 'finished') ) ? ph_clean($_GET['_status']) : '';
1240 + // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only admin list display or query; no state change.
1241 + $requested_value = isset( $_GET['_status'] ) && is_string( $_GET['_status'] ) ? sanitize_text_field( wp_unslash( $_GET['_status'] ) ) : '';
1242 + $selected_status = in_array( $requested_value, array( 'pending', 'current', 'finished'), true ) ? $requested_value : '';
1161 1243
1162 1244 // Status filtering
1163 1245 $output = '<select name="_status" id="dropdown_tenancy_status">';
1164 1246
@@ -1191,9 +1273,11 @@
1191 1273 'let_only' => 'Let Only',
1192 1274 'fully_managed' => 'Fully Managed'
1193 1275 ) );
1194 1276
1195 - $selected_management_type = isset( $_GET['_management_type'] ) && in_array( $_GET['_management_type'], array_keys($management_types) ) ? ph_clean($_GET['_management_type']) : '';
1277 + // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only admin list display or query; no state change.
1278 + $requested_value = isset( $_GET['_management_type'] ) && is_string( $_GET['_management_type'] ) ? sanitize_text_field( wp_unslash( $_GET['_management_type'] ) ) : '';
1279 + $selected_management_type = array_key_exists( $requested_value, $management_types ) ? $requested_value : '';
1196 1280
1197 1281 // Status filtering
1198 1282 $output = '<select name="_management_type" id="dropdown_tenancy_management_type">';
1199 1283
@@ -1202,9 +1286,9 @@
1202 1286 foreach ( $management_types as $key => $value )
1203 1287 {
1204 1288 $output .= '<option value="' . esc_attr($key) . '"';
1205 1289 $output .= selected( $key, $selected_management_type, false );
1206 - $output .= '>' . esc_html(__( $value, 'propertyhive' )) . '</option>';
1290 + $output .= '>' . esc_html( $value ) . '</option>';
1207 1291 }
1208 1292
1209 1293 $output .= '</select>';
1210 1294
@@ -1219,18 +1303,20 @@
1219 1303 $output .= $this->key_date_type_filter();
1220 1304 $output .= $this->key_date_status_filter();
1221 1305 $output .= $this->date_range_filter();
1222 1306
1307 + // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- Built-in controls escape their text and attributes before this trusted PHP filter adds complete HTML controls.
1223 1308 echo apply_filters( 'propertyhive_tenancy_filters', $output );
1224 1309 }
1225 1310
1226 1311 public function key_date_type_filter() {
1227 1312
1313 + // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only admin list display or query; no state change.
1228 1314 $selected_value = ! empty($_GET['_key_date_type_id']) ? (int)$_GET['_key_date_type_id'] : '';
1229 - $terms = get_terms( 'management_key_date_type', array(
1315 + $terms = get_terms( array_merge( wp_parse_args( array(
1230 1316 'hide_empty' => false,
1231 1317 'parent' => 0
1232 - ) );
1318 + ) ), array( 'taxonomy' => 'management_key_date_type' ) ) );
1233 1319
1234 1320 $output = '<select name="_key_date_type_id">';
1235 1321 $output .= '<option value="">' . esc_html(__( 'All Types', 'propertyhive' )) . '</option>';
1236 1322
@@ -1251,9 +1337,11 @@
1251 1337
1252 1338
1253 1339 public function key_date_status_filter() {
1254 1340
1255 - $selected_status = isset( $_GET['status'] ) && in_array( $_GET['status'], array( 'upcoming_and_overdue', 'overdue', 'booked', 'complete', 'pending', 'on_hold', 'cancelled') ) ? ph_clean($_GET['status']) : '';
1341 + // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only admin list display or query; no state change.
1342 + $requested_value = isset( $_GET['status'] ) && is_string( $_GET['status'] ) ? sanitize_text_field( wp_unslash( $_GET['status'] ) ) : '';
1343 + $selected_status = in_array( $requested_value, array( 'upcoming_and_overdue', 'overdue', 'booked', 'complete', 'pending', 'on_hold', 'cancelled'), true ) ? $requested_value : '';
1256 1344
1257 1345 $output = '<select name="status" id="dropdown_key_date_status">';
1258 1346
1259 1347 $output .= '<option value="">' . esc_html(__( 'All Statuses', 'propertyhive' )) . '</option>';
@@ -1298,50 +1386,71 @@
1298 1386 */
1299 1387 public function request_query( $vars ) {
1300 1388 global $typenow, $wp_query;
1301 1389
1390 + // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_query -- These hooks add status/department/taxonomy/date filters to the main admin list query. WordPress supplies the list query’s pagination; values are sanitized or selected from fixed post-type/date keys. These are request_query/filter_by_date_range values consumed by the core list table query rather than independent nopaging loops. The date meta key is chosen by post type.
1302 1391 if ( !isset($vars['meta_query']) ) { $vars['meta_query'] = array(); }
1392 + // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_tax_query -- These hooks add status/department/taxonomy/date filters to the main admin list query. WordPress supplies the list query’s pagination; values are sanitized or selected from fixed post-type/date keys. These are request_query/filter_by_date_range values consumed by the core list table query rather than independent nopaging loops. The date meta key is chosen by post type.
1303 1393 if ( !isset($vars['tax_query']) ) { $vars['tax_query'] = array(); }
1304 1394
1395 + $department = $this->get_admin_query_value( '_department' );
1396 + $marketing = $this->get_admin_query_value( '_marketing' );
1397 + $contact_type = $this->get_admin_query_value( '_contact_type' );
1398 + $status = $this->get_admin_query_value( '_status' );
1399 + $source = $this->get_admin_query_value( '_source' );
1400 + $management_type = $this->get_admin_query_value( '_management_type' );
1401 + $key_date_status = $this->get_admin_query_value( 'status' );
1402 +
1305 1403 if ( 'property' === $typenow )
1306 1404 {
1307 - if ( ! empty( $_GET['_department'] ) ) {
1405 + // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only admin list display or query; no state change.
1406 + if ( ! empty( $department ) ) {
1308 1407 $vars['meta_query'][] = array(
1309 1408 'key' => '_department',
1310 - 'value' => sanitize_text_field( $_GET['_department'] ),
1409 + 'value' => $department,
1311 1410 );
1312 1411 }
1412 + // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only admin list display or query; no state change.
1313 1413 if ( ! empty( $_GET['_office_id'] ) ) {
1314 1414 $vars['meta_query'][] = array(
1315 1415 'key' => '_office_id',
1416 + // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only admin list display or query; no state change.
1316 1417 'value' => (int)$_GET['_office_id'],
1317 1418 );
1318 1419 }
1420 + // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only admin list display or query; no state change.
1319 1421 if ( ! empty( $_GET['_negotiator_id'] ) ) {
1320 1422 $vars['meta_query'][] = array(
1321 1423 'key' => '_negotiator_id',
1424 + // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only admin list display or query; no state change.
1322 1425 'value' => (int)$_GET['_negotiator_id'],
1323 1426 );
1324 1427 }
1428 + // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only admin list display or query; no state change.
1325 1429 if ( ! empty( $_GET['_location_id'] ) ) {
1326 1430 $vars['tax_query'][] = array(
1327 1431 'taxonomy' => 'location',
1432 + // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only admin list display or query; no state change.
1328 1433 'terms' => ( (is_array($_GET['_location_id'])) ? (int)$_GET['_location_id'] : array( (int)$_GET['_location_id'] ) )
1329 1434 );
1330 1435 }
1436 + // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only admin list display or query; no state change.
1331 1437 if ( ! empty( $_GET['_availability_id'] ) ) {
1332 1438 $vars['tax_query'][] = array(
1333 1439 'taxonomy' => 'availability',
1440 + // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only admin list display or query; no state change.
1334 1441 'terms' => ( (is_array($_GET['_availability_id'])) ? (int)$_GET['_availability_id'] : array( (int)$_GET['_availability_id'] ) )
1335 1442 );
1336 1443 }
1337 - if ( ! empty( $_GET['_marketing'] ) && $_GET['_marketing'] == 'on_market' ) {
1444 + // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only admin list display or query; no state change.
1445 + if ( 'on_market' === $marketing ) {
1338 1446 $vars['meta_query'][] = array(
1339 1447 'key' => '_on_market',
1340 1448 'value' => 'yes',
1341 1449 );
1342 1450 }
1343 - if ( ! empty( $_GET['_marketing'] ) && $_GET['_marketing'] == 'off_market' ) {
1451 + // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only admin list display or query; no state change.
1452 + if ( 'off_market' === $marketing ) {
1344 1453 $vars['meta_query'][] = array(
1345 1454 'key' => '_on_market',
1346 1455 'value' => 'yes',
1347 1456 'compare' => '!=',
@@ -1346,16 +1455,18 @@
1346 1455 'value' => 'yes',
1347 1456 'compare' => '!=',
1348 1457 );
1349 1458 }
1350 - if ( ! empty( $_GET['_marketing'] ) && $_GET['_marketing'] == 'featured' ) {
1459 + // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only admin list display or query; no state change.
1460 + if ( 'featured' === $marketing ) {
1351 1461 $vars['meta_query'][] = array(
1352 1462 'key' => '_featured',
1353 1463 'value' => 'yes',
1354 1464 );
1355 - }
1356 - if ( ! empty( $_GET['_marketing'] ) && substr($_GET['_marketing'], 0, 15) == 'marketing_flag_' ) {
1357 - $marketing_flag_id = sanitize_text_field( str_replace("marketing_flag_", "", $_GET['_marketing']) );
1465 + }
1466 + // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only admin list display or query; no state change.
1467 + if ( 0 === strpos( $marketing, 'marketing_flag_' ) ) {
1468 + $marketing_flag_id = str_replace( 'marketing_flag_', '', $marketing );
1358 1469 $vars['tax_query'][] = array(
1359 1470 'taxonomy' => 'marketing_flag',
1360 1471 'terms' => ( (is_array($marketing_flag_id)) ? $marketing_flag_id : array( $marketing_flag_id ) )
1361 1472 );
@@ -1362,11 +1473,11 @@
1362 1473 }
1363 1474 }
1364 1475 elseif ( 'contact' === $typenow )
1365 1476 {
1366 - if ( ! empty( $_GET['_contact_type'] ) )
1477 + // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only admin list display or query; no state change.
1478 + if ( ! empty( $contact_type ) )
1367 1479 {
1368 - $contact_type = ph_clean($_GET['_contact_type']);
1369 1480 if ( $contact_type == 'hotapplicant' )
1370 1481 {
1371 1482 $contact_type = 'applicant';
1372 1483
@@ -1385,18 +1496,20 @@
1385 1496 $vars = $this->filter_by_date_range($vars, 'date_query');
1386 1497 }
1387 1498 elseif ( 'enquiry' === $typenow )
1388 1499 {
1389 - if ( ! empty( $_GET['_status'] ) && ph_clean($_GET['_status']) != 'all' ) {
1500 + // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only admin list display or query; no state change.
1501 + if ( ! empty( $status ) && $status != 'all' ) {
1390 1502
1391 1503 $vars['meta_query'][] = array(
1392 1504 'key' => '_status',
1393 - 'value' => sanitize_text_field( $_GET['_status'] ),
1505 + 'value' => $status,
1394 1506 );
1395 1507 }
1396 1508 else
1397 1509 {
1398 - if ( empty( $_GET['_status'] ) )
1510 + // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only admin list display or query; no state change.
1511 + if ( empty( $status ) )
1399 1512 {
1400 1513 $vars['meta_query'][] = array(
1401 1514 'key' => '_status',
1402 1515 'value' => 'open',
@@ -1402,23 +1515,28 @@
1402 1515 'value' => 'open',
1403 1516 );
1404 1517 }
1405 1518 }
1406 - if ( ! empty( $_GET['_source'] ) ) {
1519 + // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only admin list display or query; no state change.
1520 + if ( ! empty( $source ) ) {
1407 1521 $vars['meta_query'][] = array(
1408 1522 'key' => '_source',
1409 - 'value' => sanitize_text_field( $_GET['_source'] ),
1523 + 'value' => $source,
1410 1524 );
1411 1525 }
1526 + // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only admin list display or query; no state change.
1412 1527 if ( ! empty( $_GET['_office_id'] ) ) {
1413 1528 $vars['meta_query'][] = array(
1414 1529 'key' => '_office_id',
1530 + // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only admin list display or query; no state change.
1415 1531 'value' => (int)$_GET['_office_id'],
1416 1532 );
1417 1533 }
1534 + // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only admin list display or query; no state change.
1418 1535 if ( ! empty( $_GET['_negotiator_id'] ) ) {
1419 1536 $vars['meta_query'][] = array(
1420 1537 'key' => '_negotiator_id',
1538 + // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only admin list display or query; no state change.
1421 1539 'value' => (int)$_GET['_negotiator_id'],
1422 1540 );
1423 1541 }
1424 1542
@@ -1425,10 +1543,11 @@
1425 1543 $vars = $this->filter_by_date_range($vars, 'date_query');
1426 1544 }
1427 1545 elseif ( 'appraisal' === $typenow )
1428 1546 {
1429 - if ( ! empty( $_GET['_status'] ) ) {
1430 - switch ( sanitize_text_field( $_GET['_status'] ) )
1547 + // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only admin list display or query; no state change.
1548 + if ( ! empty( $status ) ) {
1549 + switch ( $status )
1431 1550 {
1432 1551 case "confirmed":
1433 1552 {
1434 1553 $vars['meta_query'][] = array(
@@ -1456,17 +1575,19 @@
1456 1575 default:
1457 1576 {
1458 1577 $vars['meta_query'][] = array(
1459 1578 'key' => '_status',
1460 - 'value' => sanitize_text_field( $_GET['_status'] ),
1579 + 'value' => $status,
1461 1580 );
1462 1581 }
1463 1582 }
1464 1583 }
1584 + // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only admin list display or query; no state change.
1465 1585 if ( ! empty( $_GET['_negotiator_id'] ) )
1466 1586 {
1467 1587 $vars['meta_query'][] = array(
1468 1588 'key' => '_negotiator_id',
1589 + // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only admin list display or query; no state change.
1469 1590 'value' => (int)$_GET['_negotiator_id'],
1470 1591 );
1471 1592 }
1472 1593
@@ -1473,17 +1594,21 @@
1473 1594 $vars = $this->filter_by_date_range($vars);
1474 1595 }
1475 1596 elseif ( 'viewing' === $typenow )
1476 1597 {
1477 - if ( ! empty( $_GET['_status'] ) ) {
1598 + // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only admin list display or query; no state change.
1599 + if ( ! empty( $status ) ) {
1478 1600
1479 - $vars['meta_query'] = add_viewing_status_meta_query( $vars['meta_query'], sanitize_text_field( $_GET['_status'] ) );
1601 + // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_query,WordPress.Security.NonceVerification.Recommended -- Read-only status filtering of the paginated core viewing list uses the existing viewing metadata schema; no state change.
1602 + $vars['meta_query'] = add_viewing_status_meta_query( $vars['meta_query'], $status );
1480 1603
1481 1604 }
1605 + // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only admin list display or query; no state change.
1482 1606 if ( ! empty( $_GET['_negotiator_id'] ) )
1483 1607 {
1484 1608 $vars['meta_query'][] = array(
1485 1609 'key' => '_negotiator_id',
1610 + // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only admin list display or query; no state change.
1486 1611 'value' => (int)$_GET['_negotiator_id'],
1487 1612 );
1488 1613 }
1489 1614
@@ -1490,12 +1615,13 @@
1490 1615 $vars = $this->filter_by_date_range($vars);
1491 1616 }
1492 1617 elseif ( 'offer' === $typenow )
1493 1618 {
1494 - if ( ! empty( $_GET['_status'] ) ) {
1619 + // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only admin list display or query; no state change.
1620 + if ( ! empty( $status ) ) {
1495 1621 $vars['meta_query'][] = array(
1496 1622 'key' => '_status',
1497 - 'value' => sanitize_text_field( $_GET['_status'] ),
1623 + 'value' => $status,
1498 1624 );
1499 1625 }
1500 1626
1501 1627 $vars = $this->filter_by_date_range($vars, '_offer_date_time');
@@ -1501,12 +1627,13 @@
1501 1627 $vars = $this->filter_by_date_range($vars, '_offer_date_time');
1502 1628 }
1503 1629 elseif ( 'sale' === $typenow )
1504 1630 {
1505 - if ( ! empty( $_GET['_status'] ) ) {
1631 + // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only admin list display or query; no state change.
1632 + if ( ! empty( $status ) ) {
1506 1633 $vars['meta_query'][] = array(
1507 1634 'key' => '_status',
1508 - 'value' => sanitize_text_field( $_GET['_status'] ),
1635 + 'value' => $status,
1509 1636 );
1510 1637 }
1511 1638
1512 1639 $vars = $this->filter_by_date_range($vars, '_sale_date_time');
@@ -1512,16 +1639,18 @@
1512 1639 $vars = $this->filter_by_date_range($vars, '_sale_date_time');
1513 1640 }
1514 1641 elseif ( 'tenancy' === $typenow )
1515 1642 {
1516 - if ( ! empty( $_GET['_status'] ) )
1643 + // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only admin list display or query; no state change.
1644 + if ( ! empty( $status ) )
1517 1645 {
1518 - switch ( $_GET['_status'] )
1646 + // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only admin list display or query; no state change.
1647 + switch ( $status )
1519 1648 {
1520 1649 case 'pending' :
1521 1650 $vars['meta_query'][] = array(
1522 1651 'key' => '_start_date',
1523 - 'value' => date('Y-m-d'),
1652 + 'value' => gmdate('Y-m-d'),
1524 1653 'type' => 'date',
1525 1654 'compare' => '>',
1526 1655 );
1527 1656 break;
@@ -1531,15 +1660,15 @@
1531 1660 'relation' => 'OR',
1532 1661 array(
1533 1662 array(
1534 1663 'key' => '_start_date',
1535 - 'value' => date('Y-m-d'),
1664 + 'value' => gmdate('Y-m-d'),
1536 1665 'type' => 'date',
1537 1666 'compare' => '<=',
1538 1667 ),
1539 1668 array(
1540 1669 'key' => '_end_date',
1541 - 'value' => date('Y-m-d'),
1670 + 'value' => gmdate('Y-m-d'),
1542 1671 'type' => 'date',
1543 1672 'compare' => '>=',
1544 1673 )
1545 1674 ),
@@ -1545,9 +1674,9 @@
1545 1674 ),
1546 1675 array(
1547 1676 array(
1548 1677 'key' => '_start_date',
1549 - 'value' => date('Y-m-d'),
1678 + 'value' => gmdate('Y-m-d'),
1550 1679 'type' => 'date',
1551 1680 'compare' => '<=',
1552 1681 ),
1553 1682 array(
@@ -1561,9 +1690,9 @@
1561 1690
1562 1691 case 'finished':
1563 1692 $vars['meta_query'][] = array(
1564 1693 'key' => '_end_date',
1565 - 'value' => date('Y-m-d'),
1694 + 'value' => gmdate('Y-m-d'),
1566 1695 'type' => 'date',
1567 1696 'compare' => '<',
1568 1697 );
1569 1698 break;
@@ -1569,20 +1698,22 @@
1569 1698 break;
1570 1699 }
1571 1700 }
1572 1701
1573 - if ( ! empty( $_GET['_management_type'] ) ) {
1702 + // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only admin list display or query; no state change.
1703 + if ( ! empty( $management_type ) ) {
1574 1704 $vars['meta_query'][] = array(
1575 1705 'key' => '_management_type',
1576 - 'value' => sanitize_text_field( $_GET['_management_type'] ),
1706 + 'value' => $management_type,
1577 1707 );
1578 1708 }
1579 1709 }
1580 1710 elseif ( 'key_date' === $typenow )
1581 1711 {
1582 - if ( ! empty( $_GET['status'] ) ) {
1712 + // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only admin list display or query; no state change.
1713 + if ( ! empty( $key_date_status ) ) {
1583 1714
1584 - $value = sanitize_text_field( $_GET['status'] );
1715 + $value = $key_date_status;
1585 1716
1586 1717 switch ($value) {
1587 1718 case 'booked':
1588 1719 case 'complete':
@@ -1606,9 +1737,9 @@
1606 1737 'compare' => 'IN'
1607 1738 );
1608 1739 $vars['meta_query'][] = array(
1609 1740 'key' => '_date_due',
1610 - 'value' => date("Y-m-d"),
1741 + 'value' => gmdate("Y-m-d"),
1611 1742 'type' => 'date',
1612 1743 'compare' => '<',
1613 1744 );
1614 1745 break;
@@ -1628,12 +1759,14 @@
1628 1759 break;
1629 1760 }
1630 1761 }
1631 1762
1763 + // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only admin list display or query; no state change.
1632 1764 if ( !empty( $_GET['_key_date_type_id'] ) )
1633 1765 {
1634 1766 $vars['meta_query'][] = array(
1635 1767 'key' => '_key_date_type_id',
1768 + // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only admin list display or query; no state change.
1636 1769 'value' => (int)$_GET['_key_date_type_id'],
1637 1770 );
1638 1771 }
1639 1772
@@ -1646,36 +1779,41 @@
1646 1779 }
1647 1780
1648 1781 private function filter_by_date_range($vars, $meta_key = '_start_date_time')
1649 1782 {
1783 + $date_range_label = $this->get_admin_query_value( '_date_range_label' );
1784 + $date_range_from = $this->get_admin_query_value( '_date_range_from' );
1785 + $date_range_to = $this->get_admin_query_value( '_date_range_to' );
1786 +
1650 1787 if (
1651 - ! empty( $_GET['_date_range_label'] )
1652 - && ! empty( $_GET['_date_range_from'] )
1653 - && ! empty( $_GET['_date_range_to'] )
1654 - && $_GET['_date_range_label'] !== 'Any Time'
1655 - && DateTime::createFromFormat('Y-m-d', $_GET['_date_range_from']) !== false
1656 - && DateTime::createFromFormat('Y-m-d', $_GET['_date_range_to']) !== false
1788 + ! empty( $date_range_label )
1789 + && ! empty( $date_range_from )
1790 + && ! empty( $date_range_to )
1791 + && $date_range_label !== 'Any Time'
1792 + && DateTime::createFromFormat('Y-m-d', $date_range_from) !== false
1793 + && DateTime::createFromFormat('Y-m-d', $date_range_to) !== false
1657 1794 )
1658 1795 {
1659 1796 if ( $meta_key == 'date_query' )
1660 1797 {
1661 1798 $vars['date_query'] = array(
1662 - 'after' => $_GET['_date_range_from'] . ' 00:00:00',
1663 - 'before' => $_GET['_date_range_to'] . ' 23:59:59',
1799 + 'after' => $date_range_from . ' 00:00:00',
1800 + 'before' => $date_range_to . ' 23:59:59',
1664 1801 );
1665 1802 }
1666 1803 else
1667 1804 {
1805 + // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_query -- Add validated date boundaries using the fixed date key selected for this paginated admin post-type list.
1668 1806 $vars['meta_query'] = array_merge($vars['meta_query'], array (
1669 1807 array(
1670 1808 'key' => $meta_key,
1671 - 'value' => ph_clean($_GET['_date_range_from']),
1809 + 'value' => $date_range_from,
1672 1810 'type' => 'date',
1673 1811 'compare' => '>='
1674 1812 ),
1675 1813 array(
1676 1814 'key' => $meta_key,
1677 - 'value' => ph_clean($_GET['_date_range_to']),
1815 + 'value' => $date_range_to,
1678 1816 'type' => 'date',
1679 1817 'compare' => '<='
1680 1818 ),
1681 1819 ));
@@ -1690,10 +1828,13 @@
1690 1828
1691 1829 if ( !$q->is_main_query() )
1692 1830 return $join;
1693 1831
1694 - if ( !isset($_GET['s']) || ( isset($_GET['s']) && ph_clean($_GET['s']) == '' ) )
1832 + // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only admin list display or query; no state change.
1833 + $search = isset( $_GET['s'] ) && is_string( $_GET['s'] ) ? sanitize_text_field( wp_unslash( $_GET['s'] ) ) : '';
1834 + if ( $search === '' ) {
1695 1835 return $join;
1836 + }
1696 1837
1697 1838 if ( 'property' === $typenow )
1698 1839 {
1699 1840 $join .= "
@@ -1704,11 +1845,13 @@
1704 1845 }
1705 1846 elseif ( 'contact' === $typenow )
1706 1847 {
1707 1848 $phone_number = '';
1708 - if ( is_numeric(substr(ph_clean($_GET['s']), 0, 1)) )
1849 + // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only admin list display or query; no state change.
1850 + if ( is_numeric(substr($search, 0, 1)) )
1709 1851 {
1710 - $phone_number = preg_replace( "/[^0-9,]/", "", ph_clean($_GET['s']) );
1852 + // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only admin list display or query; no state change.
1853 + $phone_number = preg_replace( "/[^0-9,]/", "", $search );
1711 1854 }
1712 1855
1713 1856 $join .= "
1714 1857 LEFT JOIN " . $wpdb->postmeta . " AS ph_contact_filter_meta_address_concatenated ON " . $wpdb->posts . ".ID = ph_contact_filter_meta_address_concatenated.post_id AND ph_contact_filter_meta_address_concatenated.meta_key = '_address_concatenated'
@@ -1751,35 +1894,43 @@
1751 1894
1752 1895 if ( !$q->is_main_query() )
1753 1896 return $where;
1754 1897
1755 - if ( !isset($_GET['s']) || ( isset($_GET['s']) && ph_clean($_GET['s']) == '' ) )
1898 + // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only admin list display or query; no state change.
1899 + $search = isset( $_GET['s'] ) && is_string( $_GET['s'] ) ? sanitize_text_field( wp_unslash( $_GET['s'] ) ) : '';
1900 + if ( $search === '' ) {
1756 1901 return $where;
1902 + }
1903 + $reference_like = $wpdb->prepare( '%s', $wpdb->esc_like( $search ) . '%' );
1904 + $reference_exact = $wpdb->prepare( '%s', $search );
1905 + $phone_number = '';
1757 1906
1758 1907 if ( 'property' === $typenow )
1759 1908 {
1760 - $where = preg_replace(
1761 - "/\(\s*" . $wpdb->posts . ".post_title\s+LIKE\s*(\'[^\']+\')\s*\)/",
1762 - "(
1763 - (" . $wpdb->posts . ".post_title LIKE $1)
1909 + $where = preg_replace_callback(
1910 + "/\(\s*" . $wpdb->posts . ".post_title\s+LIKE\s*('(?:\\\\.|[^'\\\\])*')\s*\)/",
1911 + static function( $matches ) use ( $wpdb, $reference_like, $reference_exact, $phone_number ) {
1912 + return "(
1913 + (" . $wpdb->posts . ".post_title LIKE " . $matches[1] . ")
1764 1914 OR
1765 - (ph_property_filter_meta_address_concatenated.meta_value LIKE $1)
1915 + (ph_property_filter_meta_address_concatenated.meta_value LIKE " . $matches[1] . ")
1766 1916 OR
1767 - (ph_property_filter_meta_reference_number.meta_value LIKE '" . esc_sql($_GET['s']) . "%')
1917 + (ph_property_filter_meta_reference_number.meta_value LIKE " . $reference_like . ")
1768 1918 OR
1769 - (ph_property_filter_meta_owner_details.meta_value LIKE $1)
1770 - )",
1919 + (ph_property_filter_meta_owner_details.meta_value LIKE " . $matches[1] . ")
1920 + )";
1921 + },
1771 1922 $where
1772 1923 );
1773 1924
1774 1925 $where = preg_replace(
1775 - "/\s+OR\s+\(\s*" . $wpdb->posts . ".post_excerpt\s+LIKE\s*(\'[^\']+\')\s*\)/",
1926 + "/\s+OR\s+\(\s*" . $wpdb->posts . ".post_excerpt\s+LIKE\s*('(?:\\\\.|[^'\\\\])*')\s*\)/",
1776 1927 "",
1777 1928 $where
1778 1929 );
1779 1930
1780 1931 $where = preg_replace(
1781 - "/\s+OR\s+\(\s*" . $wpdb->posts . ".post_content\s+LIKE\s*(\'[^\']+\')\s*\)/",
1932 + "/\s+OR\s+\(\s*" . $wpdb->posts . ".post_content\s+LIKE\s*('(?:\\\\.|[^'\\\\])*')\s*\)/",
1782 1933 "",
1783 1934 $where
1784 1935 );
1785 1936 }
@@ -1785,34 +1936,38 @@
1785 1936 }
1786 1937 elseif ( 'contact' === $typenow )
1787 1938 {
1788 1939 $phone_number = '';
1789 - if ( is_numeric(substr(ph_clean($_GET['s']), 0, 1)) )
1940 + // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only admin list display or query; no state change.
1941 + if ( is_numeric(substr($search, 0, 1)) )
1790 1942 {
1791 - $phone_number = preg_replace( "/[^0-9,]/", "", ph_clean($_GET['s']) );
1943 + // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only admin list display or query; no state change.
1944 + $phone_number = preg_replace( "/[^0-9,]/", "", $search );
1792 1945 }
1793 1946
1794 - $where = preg_replace(
1795 - "/\(\s*" . $wpdb->posts . ".post_title\s+LIKE\s*(\'[^\']+\')\s*\)/",
1796 - "(
1797 - (" . $wpdb->posts . ".post_title LIKE $1)
1947 + $where = preg_replace_callback(
1948 + "/\(\s*" . $wpdb->posts . ".post_title\s+LIKE\s*('(?:\\\\.|[^'\\\\])*')\s*\)/",
1949 + static function( $matches ) use ( $wpdb, $reference_like, $reference_exact, $phone_number ) {
1950 + return "(
1951 + (" . $wpdb->posts . ".post_title LIKE " . $matches[1] . ")
1798 1952 OR
1799 - (ph_contact_filter_meta_address_concatenated.meta_value LIKE $1)
1953 + (ph_contact_filter_meta_address_concatenated.meta_value LIKE " . $matches[1] . ")
1800 1954 OR
1801 - (ph_contact_filter_meta_email_address.meta_value LIKE $1)
1955 + (ph_contact_filter_meta_email_address.meta_value LIKE " . $matches[1] . ")
1802 1956 " . ( $phone_number != '' ? "OR (ph_contact_filter_meta_telephone_number.meta_value LIKE '%" . $phone_number . "%')" : '' ) . "
1803 - )",
1957 + )";
1958 + },
1804 1959 $where
1805 1960 );
1806 1961
1807 1962 $where = preg_replace(
1808 - "/\s+OR\s+\(\s*" . $wpdb->posts . ".post_excerpt\s+LIKE\s*(\'[^\']+\')\s*\)/",
1963 + "/\s+OR\s+\(\s*" . $wpdb->posts . ".post_excerpt\s+LIKE\s*('(?:\\\\.|[^'\\\\])*')\s*\)/",
1809 1964 "",
1810 1965 $where
1811 1966 );
1812 1967
1813 1968 $where = preg_replace(
1814 - "/\s+OR\s+\(\s*" . $wpdb->posts . ".post_content\s+LIKE\s*(\'[^\']+\')\s*\)/",
1969 + "/\s+OR\s+\(\s*" . $wpdb->posts . ".post_content\s+LIKE\s*('(?:\\\\.|[^'\\\\])*')\s*\)/",
1815 1970 "",
1816 1971 $where
1817 1972 );
1818 1973 }
@@ -1817,43 +1972,47 @@
1817 1972 );
1818 1973 }
1819 1974 elseif ( 'appraisal' === $typenow )
1820 1975 {
1821 - $where = preg_replace(
1822 - "/\(\s*" . $wpdb->posts . ".post_title\s+LIKE\s*(\'[^\']+\')\s*\)/",
1823 - "(
1824 - (" . $wpdb->posts . ".post_title LIKE $1)
1976 + $where = preg_replace_callback(
1977 + "/\(\s*" . $wpdb->posts . ".post_title\s+LIKE\s*('(?:\\\\.|[^'\\\\])*')\s*\)/",
1978 + static function( $matches ) use ( $wpdb, $reference_like, $reference_exact, $phone_number ) {
1979 + return "(
1980 + (" . $wpdb->posts . ".post_title LIKE " . $matches[1] . ")
1825 1981 OR
1826 - (ph_appraisal_filter_meta_name_number.meta_value LIKE $1)
1982 + (ph_appraisal_filter_meta_name_number.meta_value LIKE " . $matches[1] . ")
1827 1983 OR
1828 - (ph_appraisal_filter_meta_street.meta_value LIKE $1)
1984 + (ph_appraisal_filter_meta_street.meta_value LIKE " . $matches[1] . ")
1829 1985 OR
1830 - (ph_appraisal_filter_meta_2.meta_value LIKE $1)
1986 + (ph_appraisal_filter_meta_2.meta_value LIKE " . $matches[1] . ")
1831 1987 OR
1832 - (ph_appraisal_filter_meta_3.meta_value LIKE $1)
1988 + (ph_appraisal_filter_meta_3.meta_value LIKE " . $matches[1] . ")
1833 1989 OR
1834 - (ph_appraisal_filter_meta_4.meta_value LIKE $1)
1990 + (ph_appraisal_filter_meta_4.meta_value LIKE " . $matches[1] . ")
1835 1991 OR
1836 - (ph_appraisal_filter_meta_postcode.meta_value LIKE $1)
1837 - )",
1992 + (ph_appraisal_filter_meta_postcode.meta_value LIKE " . $matches[1] . ")
1993 + )";
1994 + },
1838 1995 $where
1839 1996 );
1840 1997 }
1841 1998 elseif ( 'viewing' === $typenow || 'offer' === $typenow || 'sale' === $typenow || 'tenancy' === $typenow )
1842 1999 {
1843 - $where = preg_replace(
1844 - "/\(\s*" . $wpdb->posts . ".post_title\s+LIKE\s*(\'[^\']+\')\s*\)/",
1845 - "(
1846 - (" . $wpdb->posts . ".post_title LIKE $1)
2000 + $where = preg_replace_callback(
2001 + "/\(\s*" . $wpdb->posts . ".post_title\s+LIKE\s*('(?:\\\\.|[^'\\\\])*')\s*\)/",
2002 + static function( $matches ) use ( $wpdb, $reference_like, $reference_exact, $phone_number ) {
2003 + return "(
2004 + (" . $wpdb->posts . ".post_title LIKE " . $matches[1] . ")
1847 2005 OR
1848 - (ph_property_filter_posts.post_title LIKE $1)
2006 + (ph_property_filter_posts.post_title LIKE " . $matches[1] . ")
1849 2007 OR
1850 - (ph_property_filter_meta_address_concatenated.meta_value LIKE $1)
2008 + (ph_property_filter_meta_address_concatenated.meta_value LIKE " . $matches[1] . ")
1851 2009 OR
1852 - (ph_property_filter_meta_reference_number.meta_value = '" . esc_sql($_GET['s']) . "')
2010 + (ph_property_filter_meta_reference_number.meta_value = " . $reference_exact . ")
1853 2011 OR
1854 - (ph_applicant_filter_posts.post_title LIKE $1)
1855 - )",
2012 + (ph_applicant_filter_posts.post_title LIKE " . $matches[1] . ")
2013 + )";
2014 + },
1856 2015 $where
1857 2016 );
1858 2017 }
1859 2018
@@ -1924,5 +2083,5 @@
1924 2083 }
1925 2084
1926 2085 endif;
1927 2086
1928 -return new PH_Admin_Post_Types();
2087 +return new PH_Admin_Post_Types();