<?php use Automattic\Jetpack\Constants; defined('ABSPATH') || exit; class WC_Emails { public $emails = array(); protected static $_instance = null; protected static $background_emailer = null; public static function instance() { if (is_null(self::$_instance)) { self::$_instance = new self(); } return self::$_instance; } public function __clone() { wc_doing_it_wrong(__FUNCTION__, __('Cloning is forbidden.', 'woocommerce'), '2.1'); } public function __wakeup() { wc_doing_it_wrong(__FUNCTION__, __('Unserializing instances of this class is forbidden.', 'woocommerce'), '2.1'); } public static function init_transactional_emails() { $email_actions = apply_filters('woocommerce_email_actions', array('woocommerce_low_stock', 'woocommerce_no_stock', 'woocommerce_product_on_backorder', 'woocommerce_order_status_pending_to_processing', 'woocommerce_order_status_pending_to_completed', 'woocommerce_order_status_processing_to_cancelled', 'woocommerce_order_status_pending_to_failed', 'woocommerce_order_status_pending_to_on-hold', 'woocommerce_order_status_failed_to_processing', 'woocommerce_order_status_failed_to_completed', 'woocommerce_order_status_failed_to_on-hold', 'woocommerce_order_status_cancelled_to_processing', 'woocommerce_order_status_cancelled_to_completed', 'woocommerce_order_status_cancelled_to_on-hold', 'woocommerce_order_status_on-hold_to_processing', 'woocommerce_order_status_on-hold_to_cancelled', 'woocommerce_order_status_on-hold_to_failed', 'woocommerce_order_status_completed', 'woocommerce_order_fully_refunded', 'woocommerce_order_partially_refunded', 'woocommerce_new_customer_note', 'woocommerce_created_customer')); if (apply_filters('woocommerce_defer_transactional_emails', false)) { self::$background_emailer = new WC_Background_Emailer(); foreach ($email_actions as $action) { add_action($action, array(__CLASS__, 'queue_transactional_email'), 10, 10); } } else { foreach ($email_actions as $action) { add_action($action, array(__CLASS__, 'send_transactional_email'), 10, 10); } } } public static function queue_transactional_email(...$args) { if (is_a(self::$background_emailer, 'WC_Background_Emailer')) { self::$background_emailer->push_to_queue(array('filter' => current_filter(), 'args' => func_get_args())); } else { self::send_transactional_email(...$args); } } public static function send_queued_transactional_email($filter = '', $args = array()) { if (apply_filters('woocommerce_allow_send_queued_transactional_email', true, $filter, $args)) { self::instance(); WC()->payment_gateways(); WC()->shipping(); do_action_ref_array($filter . '_notification', $args); } } public static function send_transactional_email($args = array()) { try { $args = func_get_args(); self::instance(); do_action_ref_array(current_filter() . '_notification', $args); } catch (Exception $e) { $error = 'Transactional email triggered fatal error for callback ' . current_filter(); $logger = wc_get_logger(); $logger->critical($error . PHP_EOL, array('source' => 'transactional-emails')); if (Constants::is_true('WP_DEBUG')) { trigger_error($error, E_USER_WARNING); } } } public function __construct() { $this->init(); add_action('woocommerce_email_header', array($this, 'email_header')); add_action('woocommerce_email_footer', array($this, 'email_footer')); add_action('woocommerce_email_order_details', array($this, 'order_downloads'), 10, 4); add_action('woocommerce_email_order_details', array($this, 'order_details'), 10, 4); add_action('woocommerce_email_order_meta', array($this, 'order_meta'), 10, 3); add_action('woocommerce_email_customer_details', array($this, 'customer_details'), 10, 3); add_action('woocommerce_email_customer_details', array($this, 'email_addresses'), 20, 3); add_action('woocommerce_low_stock_notification', array($this, 'low_stock')); add_action('woocommerce_no_stock_notification', array($this, 'no_stock')); add_action('woocommerce_product_on_backorder_notification', array($this, 'backorder')); add_action('woocommerce_created_customer_notification', array($this, 'customer_new_account'), 10, 3); add_filter('woocommerce_email_footer_text', array($this, 'replace_placeholders')); do_action('woocommerce_email', $this); } public function init() { include_once dirname(__FILE__) . '/emails/class-wc-email.php'; $this->emails['WC_Email_New_Order'] = (include __DIR__ . '/emails/class-wc-email-new-order.php'); $this->emails['WC_Email_Cancelled_Order'] = (include __DIR__ . '/emails/class-wc-email-cancelled-order.php'); $this->emails['WC_Email_Failed_Order'] = (include __DIR__ . '/emails/class-wc-email-failed-order.php'); $this->emails['WC_Email_Customer_On_Hold_Order'] = (include __DIR__ . '/emails/class-wc-email-customer-on-hold-order.php'); $this->emails['WC_Email_Customer_Processing_Order'] = (include __DIR__ . '/emails/class-wc-email-customer-processing-order.php'); $this->emails['WC_Email_Customer_Completed_Order'] = (include __DIR__ . '/emails/class-wc-email-customer-completed-order.php'); $this->emails['WC_Email_Customer_Refunded_Order'] = (include __DIR__ . '/emails/class-wc-email-customer-refunded-order.php'); $this->emails['WC_Email_Customer_Invoice'] = (include __DIR__ . '/emails/class-wc-email-customer-invoice.php'); $this->emails['WC_Email_Customer_Note'] = (include __DIR__ . '/emails/class-wc-email-customer-note.php'); $this->emails['WC_Email_Customer_Reset_Password'] = (include __DIR__ . '/emails/class-wc-email-customer-reset-password.php'); $this->emails['WC_Email_Customer_New_Account'] = (include __DIR__ . '/emails/class-wc-email-customer-new-account.php'); $this->emails = apply_filters('woocommerce_email_classes', $this->emails); } public function get_emails() { return $this->emails; } public function get_from_name() { return wp_specialchars_decode(get_option('woocommerce_email_from_name'), ENT_QUOTES); } public function get_from_address() { return sanitize_email(get_option('woocommerce_email_from_address')); } public function email_header($email_heading) { wc_get_template('emails/email-header.php', array('email_heading' => $email_heading)); } public function email_footer() { wc_get_template('emails/email-footer.php'); } public function replace_placeholders($string) { $domain = wp_parse_url(home_url(), PHP_URL_HOST); return str_replace(array('{site_title}', '{site_address}', '{site_url}', '{woocommerce}', '{WooCommerce}'), array($this->get_blogname(), $domain, $domain, '<a href="https://woocommerce.com">WooCommerce</a>', '<a href="https://woocommerce.com">WooCommerce</a>'), $string); } public function email_footer_replace_site_title($string) { wc_deprecated_function('WC_Emails::email_footer_replace_site_title', '3.7.0', 'WC_Emails::replace_placeholders'); return $this->replace_placeholders($string); } public function wrap_message($email_heading, $message, $plain_text = false) { ob_start(); do_action('woocommerce_email_header', $email_heading, null); echo wpautop(wptexturize($message)); do_action('woocommerce_email_footer', null); $message = ob_get_clean(); return $message; } public function send($to, $subject, $message, $headers = "Content-Type: text/html\r\n", $attachments = '') { $email = new WC_Email(); return $email->send($to, $subject, $message, $headers, $attachments); } public function customer_invoice($order) { $email = $this->emails['WC_Email_Customer_Invoice']; if (!is_object($order)) { $order = wc_get_order(absint($order)); } $email->trigger($order->get_id(), $order); } public function customer_new_account($customer_id, $new_customer_data = array(), $password_generated = false) { if (!$customer_id) { return; } $user_pass = !empty($new_customer_data['user_pass']) ? $new_customer_data['user_pass'] : ''; $email = $this->emails['WC_Email_Customer_New_Account']; $email->trigger($customer_id, $user_pass, $password_generated); } public function order_details($order, $sent_to_admin = false, $plain_text = false, $email = '') { if ($plain_text) { wc_get_template('emails/plain/email-order-details.php', array('order' => $order, 'sent_to_admin' => $sent_to_admin, 'plain_text' => $plain_text, 'email' => $email)); } else { wc_get_template('emails/email-order-details.php', array('order' => $order, 'sent_to_admin' => $sent_to_admin, 'plain_text' => $plain_text, 'email' => $email)); } } public function order_downloads($order, $sent_to_admin = false, $plain_text = false, $email = '') { $show_downloads = $order->has_downloadable_item() && $order->is_download_permitted() && !$sent_to_admin && !is_a($email, 'WC_Email_Customer_Refunded_Order'); if (!$show_downloads) { return; } $downloads = $order->get_downloadable_items(); $columns = apply_filters('woocommerce_email_downloads_columns', array('download-product' => __('Product', 'woocommerce'), 'download-expires' => __('Expires', 'woocommerce'), 'download-file' => __('Download', 'woocommerce'))); if ($plain_text) { wc_get_template('emails/plain/email-downloads.php', array('order' => $order, 'sent_to_admin' => $sent_to_admin, 'plain_text' => $plain_text, 'email' => $email, 'downloads' => $downloads, 'columns' => $columns)); } else { wc_get_template('emails/email-downloads.php', array('order' => $order, 'sent_to_admin' => $sent_to_admin, 'plain_text' => $plain_text, 'email' => $email, 'downloads' => $downloads, 'columns' => $columns)); } } public function order_meta($order, $sent_to_admin = false, $plain_text = false) { $fields = apply_filters('woocommerce_email_order_meta_fields', array(), $sent_to_admin, $order); $_fields = apply_filters('woocommerce_email_order_meta_keys', array(), $sent_to_admin); if ($_fields) { foreach ($_fields as $key => $field) { if (is_numeric($key)) { $key = $field; } $fields[$key] = array('label' => wptexturize($key), 'value' => wptexturize(get_post_meta($order->get_id(), $field, true))); } } if ($fields) { if ($plain_text) { foreach ($fields as $field) { if (isset($field['label']) && isset($field['value']) && $field['value']) { echo $field['label'] . ': ' . $field['value'] . "\n"; } } } else { foreach ($fields as $field) { if (isset($field['label']) && isset($field['value']) && $field['value']) { echo '<p><strong>' . $field['label'] . ':</strong> ' . $field['value'] . '</p>'; } } } } } public function customer_detail_field_is_valid($field) { return isset($field['label']) && !empty($field['value']); } public function customer_details($order, $sent_to_admin = false, $plain_text = false) { if (!is_a($order, 'WC_Order')) { return; } $fields = array_filter(apply_filters('woocommerce_email_customer_details_fields', array(), $sent_to_admin, $order), array($this, 'customer_detail_field_is_valid')); if (!empty($fields)) { if ($plain_text) { wc_get_template('emails/plain/email-customer-details.php', array('fields' => $fields)); } else { wc_get_template('emails/email-customer-details.php', array('fields' => $fields)); } } } public function email_addresses($order, $sent_to_admin = false, $plain_text = false) { if (!is_a($order, 'WC_Order')) { return; } if ($plain_text) { wc_get_template('emails/plain/email-addresses.php', array('order' => $order, 'sent_to_admin' => $sent_to_admin)); } else { wc_get_template('emails/email-addresses.php', array('order' => $order, 'sent_to_admin' => $sent_to_admin)); } } private function get_blogname() { return wp_specialchars_decode(get_option('blogname'), ENT_QUOTES); } public function low_stock($product) { if ('no' === get_option('woocommerce_notify_low_stock', 'yes')) { return; } if (false === apply_filters('woocommerce_should_send_low_stock_notification', true, $product->get_id())) { return; } $subject = sprintf('[%s] %s', $this->get_blogname(), __('Product low in stock', 'woocommerce')); $message = sprintf(__('%1$s is low in stock. There are %2$d left.', 'woocommerce'), html_entity_decode(wp_strip_all_tags($product->get_formatted_name()), ENT_QUOTES, get_bloginfo('charset')), html_entity_decode(wp_strip_all_tags($product->get_stock_quantity()))); wp_mail(apply_filters('woocommerce_email_recipient_low_stock', get_option('woocommerce_stock_email_recipient'), $product, null), apply_filters('woocommerce_email_subject_low_stock', $subject, $product, null), apply_filters('woocommerce_email_content_low_stock', $message, $product), apply_filters('woocommerce_email_headers', '', 'low_stock', $product, null), apply_filters('woocommerce_email_attachments', array(), 'low_stock', $product, null)); } public function no_stock($product) { if ('no' === get_option('woocommerce_notify_no_stock', 'yes')) { return; } if (false === apply_filters('woocommerce_should_send_no_stock_notification', true, $product->get_id())) { return; } $subject = sprintf('[%s] %s', $this->get_blogname(), __('Product out of stock', 'woocommerce')); $message = sprintf(__('%s is out of stock.', 'woocommerce'), html_entity_decode(wp_strip_all_tags($product->get_formatted_name()), ENT_QUOTES, get_bloginfo('charset'))); wp_mail(apply_filters('woocommerce_email_recipient_no_stock', get_option('woocommerce_stock_email_recipient'), $product, null), apply_filters('woocommerce_email_subject_no_stock', $subject, $product, null), apply_filters('woocommerce_email_content_no_stock', $message, $product), apply_filters('woocommerce_email_headers', '', 'no_stock', $product, null), apply_filters('woocommerce_email_attachments', array(), 'no_stock', $product, null)); } public function backorder($args) { $args = wp_parse_args($args, array('product' => '', 'quantity' => '', 'order_id' => '')); $order = wc_get_order($args['order_id']); if (!$args['product'] || !is_object($args['product']) || !$args['quantity'] || !$order) { return; } $subject = sprintf('[%s] %s', $this->get_blogname(), __('Product backorder', 'woocommerce')); $message = sprintf(__('%1$s units of %2$s have been backordered in order #%3$s.', 'woocommerce'), $args['quantity'], html_entity_decode(wp_strip_all_tags($args['product']->get_formatted_name()), ENT_QUOTES, get_bloginfo('charset')), $order->get_order_number()); wp_mail(apply_filters('woocommerce_email_recipient_backorder', get_option('woocommerce_stock_email_recipient'), $args, null), apply_filters('woocommerce_email_subject_backorder', $subject, $args, null), apply_filters('woocommerce_email_content_backorder', $message, $args), apply_filters('woocommerce_email_headers', '', 'backorder', $args, null), apply_filters('woocommerce_email_attachments', array(), 'backorder', $args, null)); } public function order_schema_markup($order, $sent_to_admin = false, $plain_text = false) { wc_deprecated_function('WC_Emails::order_schema_markup', '3.0', 'WC_Structured_Data::generate_order_data'); WC()->structured_data->generate_order_data($order, $sent_to_admin, $plain_text); WC()->structured_data->output_structured_data(); } }