Last active
May 28, 2025 21:04
-
-
Save damiencarbery/dfc45a9ceb52fa336749b363a6e7dd51 to your computer and use it in GitHub Desktop.
Defer WooCommerce emails for a few minutes - Defer WooCommerce emails for a specified time after the normal delivery time. https://www.damiencarbery.com/2020/04/defer-woocommerce-emails-for-a-few-minutes/
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| <?php | |
| /* | |
| Plugin Name: Defer WooCommerce emails for a few minutes (use Action Scheduler) | |
| Plugin URI: https://www.damiencarbery.com/2020/04/defer-woocommerce-emails-for-a-few-minutes/ | |
| Description: Defer WooCommerce emails for a specified time after the normal delivery time. Use Action Scheduler instead of WP Cron. Call do_action() instead of remove_action() and calling trigger. | |
| Author: Damien Carbery | |
| Author URI: https://www.damiencarbery.com | |
| Version: 0.11.20250528 | |
| */ | |
| class DeferSendingWooCommerceEmails { | |
| private static $instance; | |
| private $default_defer_time; | |
| // An associative array to match $email_id with email class, to allow for the deferring of different emails. | |
| private $email_id_to_defer; | |
| private $debug_mode; | |
| // Returns an instance of this class. | |
| public static function get_instance() { | |
| if ( null == self::$instance ) { | |
| self::$instance = new DeferSendingWooCommerceEmails(); | |
| } | |
| return self::$instance; | |
| } | |
| // Initialize the plugin variables. | |
| public function __construct() { | |
| $this->debug_mode = false; | |
| // Uncomment this to enable debug mode. | |
| //$this->debug_mode = true; | |
| $this->default_defer_time = 600; // Defer for 600 seconds (10 minutes). | |
| if ( $this->debug_mode ) { | |
| $this->default_defer_time = 60; // Defer for 1 minute when debugging. | |
| } | |
| // Can add other email IDs and their defer times. | |
| // You can uncomment the add_action() with 'order_status_changed' function to find out the before and after status. | |
| // This is also the list of emails that will be deferred. | |
| $this->email_id_to_defer = array( | |
| 'woocommerce_order_status_completed' => $this->default_defer_time, | |
| //'woocommerce_order_status_processing' => $this->default_defer_time, | |
| // Probably don't want to defer these emails but they are shown | |
| // here as a demo of a different defer time. | |
| //'woocommerce_new_customer_note' => $this->default_defer_time, | |
| //'woocommerce_order_status_pending_to_on-hold' => $this->default_defer_time, // New order | |
| //'woocommerce_order_status_pending_to_processing' => $this->default_defer_time, | |
| // Additional transitition-to-email class info from @djm56 | |
| //'woocommerce_order_status_on-hold' => $this->default_defer_time, // Order on hold. | |
| //'woocommerce_order_status_pending_to_on-hold' => $this->default_defer_time, // Order on hold. | |
| //'woocommerce_order_status_cancelled_to_on-hold' => $this->default_defer_time, // Order on hold. | |
| //'woocommerce_order_status_pending_to_on-hold' => $this->default_defer_time, // Order on hold. | |
| ); | |
| // Wait until other plugins loaded so that we can check for some specific plugins. | |
| add_action( 'plugins_loaded', array( $this, 'init' ) ); | |
| } | |
| // Set up WordPress specfic actions. | |
| public function init() { | |
| // Use a filter to enable (or disable) debug mode. | |
| $this->debug_mode = apply_filters( 'DeferSendingWooCommerceEmails_debug_mode', $this->debug_mode ); | |
| // Set all WooCommerce emails to be deferred. | |
| add_filter( 'woocommerce_defer_transactional_emails', '__return_true' ); | |
| // Allow most emails to be sent as normal but prevent emails listed in $this->email_id_to_defer. Schedule them for a time in the future. | |
| add_filter( 'woocommerce_allow_send_queued_transactional_email', array( $this, 'whether_send_queued_wc_email' ), 10, 3 ); | |
| // This is the scheduled function that will send the email. | |
| add_action( 'send_deferred_woocommerce_email', array( $this, 'send_deferred_woocommerce_email' ), 10, 2 ); | |
| // If Custom Order Statuses for WooCommerce by Nuggethon is active (https://wordpress.org/plugins/custom-order-statuses-for-woocommerce/) | |
| // attached to some additional hooks to defer new order status emails. | |
| if ( class_exists( 'WOOCOS_Email_Manager' ) ) { | |
| add_action( 'woocommerce_order_status_changed', array( $this, 'defer_woocos_emails' ), 1, 3 ); | |
| //add_action( 'woocommerce_order_status_changed_to_woocos', array( $this, 'woocos_email_trigger' ), 5, 4 ); | |
| // This is the scheduled function that will send the WooCOS emails. | |
| add_action( 'send_deferred_woocos_mails', array( $this, 'send_deferred_woocos_mails' ), 10, 3 ); | |
| } | |
| // DEBUG: Add the order modification time and current time to prove that the email was intentionally delayed. | |
| if ( $this->debug_mode ) { | |
| add_action( 'woocommerce_email_order_details', array( $this, 'add_defer_length_info_to_order_email' ), 5, 4 ); | |
| } | |
| // Uncomment this to log the before and after statuses so you know what ones | |
| // to add to the $this->email_id_to_defer array. | |
| //add_action('woocommerce_order_status_changed', array( $this, 'order_status_changed' ), 10, 4); | |
| } | |
| // Log the before and after status to discover which one to add to $this->email_id_to_defer array. | |
| public function order_status_changed( $id, $from, $to, $order ) { | |
| if ( $this->debug_mode ) { | |
| error_log( 'Order ID: ' . $id ); | |
| error_log( 'Status from: ' . $from ); | |
| error_log( 'Status to: ' . $to ); | |
| } | |
| } | |
| private function get_email_defer_time( $filter ) { | |
| if ( array_key_exists( $filter, $this->email_id_to_defer ) ) { | |
| return $this->email_id_to_defer[ $filter ]; | |
| } | |
| return $this->default_defer_time; | |
| } | |
| public function whether_send_queued_wc_email( $true, $filter, $args ) { | |
| if ( $this->debug_mode ) { | |
| error_log( 'woocommerce_allow_send_queued_transactional_email $filter: ' . var_export( $filter, true ) ); | |
| error_log( 'woocommerce_allow_send_queued_transactional_email order_number: ' . var_export( $args[ 0 ], true ) ); | |
| error_log( 'woocommerce_allow_send_queued_transactional_email defer this email? ' . array_key_exists( $filter, $this->email_id_to_defer ) ? 'Yes' : 'No' ); | |
| } | |
| if ( array_key_exists( $filter, $this->email_id_to_defer ) ) { | |
| // TODO: Consider verifying that $args[0] is a valid order number. | |
| //$order = wc_get_order( $args[ 0 ] ); | |
| //$action_args = array( 'filter' => $filter, 'args' => $args ); | |
| // Call Action Scheduler instead of WP Cron. Args will be filter and $args. | |
| $event_id = as_schedule_single_action( time() + $this->get_email_defer_time( $filter ), 'send_deferred_woocommerce_email', array( $filter, $args ) ); | |
| //$order_num = $args[ 0 ]; | |
| //error_log( sprintf( 'woocommerce_allow_send_queued_transactional_email: Defer a %s email for order: %s for %d seconds (event ID: %d).', $filter, $order_num, $this->get_email_defer_time( $filter ), $event_id ) ); | |
| return false; | |
| } | |
| if ( $this->debug_mode ) { | |
| error_log( 'woocommerce_allow_send_queued_transactional_email: Ok to send email.' ); | |
| } | |
| return $true; | |
| } | |
| // Send the deferred email for order $order_id. | |
| public function send_deferred_woocommerce_email( $filter, $args = array() ) { | |
| if ( $this->debug_mode ) { | |
| error_log( 'send_deferred_woocommerce_email for $filter: ' . $filter ); | |
| error_log( 'send_deferred_woocommerce_email for $args: ' . var_export( $args, true ) ); | |
| } | |
| // Use same code as WC_Emails::send_queued_transactional_email() in woocommerce/includes/class-wc-emails.php | |
| WC_Emails::instance(); | |
| // Ensure gateways are loaded in case they need to insert data into the emails. | |
| WC()->payment_gateways(); | |
| WC()->shipping(); | |
| do_action_ref_array( $filter . '_notification', $args ); | |
| } | |
| public function woocos_email_trigger( $order_id, $custom_order_status, $new_status, $posted ) { | |
| if ( $this->debug_mode ) { | |
| error_log( '$order_id:' . var_export( $order_id, true ) ); | |
| error_log( '$custom_order_status:' . var_export( $custom_order_status, true ) ); | |
| error_log( '$new_status:' . var_export( $new_status, true ) ); | |
| error_log( '$posted:' . var_export( $posted, true ) ); | |
| } | |
| } | |
| // Remove the WooCOS function and schedule it to be called it later. | |
| public function defer_woocos_emails( $order_id, $old_status, $new_status ) { | |
| remove_action('woocommerce_order_status_changed', 'woocos_add_custom_order_status_actions', 10, 3); | |
| $event_id = as_schedule_single_action( time() + $this->get_email_defer_time( 'woocos' ), 'send_deferred_woocos_mails', array( $order_id, $old_status, $new_status ) ); | |
| } | |
| public function send_deferred_woocos_mails( $order_id, $old_status, $new_status ) { | |
| woocos_add_custom_order_status_actions( $order_id, $old_status, $new_status ); | |
| } | |
| // This is an experimental function to add the date/time the order was modified and | |
| // the date/time the email was sent into email - to demonstrate that the deferring code worked. | |
| public function add_defer_length_info_to_order_email( $order, $sent_to_admin, $plain_text, $email ) { | |
| if ( !is_object( $email ) ) { return; } // Ensure $email is an object before testing $email->id. | |
| if ( 'customer_completed_order' == $email->id ) { | |
| if ( $plain_text ) { | |
| printf( '%sThe order was modified at %s and email sent at %s.', "\n", $order->get_date_modified(), current_time( 'mysql' ) ); | |
| } | |
| else { | |
| printf( '<p>The order was modified at <strong>%s</strong> and email sent at <strong>%s</strong>.</p>', $order->get_date_modified(), current_time( 'mysql' ) ); | |
| } | |
| } | |
| } | |
| } | |
| $DeferSendingWooCommerceEmails = new DeferSendingWooCommerceEmails(); |
Author
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Yes, I think that there must be something else clashing. When debug mode is enabled there should be a number of messages in the log.
Please email me at [email protected] so we can investigate this.