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