WC_Emails

Transactional Emails Controller.

Defined (1)

The class is defined in the following location(s).

/includes/class-wc-emails.php  
  1. class WC_Emails { 
  2.  
  3. /** @var array Array of email notification classes */ 
  4. public $emails; 
  5.  
  6. /** @var WC_Emails The single instance of the class */ 
  7. protected static $_instance = null; 
  8.  
  9. /** 
  10. * Background emailer class. 
  11. */ 
  12. protected static $background_emailer; 
  13.  
  14. /** 
  15. * Main WC_Emails Instance. 
  16. * Ensures only one instance of WC_Emails is loaded or can be loaded. 
  17. * @since 2.1 
  18. * @static 
  19. * @return WC_Emails Main instance 
  20. */ 
  21. public static function instance() { 
  22. if ( is_null( self::$_instance ) ) { 
  23. self::$_instance = new self(); 
  24. return self::$_instance; 
  25.  
  26. /** 
  27. * Cloning is forbidden. 
  28. * @since 2.1 
  29. */ 
  30. public function __clone() { 
  31. wc_doing_it_wrong( __FUNCTION__, __( 'Cheatin’ huh?', 'woocommerce' ), '2.1' ); 
  32.  
  33. /** 
  34. * Unserializing instances of this class is forbidden. 
  35. * @since 2.1 
  36. */ 
  37. public function __wakeup() { 
  38. wc_doing_it_wrong( __FUNCTION__, __( 'Cheatin’ huh?', 'woocommerce' ), '2.1' ); 
  39.  
  40. /** 
  41. * Hook in all transactional emails. 
  42. */ 
  43. public static function init_transactional_emails() { 
  44. $email_actions = apply_filters( 'woocommerce_email_actions', array( 
  45. 'woocommerce_low_stock',  
  46. 'woocommerce_no_stock',  
  47. 'woocommerce_product_on_backorder',  
  48. 'woocommerce_order_status_pending_to_processing',  
  49. 'woocommerce_order_status_pending_to_completed',  
  50. 'woocommerce_order_status_pending_to_cancelled',  
  51. 'woocommerce_order_status_pending_to_failed',  
  52. 'woocommerce_order_status_pending_to_on-hold',  
  53. 'woocommerce_order_status_failed_to_processing',  
  54. 'woocommerce_order_status_failed_to_completed',  
  55. 'woocommerce_order_status_failed_to_on-hold',  
  56. 'woocommerce_order_status_on-hold_to_processing',  
  57. 'woocommerce_order_status_on-hold_to_cancelled',  
  58. 'woocommerce_order_status_on-hold_to_failed',  
  59. 'woocommerce_order_status_completed',  
  60. 'woocommerce_order_fully_refunded',  
  61. 'woocommerce_order_partially_refunded',  
  62. 'woocommerce_new_customer_note',  
  63. 'woocommerce_created_customer',  
  64. ) ); 
  65.  
  66. if ( apply_filters( 'woocommerce_defer_transactional_emails', false ) ) { 
  67. self::$background_emailer = new WC_Background_Emailer(); 
  68.  
  69. foreach ( $email_actions as $action ) { 
  70. add_action( $action, array( __CLASS__, 'queue_transactional_email' ), 10, 10 ); 
  71. } else { 
  72. foreach ( $email_actions as $action ) { 
  73. add_action( $action, array( __CLASS__, 'send_transactional_email' ), 10, 10 ); 
  74.  
  75. /** 
  76. * Queues transactional email so it's not sent in current request if enabled,  
  77. * otherwise falls back to send now. 
  78. */ 
  79. public static function queue_transactional_email() { 
  80. if ( is_a( self::$background_emailer, 'WC_Background_Emailer' ) ) { 
  81. self::$background_emailer->push_to_queue( array( 
  82. 'filter' => current_filter(),  
  83. 'args' => func_get_args(),  
  84. ) ); 
  85. } else { 
  86. call_user_func_array( array( __CLASS__, 'send_transactional_email' ), func_get_args() ); 
  87.  
  88. /** 
  89. * Init the mailer instance and call the notifications for the current filter. 
  90. * @internal 
  91. * @param string $filter Filter name. 
  92. * @param array $args Email args (default: []). 
  93. */ 
  94. public static function send_queued_transactional_email( $filter = '', $args = array() ) { 
  95. if ( apply_filters( 'woocommerce_allow_send_queued_transactional_email', true, $filter, $args ) ) { 
  96. self::instance(); // Init self so emails exist. 
  97.  
  98. // Ensure gateways are loaded in case they need to insert data into the emails. 
  99. WC()->payment_gateways(); 
  100. WC()->shipping(); 
  101.  
  102. do_action_ref_array( $filter . '_notification', $args ); 
  103.  
  104. /** 
  105. * Init the mailer instance and call the notifications for the current filter. 
  106. * @internal 
  107. * @param array $args Email args (default: []). 
  108. */ 
  109. public static function send_transactional_email( $args = array() ) { 
  110. try { 
  111. $args = func_get_args(); 
  112. self::instance(); // Init self so emails exist. 
  113. do_action_ref_array( current_filter() . '_notification', $args ); 
  114. } catch ( Exception $e ) { 
  115. if ( defined( 'WP_DEBUG' ) && WP_DEBUG ) { 
  116. trigger_error( 'Transactional email triggered fatal error for callback ' . current_filter(), E_USER_WARNING ); 
  117.  
  118. /** 
  119. * Constructor for the email class hooks in all emails that can be sent. 
  120. */ 
  121. public function __construct() { 
  122. $this->init(); 
  123.  
  124. // Email Header, Footer and content hooks 
  125. add_action( 'woocommerce_email_header', array( $this, 'email_header' ) ); 
  126. add_action( 'woocommerce_email_footer', array( $this, 'email_footer' ) ); 
  127. add_action( 'woocommerce_email_order_details', array( $this, 'order_details' ), 10, 4 ); 
  128. add_action( 'woocommerce_email_order_meta', array( $this, 'order_meta' ), 10, 3 ); 
  129. add_action( 'woocommerce_email_customer_details', array( $this, 'customer_details' ), 10, 3 ); 
  130. add_action( 'woocommerce_email_customer_details', array( $this, 'email_addresses' ), 20, 3 ); 
  131.  
  132. // Hooks for sending emails during store events 
  133. add_action( 'woocommerce_low_stock_notification', array( $this, 'low_stock' ) ); 
  134. add_action( 'woocommerce_no_stock_notification', array( $this, 'no_stock' ) ); 
  135. add_action( 'woocommerce_product_on_backorder_notification', array( $this, 'backorder' ) ); 
  136. add_action( 'woocommerce_created_customer_notification', array( $this, 'customer_new_account' ), 10, 3 ); 
  137.  
  138. // Let 3rd parties unhook the above via this hook 
  139. do_action( 'woocommerce_email', $this ); 
  140.  
  141. /** 
  142. * Init email classes. 
  143. */ 
  144. public function init() { 
  145. // Include email classes 
  146. include_once( dirname( __FILE__ ) . '/emails/class-wc-email.php' ); 
  147.  
  148. $this->emails['WC_Email_New_Order'] = include( 'emails/class-wc-email-new-order.php' ); 
  149. $this->emails['WC_Email_Cancelled_Order'] = include( 'emails/class-wc-email-cancelled-order.php' ); 
  150. $this->emails['WC_Email_Failed_Order'] = include( 'emails/class-wc-email-failed-order.php' ); 
  151. $this->emails['WC_Email_Customer_On_Hold_Order'] = include( 'emails/class-wc-email-customer-on-hold-order.php' ); 
  152. $this->emails['WC_Email_Customer_Processing_Order'] = include( 'emails/class-wc-email-customer-processing-order.php' ); 
  153. $this->emails['WC_Email_Customer_Completed_Order'] = include( 'emails/class-wc-email-customer-completed-order.php' ); 
  154. $this->emails['WC_Email_Customer_Refunded_Order'] = include( 'emails/class-wc-email-customer-refunded-order.php' ); 
  155. $this->emails['WC_Email_Customer_Invoice'] = include( 'emails/class-wc-email-customer-invoice.php' ); 
  156. $this->emails['WC_Email_Customer_Note'] = include( 'emails/class-wc-email-customer-note.php' ); 
  157. $this->emails['WC_Email_Customer_Reset_Password'] = include( 'emails/class-wc-email-customer-reset-password.php' ); 
  158. $this->emails['WC_Email_Customer_New_Account'] = include( 'emails/class-wc-email-customer-new-account.php' ); 
  159.  
  160. $this->emails = apply_filters( 'woocommerce_email_classes', $this->emails ); 
  161.  
  162. // include css inliner 
  163. if ( ! class_exists( 'Emogrifier' ) && class_exists( 'DOMDocument' ) ) { 
  164. include_once( dirname( __FILE__ ) . '/libraries/class-emogrifier.php' ); 
  165.  
  166. /** 
  167. * Return the email classes - used in admin to load settings. 
  168. * @return array 
  169. */ 
  170. public function get_emails() { 
  171. return $this->emails; 
  172.  
  173. /** 
  174. * Get from name for email. 
  175. * @return string 
  176. */ 
  177. public function get_from_name() { 
  178. return wp_specialchars_decode( get_option( 'woocommerce_email_from_name' ), ENT_QUOTES ); 
  179.  
  180. /** 
  181. * Get from email address. 
  182. * @return string 
  183. */ 
  184. public function get_from_address() { 
  185. return sanitize_email( get_option( 'woocommerce_email_from_address' ) ); 
  186.  
  187. /** 
  188. * Get the email header. 
  189. * @param mixed $email_heading heading for the email 
  190. */ 
  191. public function email_header( $email_heading ) { 
  192. wc_get_template( 'emails/email-header.php', array( 'email_heading' => $email_heading ) ); 
  193.  
  194. /** 
  195. * Get the email footer. 
  196. */ 
  197. public function email_footer() { 
  198. wc_get_template( 'emails/email-footer.php' ); 
  199.  
  200. /** 
  201. * Wraps a message in the woocommerce mail template. 
  202. * @param mixed $email_heading 
  203. * @param string $message 
  204. * @return string 
  205. */ 
  206. public function wrap_message( $email_heading, $message, $plain_text = false ) { 
  207. // Buffer 
  208. ob_start(); 
  209.  
  210. do_action( 'woocommerce_email_header', $email_heading ); 
  211.  
  212. echo wpautop( wptexturize( $message ) ); 
  213.  
  214. do_action( 'woocommerce_email_footer' ); 
  215.  
  216. // Get contents 
  217. $message = ob_get_clean(); 
  218.  
  219. return $message; 
  220.  
  221. /** 
  222. * Send the email. 
  223. * @param mixed $to 
  224. * @param mixed $subject 
  225. * @param mixed $message 
  226. * @param string $headers (default: "Content-Type: text/html\r\n") 
  227. * @param string $attachments (default: "") 
  228. * @return bool 
  229. */ 
  230. public function send( $to, $subject, $message, $headers = "Content-Type: text/html\r\n", $attachments = "" ) { 
  231. // Send 
  232. $email = new WC_Email(); 
  233. return $email->send( $to, $subject, $message, $headers, $attachments ); 
  234.  
  235. /** 
  236. * Prepare and send the customer invoice email on demand. 
  237. */ 
  238. public function customer_invoice( $order ) { 
  239. $email = $this->emails['WC_Email_Customer_Invoice']; 
  240.  
  241. if ( ! is_object( $order ) ) { 
  242. $order = wc_get_order( absint( $order ) ); 
  243.  
  244. $email->trigger( $order->get_id(), $order ); 
  245.  
  246. /** 
  247. * Customer new account welcome email. 
  248. * @param int $customer_id 
  249. * @param array $new_customer_data 
  250. */ 
  251. public function customer_new_account( $customer_id, $new_customer_data = array(), $password_generated = false ) { 
  252. if ( ! $customer_id ) { 
  253. return; 
  254.  
  255. $user_pass = ! empty( $new_customer_data['user_pass'] ) ? $new_customer_data['user_pass'] : ''; 
  256.  
  257. $email = $this->emails['WC_Email_Customer_New_Account']; 
  258. $email->trigger( $customer_id, $user_pass, $password_generated ); 
  259.  
  260. /** 
  261. * Show the order details table 
  262. */ 
  263. public function order_details( $order, $sent_to_admin = false, $plain_text = false, $email = '' ) { 
  264. if ( $plain_text ) { 
  265. wc_get_template( 'emails/plain/email-order-details.php', array( 'order' => $order, 'sent_to_admin' => $sent_to_admin, 'plain_text' => $plain_text, 'email' => $email ) ); 
  266. } else { 
  267. wc_get_template( 'emails/email-order-details.php', array( 'order' => $order, 'sent_to_admin' => $sent_to_admin, 'plain_text' => $plain_text, 'email' => $email ) ); 
  268.  
  269. /** 
  270. * Add order meta to email templates. 
  271. * @param mixed $order 
  272. * @param bool $sent_to_admin (default: false) 
  273. * @param bool $plain_text (default: false) 
  274. * @return string 
  275. */ 
  276. public function order_meta( $order, $sent_to_admin = false, $plain_text = false ) { 
  277. $fields = apply_filters( 'woocommerce_email_order_meta_fields', array(), $sent_to_admin, $order ); 
  278.  
  279. /** 
  280. * Deprecated woocommerce_email_order_meta_keys filter. 
  281. * @since 2.3.0 
  282. */ 
  283. $_fields = apply_filters( 'woocommerce_email_order_meta_keys', array(), $sent_to_admin ); 
  284.  
  285. if ( $_fields ) { 
  286. foreach ( $_fields as $key => $field ) { 
  287. if ( is_numeric( $key ) ) { 
  288. $key = $field; 
  289.  
  290. $fields[ $key ] = array( 
  291. 'label' => wptexturize( $key ),  
  292. 'value' => wptexturize( get_post_meta( $order->get_id(), $field, true ) ),  
  293. ); 
  294.  
  295. if ( $fields ) { 
  296.  
  297. if ( $plain_text ) { 
  298.  
  299. foreach ( $fields as $field ) { 
  300. if ( isset( $field['label'] ) && isset( $field['value'] ) && $field['value'] ) { 
  301. echo $field['label'] . ': ' . $field['value'] . "\n"; 
  302. } else { 
  303.  
  304. foreach ( $fields as $field ) { 
  305. if ( isset( $field['label'] ) && isset( $field['value'] ) && $field['value'] ) { 
  306. echo '<p><strong>' . $field['label'] . ':</strong> ' . $field['value'] . '</p>'; 
  307.  
  308. /** 
  309. * Is customer detail field valid? 
  310. * @param array $field 
  311. * @return boolean 
  312. */ 
  313. public function customer_detail_field_is_valid( $field ) { 
  314. return isset( $field['label'] ) && ! empty( $field['value'] ); 
  315.  
  316. /** 
  317. * Add customer details to email templates. 
  318. * @param WC_Order $order 
  319. * @param bool $sent_to_admin (default: false) 
  320. * @param bool $plain_text (default: false) 
  321. * @return string 
  322. */ 
  323. public function customer_details( $order, $sent_to_admin = false, $plain_text = false ) { 
  324. if ( ! is_a( $order, 'WC_Order' ) ) { 
  325. return; 
  326. $fields = array(); 
  327.  
  328. if ( $order->get_customer_note() ) { 
  329. $fields['customer_note'] = array( 
  330. 'label' => __( 'Note', 'woocommerce' ),  
  331. 'value' => wptexturize( $order->get_customer_note() ),  
  332. ); 
  333.  
  334. if ( $order->get_billing_email() ) { 
  335. $fields['billing_email'] = array( 
  336. 'label' => __( 'Email address', 'woocommerce' ),  
  337. 'value' => wptexturize( $order->get_billing_email() ),  
  338. ); 
  339.  
  340. if ( $order->get_billing_phone() ) { 
  341. $fields['billing_phone'] = array( 
  342. 'label' => __( 'Phone', 'woocommerce' ),  
  343. 'value' => wptexturize( $order->get_billing_phone() ),  
  344. ); 
  345.  
  346. $fields = array_filter( apply_filters( 'woocommerce_email_customer_details_fields', $fields, $sent_to_admin, $order ), array( $this, 'customer_detail_field_is_valid' ) ); 
  347.  
  348. if ( $plain_text ) { 
  349. wc_get_template( 'emails/plain/email-customer-details.php', array( 'fields' => $fields ) ); 
  350. } else { 
  351. wc_get_template( 'emails/email-customer-details.php', array( 'fields' => $fields ) ); 
  352.  
  353. /** 
  354. * Get the email addresses. 
  355. */ 
  356. public function email_addresses( $order, $sent_to_admin = false, $plain_text = false ) { 
  357. if ( ! is_a( $order, 'WC_Order' ) ) { 
  358. return; 
  359. if ( $plain_text ) { 
  360. wc_get_template( 'emails/plain/email-addresses.php', array( 'order' => $order ) ); 
  361. } else { 
  362. wc_get_template( 'emails/email-addresses.php', array( 'order' => $order ) ); 
  363.  
  364. /** 
  365. * Get blog name formatted for emails. 
  366. * @return string 
  367. */ 
  368. private function get_blogname() { 
  369. return wp_specialchars_decode( get_option( 'blogname' ), ENT_QUOTES ); 
  370.  
  371. /** 
  372. * Low stock notification email. 
  373. * @param WC_Product $product 
  374. */ 
  375. public function low_stock( $product ) { 
  376. if ( 'no' === get_option( 'woocommerce_notify_low_stock', 'yes' ) ) { 
  377. return; 
  378.  
  379. $subject = sprintf( '[%s] %s', $this->get_blogname(), __( 'Product low in stock', 'woocommerce' ) ); 
  380. /** translators: 1: product name 2: items in stock */ 
  381. $message = sprintf( 
  382. __( '%1$s is low in stock. There are %2$d left.', 'woocommerce' ),  
  383. html_entity_decode( strip_tags( $product->get_formatted_name() ), ENT_QUOTES, get_bloginfo( 'charset' ) ),  
  384. html_entity_decode( strip_tags( $product->get_stock_quantity() ) ) 
  385. ); 
  386.  
  387. wp_mail( 
  388. apply_filters( 'woocommerce_email_recipient_low_stock', get_option( 'woocommerce_stock_email_recipient' ), $product ),  
  389. apply_filters( 'woocommerce_email_subject_low_stock', $subject, $product ),  
  390. apply_filters( 'woocommerce_email_content_low_stock', $message, $product ),  
  391. apply_filters( 'woocommerce_email_headers', '', 'low_stock', $product ),  
  392. apply_filters( 'woocommerce_email_attachments', array(), 'low_stock', $product ) 
  393. ); 
  394.  
  395. /** 
  396. * No stock notification email. 
  397. * @param WC_Product $product 
  398. */ 
  399. public function no_stock( $product ) { 
  400. if ( 'no' === get_option( 'woocommerce_notify_no_stock', 'yes' ) ) { 
  401. return; 
  402.  
  403. $subject = sprintf( '[%s] %s', $this->get_blogname(), __( 'Product out of stock', 'woocommerce' ) ); 
  404. /** translators: %s: product name */ 
  405. $message = sprintf( __( '%s is out of stock.', 'woocommerce' ), html_entity_decode( strip_tags( $product->get_formatted_name() ), ENT_QUOTES, get_bloginfo( 'charset' ) ) ); 
  406.  
  407. wp_mail( 
  408. apply_filters( 'woocommerce_email_recipient_no_stock', get_option( 'woocommerce_stock_email_recipient' ), $product ),  
  409. apply_filters( 'woocommerce_email_subject_no_stock', $subject, $product ),  
  410. apply_filters( 'woocommerce_email_content_no_stock', $message, $product ),  
  411. apply_filters( 'woocommerce_email_headers', '', 'no_stock', $product ),  
  412. apply_filters( 'woocommerce_email_attachments', array(), 'no_stock', $product ) 
  413. ); 
  414.  
  415. /** 
  416. * Backorder notification email. 
  417. * @param array $args 
  418. */ 
  419. public function backorder( $args ) { 
  420. $args = wp_parse_args( $args, array( 
  421. 'product' => '',  
  422. 'quantity' => '',  
  423. 'order_id' => '',  
  424. ) ); 
  425.  
  426. extract( $args ); 
  427.  
  428. if ( ! $product || ! $quantity || ! ( $order = wc_get_order( $order_id ) ) ) { 
  429. return; 
  430.  
  431. $subject = sprintf( '[%s] %s', $this->get_blogname(), __( 'Product backorder', 'woocommerce' ) ); 
  432. $message = sprintf( __( '%1$s units of %2$s have been backordered in order #%3$s.', 'woocommerce' ), $quantity, html_entity_decode( strip_tags( $product->get_formatted_name() ), ENT_QUOTES, get_bloginfo( 'charset' ) ), $order->get_order_number() ); 
  433.  
  434. wp_mail( 
  435. apply_filters( 'woocommerce_email_recipient_backorder', get_option( 'woocommerce_stock_email_recipient' ), $args ),  
  436. apply_filters( 'woocommerce_email_subject_backorder', $subject, $args ),  
  437. apply_filters( 'woocommerce_email_content_backorder', $message, $args ),  
  438. apply_filters( 'woocommerce_email_headers', '', 'backorder', $args ),  
  439. apply_filters( 'woocommerce_email_attachments', array(), 'backorder', $args ) 
  440. ); 
  441.  
  442. /** 
  443. * Adds Schema.org markup for order in JSON-LD format. 
  444. * @deprecated 3.0.0 
  445. * @see WC_Structured_Data::generate_order_data() 
  446. * @since 2.6.0 
  447. * @param mixed $order 
  448. * @param bool $sent_to_admin (default: false) 
  449. * @param bool $plain_text (default: false) 
  450. */ 
  451. public function order_schema_markup( $order, $sent_to_admin = false, $plain_text = false ) { 
  452. wc_deprecated_function( 'WC_Emails::order_schema_markup', '3.0', 'WC_Structured_Data::generate_order_data' ); 
  453.  
  454. WC()->structured_data->generate_order_data( $order, $sent_to_admin, $plain_text ); 
  455. WC()->structured_data->output_structured_data();