← All changes
|
includes/admin/settings/class-ph-settings-emails.php
+71
-58
2.2.3
→
2.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§ion=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§ion=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§ion=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§ion=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§ion=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§ion=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§ion=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§ion=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§ion=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§ion=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(); | |