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/settings/class-ph-settings-emails.php +71 -58 2.2.32.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 * PropertyHive Email Settings
4 7 *
5 8 * @author PropertyHive
@@ -16,8 +19,9 @@
16 19
17 20 /**
18 21 * PH_Settings_Emails.
19 22 */
23 +// phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedClassFound -- Legacy public global class PH_Settings_Emails; preserving the existing PH_* class name is required for plugin and extension compatibility.
20 24 class PH_Settings_Emails extends PH_Settings_Page {
21 25
22 26 /**
23 27 * Constructor.
@@ -223,9 +227,9 @@
223 227 $args = array(
224 228 'hide_empty' => false,
225 229 'parent' => 0
226 230 );
227 - $terms = get_terms( 'availability', $args );
231 + $terms = get_terms( array_merge( wp_parse_args( $args ), array( 'taxonomy' => 'availability' ) ) );
228 232
229 233 if ( !empty( $terms ) && !is_wp_error( $terms ) )
230 234 {
231 235 foreach ($terms as $term)
@@ -248,9 +252,9 @@
248 252 'title' => __( 'Automatically Send Matching Properties To Applicants', 'propertyhive' ),
249 253 'desc' => __( 'Enabling this setting will mean applicants will automatically get sent properties.<br><br>
250 254 - This will only apply to properties added from the moment this option is activated.<br>
251 255 - When enabled, this can be disabled on a per-applicant basis by going into their record.<br>
252 - - When sending out lots of emails we recommend using <a href="https://en-gb.wordpress.org/plugins/tags/smtp" target="_blank">a plugin</a> to send them out using SMTP. Your web developer or hosting company should be able to advise on this.', 'propertyhive' ) . ( ( get_option( 'propertyhive_auto_property_match', '' ) == 'yes' && get_option( 'propertyhive_auto_property_match_enabled_date', '' ) != '' ) ? '<br><br>Enabled on ' . date("jS F Y H:i", strtotime(get_option( 'propertyhive_auto_property_match_enabled_date', '' )) + $time_offset) : '' ),
256 + - When sending out lots of emails we recommend using <a href="https://en-gb.wordpress.org/plugins/tags/smtp" target="_blank">a plugin</a> to send them out using SMTP. Your web developer or hosting company should be able to advise on this.', 'propertyhive' ) . ( ( get_option( 'propertyhive_auto_property_match', '' ) == 'yes' && get_option( 'propertyhive_auto_property_match_enabled_date', '' ) != '' ) ? '<br><br>Enabled on ' . gmdate("jS F Y H:i", strtotime(get_option( 'propertyhive_auto_property_match_enabled_date', '' )) + $time_offset) : '' ),
253 257 'id' => 'propertyhive_auto_property_match',
254 258 'type' => 'checkbox',
255 259 'default' => '',
256 260 );
@@ -466,23 +470,25 @@
466 470 public function email_queue_setting()
467 471 {
468 472 global $wpdb, $post;
469 473
470 - $additional_query = '';
471 - $additional_query_string = '';
472 - if ( isset($_GET['date_from']) && sanitize_text_field($_GET['date_from']) != '' )
473 - {
474 - $additional_query_string .= '&date_from=' . sanitize_text_field($_GET['date_from']);
475 - if ( sanitize_text_field($_GET['date_from']) != 'all' )
476 - {
477 - $additional_query .= " AND send_at >= '" . sanitize_text_field($_GET['date_from']) . " 00:00:00' ";
478 - }
474 + $date_from = '';
475 + // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- The email queue date filter is a read-only GET parameter; the separate Run Now action has its own capability and nonce gate.
476 + if ( isset( $_GET['date_from'] ) && is_string( $_GET['date_from'] ) ) {
477 + // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- The email queue date filter is a read-only GET parameter; the separate Run Now action has its own capability and nonce gate.
478 + $date_from = sanitize_text_field( wp_unslash( $_GET['date_from'] ) );
479 479 }
480 - else
481 - {
482 - // Default to 30 days
483 - $additional_query .= " AND send_at >= '" . date("Y-m-d", strtotime('-30 days')) . " 00:00:00' ";
480 + $date = DateTime::createFromFormat( 'Y-m-d', $date_from );
481 + if ( 'all' !== $date_from && ( ! $date || $date->format( 'Y-m-d' ) !== $date_from ) ) {
482 + $date_from = gmdate( 'Y-m-d', strtotime( '-30 days' ) );
484 483 }
484 + $status = '';
485 + // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- The email queue status filter is a read-only GET parameter; the separate Run Now action has its own capability and nonce gate.
486 + if ( isset( $_GET['status'] ) && is_string( $_GET['status'] ) ) {
487 + // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- The email queue status filter is a read-only GET parameter; the separate Run Now action has its own capability and nonce gate.
488 + $status = sanitize_key( wp_unslash( $_GET['status'] ) );
489 + }
490 + $additional_query_string = '&date_from=' . rawurlencode( $date_from );
485 491 ?>
486 492 <tr valign="top">
487 493 <td style="padding:0">
488 494
@@ -500,24 +506,26 @@
500 506 echo 'Whoops. WordPress doesn\'t have the emails automated task scheduled. A quick fix for this is to deactivate, then re-activate the plugin.';
501 507 }
502 508 else
503 509 {
504 - echo esc_html(__( 'Next scheduled to run at', 'propertyhive' ) . ' ' . date("H:i jS F Y", $next_due));
510 + echo esc_html(__( 'Next scheduled to run at', 'propertyhive' ) . ' ' . wp_date("H:i jS F Y", $next_due));
505 511 }
506 - ?></strong> <a href="<?php echo esc_url(admin_url('admin.php?page=ph-settings&tab=email&section=log&custom_email_log_cron=propertyhive_process_email_log' )); ?>" class="button">Run Now</a></p>
512 + ?></strong> <a href="<?php echo esc_url( wp_nonce_url( admin_url( 'admin.php?page=ph-settings&tab=email&section=log&custom_email_log_cron=propertyhive_process_email_log' ), 'propertyhive-run-email-job' ) ); ?>" class="button">Run Now</a></p>
507 513
508 514 <br>
509 515
510 516 <ul class="subsubsub">
511 517 <?php
512 - $emails = $wpdb->get_var("
518 + // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- The email queue is stored in Property Hive's custom table, which has no WP_Query API; this live admin count must reflect asynchronous queue changes immediately.
519 + $emails = $wpdb->get_var("
513 520 SELECT COUNT(*)
514 521 FROM " . $wpdb->prefix . "ph_email_log
515 522 ");
516 523 ?>
517 - <li class="all"><a href="<?php echo esc_url(admin_url('admin.php?page=ph-settings&tab=email&section=log' . $additional_query_string)); ?>"<?php if ( !isset($_GET['status']) || (isset($_GET['status']) && $_GET['status'] == '') ) { echo ' class="current"'; } ?>>All <span class="count">(<?php echo number_format($emails); ?>)</span></a> |</li>
524 + <li class="all"><a href="<?php echo esc_url(admin_url('admin.php?page=ph-settings&tab=email&section=log' . $additional_query_string)); ?>"<?php if ( '' === $status ) { echo ' class="current"'; } ?>>All <span class="count">(<?php echo number_format($emails); ?>)</span></a> |</li>
518 525 <?php
519 - $emails = $wpdb->get_var("
526 + // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- The email queue is stored in Property Hive's custom table, which has no WP_Query API; this live admin count must reflect asynchronous queue changes immediately.
527 + $emails = $wpdb->get_var("
520 528 SELECT COUNT(*)
521 529 FROM " . $wpdb->prefix . "ph_email_log
522 530 WHERE
523 531 status = ''
@@ -522,11 +530,12 @@
522 530 WHERE
523 531 status = ''
524 532 ");
525 533 ?>
526 - <li class="queued"><a href="<?php echo esc_url(admin_url('admin.php?page=ph-settings&tab=email&section=log&status=queued' . $additional_query_string)); ?>"<?php if ( isset($_GET['status']) && $_GET['status'] == 'queued' ) { echo ' class="current"'; } ?>>Queued <span class="count">(<?php echo number_format($emails); ?>)</span></a> |</li>
534 + <li class="queued"><a href="<?php echo esc_url(admin_url('admin.php?page=ph-settings&tab=email&section=log&status=queued' . $additional_query_string)); ?>"<?php if ( 'queued' === $status ) { echo ' class="current"'; } ?>>Queued <span class="count">(<?php echo number_format($emails); ?>)</span></a> |</li>
527 535 <?php
528 - $emails = $wpdb->get_var("
536 + // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- The email queue is stored in Property Hive's custom table, which has no WP_Query API; this live admin count must reflect asynchronous queue changes immediately.
537 + $emails = $wpdb->get_var("
529 538 SELECT COUNT(*)
530 539 FROM " . $wpdb->prefix . "ph_email_log
531 540 WHERE
532 541 status IN ('fail1', 'fail2')
@@ -531,11 +540,12 @@
531 540 WHERE
532 541 status IN ('fail1', 'fail2')
533 542 ");
534 543 ?>
535 - <li class="failed"><a href="<?php echo esc_url(admin_url('admin.php?page=ph-settings&tab=email&section=log&status=failed' . $additional_query_string)); ?>"<?php if ( isset($_GET['status']) && $_GET['status'] == 'failed' ) { echo ' class="current"'; } ?>>Failed <span class="count">(<?php echo number_format($emails); ?>)</span></a> |</li>
544 + <li class="failed"><a href="<?php echo esc_url(admin_url('admin.php?page=ph-settings&tab=email&section=log&status=failed' . $additional_query_string)); ?>"<?php if ( 'failed' === $status ) { echo ' class="current"'; } ?>>Failed <span class="count">(<?php echo number_format($emails); ?>)</span></a> |</li>
536 545 <?php
537 - $emails = $wpdb->get_var("
546 + // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- The email queue is stored in Property Hive's custom table, which has no WP_Query API; this live admin count must reflect asynchronous queue changes immediately.
547 + $emails = $wpdb->get_var("
538 548 SELECT COUNT(*)
539 549 FROM " . $wpdb->prefix . "ph_email_log
540 550 WHERE
541 551 status = 'sent'
@@ -540,9 +550,9 @@
540 550 WHERE
541 551 status = 'sent'
542 552 ");
543 553 ?>
544 - <li class="sent"><a href="<?php echo esc_url(admin_url('admin.php?page=ph-settings&tab=email&section=log&status=sent' . $additional_query_string)); ?>"<?php if ( isset($_GET['status']) && $_GET['status'] == 'sent' ) { echo ' class="current"'; } ?>>Sent <span class="count">(<?php echo number_format($emails); ?>)</span></a></li>
554 + <li class="sent"><a href="<?php echo esc_url(admin_url('admin.php?page=ph-settings&tab=email&section=log&status=sent' . $additional_query_string)); ?>"<?php if ( 'sent' === $status ) { echo ' class="current"'; } ?>>Sent <span class="count">(<?php echo number_format($emails); ?>)</span></a></li>
545 555 </ul>
546 556
547 557 <div class="tablenav top">
548 558 <div class="alignleft actions bulkactions">
@@ -549,14 +559,14 @@
549 559 <div class="alignleft actions">
550 560 <input type="hidden" name="page" value="ph-settings">
551 561 <input type="hidden" name="tab" value="email">
552 562 <input type="hidden" name="section" value="log">
553 - <input type="hidden" name="status" value="<?php echo ( ( isset($_GET['status']) ) ? esc_attr(ph_clean($_GET['status'])) : '' ); ?>">
563 + <input type="hidden" name="status" value="<?php echo esc_attr( $status ); ?>">
554 564 <select name="date_from" id="dropdown_date_from">
555 - <option value="<?php echo esc_attr(date("Y-m-d", strtotime("-7 days"))); ?>"<?php if ( isset($_GET['date_from']) && $_GET['date_from'] == date("Y-m-d", strtotime("-7 days")) ) { echo ' selected'; } ?>>Last 7 Days</option>
556 - <option value="<?php echo esc_attr(date("Y-m-d", strtotime("-14 days"))); ?>"<?php if ( isset($_GET['date_from']) && $_GET['date_from'] == date("Y-m-d", strtotime("-14 days")) ) { echo ' selected'; } ?>>Last 14 Days</option>
557 - <option value="<?php echo esc_attr(date("Y-m-d", strtotime("-30 days"))); ?>"<?php if ( !isset($_GET['date_from']) || ( isset($_GET['date_from']) && $_GET['date_from'] == date("Y-m-d", strtotime("-30 days")) ) ) { echo ' selected'; } ?>>Last 30 Days</option>
558 - <option value="all"<?php if ( isset($_GET['date_from']) && $_GET['date_from'] == 'all' ) { echo ' selected'; } ?>>All Time</option>
565 + <option value="<?php echo esc_attr(gmdate("Y-m-d", strtotime("-7 days"))); ?>"<?php if ( $date_from === gmdate("Y-m-d", strtotime("-7 days")) ) { echo ' selected'; } ?>>Last 7 Days</option>
566 + <option value="<?php echo esc_attr(gmdate("Y-m-d", strtotime("-14 days"))); ?>"<?php if ( $date_from === gmdate("Y-m-d", strtotime("-14 days")) ) { echo ' selected'; } ?>>Last 14 Days</option>
567 + <option value="<?php echo esc_attr(gmdate("Y-m-d", strtotime("-30 days"))); ?>"<?php if ( $date_from === gmdate("Y-m-d", strtotime("-30 days")) ) { echo ' selected'; } ?>>Last 30 Days</option>
568 + <option value="all"<?php if ( 'all' === $date_from ) { echo ' selected'; } ?>>All Time</option>
559 569 </select>
560 570 <input type="submit" name="filter_action" id="post-query-submit" class="button" value="Filter">
561 571
562 572 <script>
@@ -583,35 +593,33 @@
583 593 </tr>
584 594 </thead>
585 595 <tbody>
586 596 <?php
587 - $query = "
588 - SELECT
589 - email_id,
590 - contact_id,
591 - to_email_address,
592 - subject,
593 - status,
594 - send_at
595 - FROM " . $wpdb->prefix . "ph_email_log
596 - WHERE
597 - 1=1 ";
598 - if ( isset($_GET['status']) )
599 - {
600 - switch ( ph_clean($_GET['status']) )
601 - {
602 - case "queued": { $query .= " AND status = '' "; break; }
603 - case "failed": { $query .= " AND status IN ('fail1', 'fail2') "; break; }
604 - case "sent": { $query .= " AND status = 'sent' "; break; }
605 - }
606 - }
607 - $query .= $additional_query;
597 + $query = "SELECT email_id, contact_id, to_email_address, subject, status, send_at
598 + FROM {$wpdb->prefix}ph_email_log
599 + WHERE %s = %s";
600 + $query_args = array( '1', '1' );
601 + if ( 'all' !== $date_from ) {
602 + $query .= ' AND send_at >= %s';
603 + $query_args[] = $date_from . ' 00:00:00';
604 + }
605 + switch ( $status ) {
606 + case 'queued':
607 + $query .= ' AND status = %s';
608 + $query_args[] = '';
609 + break;
610 + case 'failed':
611 + $query .= " AND status IN ('fail1', 'fail2')";
612 + break;
613 + case 'sent':
614 + $query .= ' AND status = %s';
615 + $query_args[] = 'sent';
616 + break;
617 + }
618 + $query .= ' ORDER BY send_at DESC LIMIT 250';
619 + // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.NotPrepared, PluginCheck.Security.DirectDB.UnescapedDBParameter -- The email queue is stored in Property Hive's custom table, which has no WP_Query API; this live filtered log must reflect asynchronous queue changes immediately. The query contains only fixed SQL branches and passes all date/status values through $wpdb->prepare().
620 + $emails = $wpdb->get_results( $wpdb->prepare( $query, $query_args ) );
608 621
609 - $query .= " ORDER BY send_at DESC
610 - LIMIT 250
611 - ";
612 - $emails = $wpdb->get_results( $query );
613 -
614 622 if ( is_array($emails) && !empty($emails) )
615 623 {
616 624 foreach ( $emails as $email )
617 625 {
@@ -616,9 +624,9 @@
616 624 foreach ( $emails as $email )
617 625 {
618 626 ?>
619 627 <tr>
620 - <td class="date-time"><?php echo esc_html(date("jS M Y H:i", strtotime($email->send_at))); ?></td>
628 + <td class="date-time"><?php echo esc_html(gmdate("jS M Y H:i", strtotime($email->send_at))); ?></td>
621 629 <td class="recipient"><?php echo '<a href="' . esc_url(get_edit_post_link($email->contact_id)) . '">' . esc_html(get_the_title($email->contact_id)) . '</a><br>' . esc_html($email->to_email_address); ?></td>
622 630 <td class="subject"><?php echo esc_html($email->subject); ?></td>
623 631 <td class="status"><?php
624 632 switch ($email->status)
@@ -664,8 +672,9 @@
664 672 {
665 673 case "enquiry-auto-responder": { $settings = $this->get_enquiry_autoresponder_settings(); break; }
666 674 case "match": { $settings = $this->get_property_match_settings(); break; }
667 675 case "booking-confirmation": { $settings = $this->get_booking_confirmation_settings(); break; }
676 + // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedVariableFound -- Shared admin settings-view state; this global is intentionally used to control the common settings template and is not an arbitrary application global.
668 677 case "log": { $settings = $this->get_email_queue_settings(); $hide_save_button = true; break; }
669 678 default: { die("Unknown setting section"); }
670 679 }
671 680 }
@@ -681,8 +690,12 @@
681 690 * Save settings.
682 691 */
683 692 public function save()
684 693 {
694 + if ( ! current_user_can( 'manage_options' ) || ! isset( $_REQUEST['_wpnonce'] ) || ! is_string( $_REQUEST['_wpnonce'] ) || ! wp_verify_nonce( sanitize_text_field( wp_unslash( $_REQUEST['_wpnonce'] ) ), 'propertyhive-settings' ) ) {
695 + return;
696 + }
697 +
685 698 global $current_section;
686 699
687 700 if ( $current_section != '' )
688 701 {
@@ -754,5 +767,5 @@
754 767 }
755 768
756 769 endif;
757 770
758 -return new PH_Settings_Emails();
771 +return new PH_Settings_Emails();