No Description

class-wc-emails.php 22KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732
  1. <?php
  2. /**
  3. * Transactional Emails Controller
  4. *
  5. * WooCommerce Emails Class which handles the sending on transactional emails and email templates. This class loads in available emails.
  6. *
  7. * @package WooCommerce\Classes\Emails
  8. * @version 2.3.0
  9. */
  10. use Automattic\Jetpack\Constants;
  11. defined( 'ABSPATH' ) || exit;
  12. /**
  13. * Emails class.
  14. */
  15. class WC_Emails {
  16. /**
  17. * Array of email notification classes
  18. *
  19. * @var WC_Email[]
  20. */
  21. public $emails = array();
  22. /**
  23. * The single instance of the class
  24. *
  25. * @var WC_Emails
  26. */
  27. protected static $_instance = null;
  28. /**
  29. * Background emailer class.
  30. *
  31. * @var WC_Background_Emailer
  32. */
  33. protected static $background_emailer = null;
  34. /**
  35. * Main WC_Emails Instance.
  36. *
  37. * Ensures only one instance of WC_Emails is loaded or can be loaded.
  38. *
  39. * @since 2.1
  40. * @static
  41. * @return WC_Emails Main instance
  42. */
  43. public static function instance() {
  44. if ( is_null( self::$_instance ) ) {
  45. self::$_instance = new self();
  46. }
  47. return self::$_instance;
  48. }
  49. /**
  50. * Cloning is forbidden.
  51. *
  52. * @since 2.1
  53. */
  54. public function __clone() {
  55. wc_doing_it_wrong( __FUNCTION__, __( 'Cloning is forbidden.', 'woocommerce' ), '2.1' );
  56. }
  57. /**
  58. * Unserializing instances of this class is forbidden.
  59. *
  60. * @since 2.1
  61. */
  62. public function __wakeup() {
  63. wc_doing_it_wrong( __FUNCTION__, __( 'Unserializing instances of this class is forbidden.', 'woocommerce' ), '2.1' );
  64. }
  65. /**
  66. * Hook in all transactional emails.
  67. */
  68. public static function init_transactional_emails() {
  69. $email_actions = apply_filters(
  70. 'woocommerce_email_actions',
  71. array(
  72. 'woocommerce_low_stock',
  73. 'woocommerce_no_stock',
  74. 'woocommerce_product_on_backorder',
  75. 'woocommerce_order_status_pending_to_processing',
  76. 'woocommerce_order_status_pending_to_completed',
  77. 'woocommerce_order_status_processing_to_cancelled',
  78. 'woocommerce_order_status_pending_to_failed',
  79. 'woocommerce_order_status_pending_to_on-hold',
  80. 'woocommerce_order_status_failed_to_processing',
  81. 'woocommerce_order_status_failed_to_completed',
  82. 'woocommerce_order_status_failed_to_on-hold',
  83. 'woocommerce_order_status_cancelled_to_processing',
  84. 'woocommerce_order_status_cancelled_to_completed',
  85. 'woocommerce_order_status_cancelled_to_on-hold',
  86. 'woocommerce_order_status_on-hold_to_processing',
  87. 'woocommerce_order_status_on-hold_to_cancelled',
  88. 'woocommerce_order_status_on-hold_to_failed',
  89. 'woocommerce_order_status_completed',
  90. 'woocommerce_order_fully_refunded',
  91. 'woocommerce_order_partially_refunded',
  92. 'woocommerce_new_customer_note',
  93. 'woocommerce_created_customer',
  94. )
  95. );
  96. if ( apply_filters( 'woocommerce_defer_transactional_emails', false ) ) {
  97. self::$background_emailer = new WC_Background_Emailer();
  98. foreach ( $email_actions as $action ) {
  99. add_action( $action, array( __CLASS__, 'queue_transactional_email' ), 10, 10 );
  100. }
  101. } else {
  102. foreach ( $email_actions as $action ) {
  103. add_action( $action, array( __CLASS__, 'send_transactional_email' ), 10, 10 );
  104. }
  105. }
  106. }
  107. /**
  108. * Queues transactional email so it's not sent in current request if enabled,
  109. * otherwise falls back to send now.
  110. *
  111. * @param mixed ...$args Optional arguments.
  112. */
  113. public static function queue_transactional_email( ...$args ) {
  114. if ( is_a( self::$background_emailer, 'WC_Background_Emailer' ) ) {
  115. self::$background_emailer->push_to_queue(
  116. array(
  117. 'filter' => current_filter(),
  118. 'args' => func_get_args(),
  119. )
  120. );
  121. } else {
  122. self::send_transactional_email( ...$args );
  123. }
  124. }
  125. /**
  126. * Init the mailer instance and call the notifications for the current filter.
  127. *
  128. * @internal
  129. *
  130. * @param string $filter Filter name.
  131. * @param array $args Email args (default: []).
  132. */
  133. public static function send_queued_transactional_email( $filter = '', $args = array() ) {
  134. if ( apply_filters( 'woocommerce_allow_send_queued_transactional_email', true, $filter, $args ) ) {
  135. self::instance(); // Init self so emails exist.
  136. // Ensure gateways are loaded in case they need to insert data into the emails.
  137. WC()->payment_gateways();
  138. WC()->shipping();
  139. do_action_ref_array( $filter . '_notification', $args );
  140. }
  141. }
  142. /**
  143. * Init the mailer instance and call the notifications for the current filter.
  144. *
  145. * @internal
  146. *
  147. * @param array $args Email args (default: []).
  148. */
  149. public static function send_transactional_email( $args = array() ) {
  150. try {
  151. $args = func_get_args();
  152. self::instance(); // Init self so emails exist.
  153. do_action_ref_array( current_filter() . '_notification', $args );
  154. } catch ( Exception $e ) {
  155. $error = 'Transactional email triggered fatal error for callback ' . current_filter();
  156. $logger = wc_get_logger();
  157. $logger->critical(
  158. $error . PHP_EOL,
  159. array(
  160. 'source' => 'transactional-emails',
  161. )
  162. );
  163. if ( Constants::is_true( 'WP_DEBUG' ) ) {
  164. trigger_error( $error, E_USER_WARNING ); // phpcs:ignore WordPress.XSS.EscapeOutput.OutputNotEscaped, WordPress.PHP.DevelopmentFunctions.error_log_trigger_error
  165. }
  166. }
  167. }
  168. /**
  169. * Constructor for the email class hooks in all emails that can be sent.
  170. */
  171. public function __construct() {
  172. $this->init();
  173. // Email Header, Footer and content hooks.
  174. add_action( 'woocommerce_email_header', array( $this, 'email_header' ) );
  175. add_action( 'woocommerce_email_footer', array( $this, 'email_footer' ) );
  176. add_action( 'woocommerce_email_order_details', array( $this, 'order_downloads' ), 10, 4 );
  177. add_action( 'woocommerce_email_order_details', array( $this, 'order_details' ), 10, 4 );
  178. add_action( 'woocommerce_email_order_meta', array( $this, 'order_meta' ), 10, 3 );
  179. add_action( 'woocommerce_email_customer_details', array( $this, 'customer_details' ), 10, 3 );
  180. add_action( 'woocommerce_email_customer_details', array( $this, 'email_addresses' ), 20, 3 );
  181. // Hooks for sending emails during store events.
  182. add_action( 'woocommerce_low_stock_notification', array( $this, 'low_stock' ) );
  183. add_action( 'woocommerce_no_stock_notification', array( $this, 'no_stock' ) );
  184. add_action( 'woocommerce_product_on_backorder_notification', array( $this, 'backorder' ) );
  185. add_action( 'woocommerce_created_customer_notification', array( $this, 'customer_new_account' ), 10, 3 );
  186. // Hook for replacing {site_title} in email-footer.
  187. add_filter( 'woocommerce_email_footer_text', array( $this, 'replace_placeholders' ) );
  188. // Let 3rd parties unhook the above via this hook.
  189. do_action( 'woocommerce_email', $this );
  190. }
  191. /**
  192. * Init email classes.
  193. */
  194. public function init() {
  195. // Include email classes.
  196. include_once dirname( __FILE__ ) . '/emails/class-wc-email.php';
  197. $this->emails['WC_Email_New_Order'] = include __DIR__ . '/emails/class-wc-email-new-order.php';
  198. $this->emails['WC_Email_Cancelled_Order'] = include __DIR__ . '/emails/class-wc-email-cancelled-order.php';
  199. $this->emails['WC_Email_Failed_Order'] = include __DIR__ . '/emails/class-wc-email-failed-order.php';
  200. $this->emails['WC_Email_Customer_On_Hold_Order'] = include __DIR__ . '/emails/class-wc-email-customer-on-hold-order.php';
  201. $this->emails['WC_Email_Customer_Processing_Order'] = include __DIR__ . '/emails/class-wc-email-customer-processing-order.php';
  202. $this->emails['WC_Email_Customer_Completed_Order'] = include __DIR__ . '/emails/class-wc-email-customer-completed-order.php';
  203. $this->emails['WC_Email_Customer_Refunded_Order'] = include __DIR__ . '/emails/class-wc-email-customer-refunded-order.php';
  204. $this->emails['WC_Email_Customer_Invoice'] = include __DIR__ . '/emails/class-wc-email-customer-invoice.php';
  205. $this->emails['WC_Email_Customer_Note'] = include __DIR__ . '/emails/class-wc-email-customer-note.php';
  206. $this->emails['WC_Email_Customer_Reset_Password'] = include __DIR__ . '/emails/class-wc-email-customer-reset-password.php';
  207. $this->emails['WC_Email_Customer_New_Account'] = include __DIR__ . '/emails/class-wc-email-customer-new-account.php';
  208. $this->emails = apply_filters( 'woocommerce_email_classes', $this->emails );
  209. }
  210. /**
  211. * Return the email classes - used in admin to load settings.
  212. *
  213. * @return WC_Email[]
  214. */
  215. public function get_emails() {
  216. return $this->emails;
  217. }
  218. /**
  219. * Get from name for email.
  220. *
  221. * @return string
  222. */
  223. public function get_from_name() {
  224. return wp_specialchars_decode( get_option( 'woocommerce_email_from_name' ), ENT_QUOTES );
  225. }
  226. /**
  227. * Get from email address.
  228. *
  229. * @return string
  230. */
  231. public function get_from_address() {
  232. return sanitize_email( get_option( 'woocommerce_email_from_address' ) );
  233. }
  234. /**
  235. * Get the email header.
  236. *
  237. * @param mixed $email_heading Heading for the email.
  238. */
  239. public function email_header( $email_heading ) {
  240. wc_get_template( 'emails/email-header.php', array( 'email_heading' => $email_heading ) );
  241. }
  242. /**
  243. * Get the email footer.
  244. */
  245. public function email_footer() {
  246. wc_get_template( 'emails/email-footer.php' );
  247. }
  248. /**
  249. * Replace placeholder text in strings.
  250. *
  251. * @since 3.7.0
  252. * @param string $string Email footer text.
  253. * @return string Email footer text with any replacements done.
  254. */
  255. public function replace_placeholders( $string ) {
  256. $domain = wp_parse_url( home_url(), PHP_URL_HOST );
  257. return str_replace(
  258. array(
  259. '{site_title}',
  260. '{site_address}',
  261. '{site_url}',
  262. '{woocommerce}',
  263. '{WooCommerce}',
  264. ),
  265. array(
  266. $this->get_blogname(),
  267. $domain,
  268. $domain,
  269. '<a href="https://woocommerce.com">WooCommerce</a>',
  270. '<a href="https://woocommerce.com">WooCommerce</a>',
  271. ),
  272. $string
  273. );
  274. }
  275. /**
  276. * Filter callback to replace {site_title} in email footer
  277. *
  278. * @since 3.3.0
  279. * @deprecated 3.7.0
  280. * @param string $string Email footer text.
  281. * @return string Email footer text with any replacements done.
  282. */
  283. public function email_footer_replace_site_title( $string ) {
  284. wc_deprecated_function( 'WC_Emails::email_footer_replace_site_title', '3.7.0', 'WC_Emails::replace_placeholders' );
  285. return $this->replace_placeholders( $string );
  286. }
  287. /**
  288. * Wraps a message in the woocommerce mail template.
  289. *
  290. * @param string $email_heading Heading text.
  291. * @param string $message Email message.
  292. * @param bool $plain_text Set true to send as plain text. Default to false.
  293. *
  294. * @return string
  295. */
  296. public function wrap_message( $email_heading, $message, $plain_text = false ) {
  297. // Buffer.
  298. ob_start();
  299. do_action( 'woocommerce_email_header', $email_heading, null );
  300. echo wpautop( wptexturize( $message ) ); // WPCS: XSS ok.
  301. do_action( 'woocommerce_email_footer', null );
  302. // Get contents.
  303. $message = ob_get_clean();
  304. return $message;
  305. }
  306. /**
  307. * Send the email.
  308. *
  309. * @param mixed $to Receiver.
  310. * @param mixed $subject Email subject.
  311. * @param mixed $message Message.
  312. * @param string $headers Email headers (default: "Content-Type: text/html\r\n").
  313. * @param string $attachments Attachments (default: "").
  314. * @return bool
  315. */
  316. public function send( $to, $subject, $message, $headers = "Content-Type: text/html\r\n", $attachments = '' ) {
  317. // Send.
  318. $email = new WC_Email();
  319. return $email->send( $to, $subject, $message, $headers, $attachments );
  320. }
  321. /**
  322. * Prepare and send the customer invoice email on demand.
  323. *
  324. * @param int|WC_Order $order Order instance or ID.
  325. */
  326. public function customer_invoice( $order ) {
  327. $email = $this->emails['WC_Email_Customer_Invoice'];
  328. if ( ! is_object( $order ) ) {
  329. $order = wc_get_order( absint( $order ) );
  330. }
  331. $email->trigger( $order->get_id(), $order );
  332. }
  333. /**
  334. * Customer new account welcome email.
  335. *
  336. * @param int $customer_id Customer ID.
  337. * @param array $new_customer_data New customer data.
  338. * @param bool $password_generated If password is generated.
  339. */
  340. public function customer_new_account( $customer_id, $new_customer_data = array(), $password_generated = false ) {
  341. if ( ! $customer_id ) {
  342. return;
  343. }
  344. $user_pass = ! empty( $new_customer_data['user_pass'] ) ? $new_customer_data['user_pass'] : '';
  345. $email = $this->emails['WC_Email_Customer_New_Account'];
  346. $email->trigger( $customer_id, $user_pass, $password_generated );
  347. }
  348. /**
  349. * Show the order details table
  350. *
  351. * @param WC_Order $order Order instance.
  352. * @param bool $sent_to_admin If should sent to admin.
  353. * @param bool $plain_text If is plain text email.
  354. * @param string $email Email address.
  355. */
  356. public function order_details( $order, $sent_to_admin = false, $plain_text = false, $email = '' ) {
  357. if ( $plain_text ) {
  358. wc_get_template(
  359. 'emails/plain/email-order-details.php',
  360. array(
  361. 'order' => $order,
  362. 'sent_to_admin' => $sent_to_admin,
  363. 'plain_text' => $plain_text,
  364. 'email' => $email,
  365. )
  366. );
  367. } else {
  368. wc_get_template(
  369. 'emails/email-order-details.php',
  370. array(
  371. 'order' => $order,
  372. 'sent_to_admin' => $sent_to_admin,
  373. 'plain_text' => $plain_text,
  374. 'email' => $email,
  375. )
  376. );
  377. }
  378. }
  379. /**
  380. * Show order downloads in a table.
  381. *
  382. * @since 3.2.0
  383. * @param WC_Order $order Order instance.
  384. * @param bool $sent_to_admin If should sent to admin.
  385. * @param bool $plain_text If is plain text email.
  386. * @param string $email Email address.
  387. */
  388. public function order_downloads( $order, $sent_to_admin = false, $plain_text = false, $email = '' ) {
  389. $show_downloads = $order->has_downloadable_item() && $order->is_download_permitted() && ! $sent_to_admin && ! is_a( $email, 'WC_Email_Customer_Refunded_Order' );
  390. if ( ! $show_downloads ) {
  391. return;
  392. }
  393. $downloads = $order->get_downloadable_items();
  394. $columns = apply_filters(
  395. 'woocommerce_email_downloads_columns',
  396. array(
  397. 'download-product' => __( 'Product', 'woocommerce' ),
  398. 'download-expires' => __( 'Expires', 'woocommerce' ),
  399. 'download-file' => __( 'Download', 'woocommerce' ),
  400. )
  401. );
  402. if ( $plain_text ) {
  403. wc_get_template(
  404. 'emails/plain/email-downloads.php',
  405. array(
  406. 'order' => $order,
  407. 'sent_to_admin' => $sent_to_admin,
  408. 'plain_text' => $plain_text,
  409. 'email' => $email,
  410. 'downloads' => $downloads,
  411. 'columns' => $columns,
  412. )
  413. );
  414. } else {
  415. wc_get_template(
  416. 'emails/email-downloads.php',
  417. array(
  418. 'order' => $order,
  419. 'sent_to_admin' => $sent_to_admin,
  420. 'plain_text' => $plain_text,
  421. 'email' => $email,
  422. 'downloads' => $downloads,
  423. 'columns' => $columns,
  424. )
  425. );
  426. }
  427. }
  428. /**
  429. * Add order meta to email templates.
  430. *
  431. * @param WC_Order $order Order instance.
  432. * @param bool $sent_to_admin If should sent to admin.
  433. * @param bool $plain_text If is plain text email.
  434. */
  435. public function order_meta( $order, $sent_to_admin = false, $plain_text = false ) {
  436. $fields = apply_filters( 'woocommerce_email_order_meta_fields', array(), $sent_to_admin, $order );
  437. /**
  438. * Deprecated woocommerce_email_order_meta_keys filter.
  439. *
  440. * @since 2.3.0
  441. */
  442. $_fields = apply_filters( 'woocommerce_email_order_meta_keys', array(), $sent_to_admin );
  443. if ( $_fields ) {
  444. foreach ( $_fields as $key => $field ) {
  445. if ( is_numeric( $key ) ) {
  446. $key = $field;
  447. }
  448. $fields[ $key ] = array(
  449. 'label' => wptexturize( $key ),
  450. 'value' => wptexturize( get_post_meta( $order->get_id(), $field, true ) ),
  451. );
  452. }
  453. }
  454. if ( $fields ) {
  455. if ( $plain_text ) {
  456. foreach ( $fields as $field ) {
  457. if ( isset( $field['label'] ) && isset( $field['value'] ) && $field['value'] ) {
  458. echo $field['label'] . ': ' . $field['value'] . "\n"; // WPCS: XSS ok.
  459. }
  460. }
  461. } else {
  462. foreach ( $fields as $field ) {
  463. if ( isset( $field['label'] ) && isset( $field['value'] ) && $field['value'] ) {
  464. echo '<p><strong>' . $field['label'] . ':</strong> ' . $field['value'] . '</p>'; // WPCS: XSS ok.
  465. }
  466. }
  467. }
  468. }
  469. }
  470. /**
  471. * Is customer detail field valid?
  472. *
  473. * @param array $field Field data to check if is valid.
  474. * @return boolean
  475. */
  476. public function customer_detail_field_is_valid( $field ) {
  477. return isset( $field['label'] ) && ! empty( $field['value'] );
  478. }
  479. /**
  480. * Allows developers to add additional customer details to templates.
  481. *
  482. * In versions prior to 3.2 this was used for notes, phone and email but this data has moved.
  483. *
  484. * @param WC_Order $order Order instance.
  485. * @param bool $sent_to_admin If should sent to admin.
  486. * @param bool $plain_text If is plain text email.
  487. */
  488. public function customer_details( $order, $sent_to_admin = false, $plain_text = false ) {
  489. if ( ! is_a( $order, 'WC_Order' ) ) {
  490. return;
  491. }
  492. $fields = array_filter( apply_filters( 'woocommerce_email_customer_details_fields', array(), $sent_to_admin, $order ), array( $this, 'customer_detail_field_is_valid' ) );
  493. if ( ! empty( $fields ) ) {
  494. if ( $plain_text ) {
  495. wc_get_template( 'emails/plain/email-customer-details.php', array( 'fields' => $fields ) );
  496. } else {
  497. wc_get_template( 'emails/email-customer-details.php', array( 'fields' => $fields ) );
  498. }
  499. }
  500. }
  501. /**
  502. * Get the email addresses.
  503. *
  504. * @param WC_Order $order Order instance.
  505. * @param bool $sent_to_admin If should sent to admin.
  506. * @param bool $plain_text If is plain text email.
  507. */
  508. public function email_addresses( $order, $sent_to_admin = false, $plain_text = false ) {
  509. if ( ! is_a( $order, 'WC_Order' ) ) {
  510. return;
  511. }
  512. if ( $plain_text ) {
  513. wc_get_template(
  514. 'emails/plain/email-addresses.php',
  515. array(
  516. 'order' => $order,
  517. 'sent_to_admin' => $sent_to_admin,
  518. )
  519. );
  520. } else {
  521. wc_get_template(
  522. 'emails/email-addresses.php',
  523. array(
  524. 'order' => $order,
  525. 'sent_to_admin' => $sent_to_admin,
  526. )
  527. );
  528. }
  529. }
  530. /**
  531. * Get blog name formatted for emails.
  532. *
  533. * @return string
  534. */
  535. private function get_blogname() {
  536. return wp_specialchars_decode( get_option( 'blogname' ), ENT_QUOTES );
  537. }
  538. /**
  539. * Low stock notification email.
  540. *
  541. * @param WC_Product $product Product instance.
  542. */
  543. public function low_stock( $product ) {
  544. if ( 'no' === get_option( 'woocommerce_notify_low_stock', 'yes' ) ) {
  545. return;
  546. }
  547. /**
  548. * Determine if the current product should trigger a low stock notification
  549. *
  550. * @param int $product_id - The low stock product id
  551. *
  552. * @since 4.7.0
  553. */
  554. if ( false === apply_filters( 'woocommerce_should_send_low_stock_notification', true, $product->get_id() ) ) {
  555. return;
  556. }
  557. $subject = sprintf( '[%s] %s', $this->get_blogname(), __( 'Product low in stock', 'woocommerce' ) );
  558. $message = sprintf(
  559. /* translators: 1: product name 2: items in stock */
  560. __( '%1$s is low in stock. There are %2$d left.', 'woocommerce' ),
  561. html_entity_decode( wp_strip_all_tags( $product->get_formatted_name() ), ENT_QUOTES, get_bloginfo( 'charset' ) ),
  562. html_entity_decode( wp_strip_all_tags( $product->get_stock_quantity() ) )
  563. );
  564. wp_mail(
  565. apply_filters( 'woocommerce_email_recipient_low_stock', get_option( 'woocommerce_stock_email_recipient' ), $product, null ),
  566. apply_filters( 'woocommerce_email_subject_low_stock', $subject, $product, null ),
  567. apply_filters( 'woocommerce_email_content_low_stock', $message, $product ),
  568. apply_filters( 'woocommerce_email_headers', '', 'low_stock', $product, null ),
  569. apply_filters( 'woocommerce_email_attachments', array(), 'low_stock', $product, null )
  570. );
  571. }
  572. /**
  573. * No stock notification email.
  574. *
  575. * @param WC_Product $product Product instance.
  576. */
  577. public function no_stock( $product ) {
  578. if ( 'no' === get_option( 'woocommerce_notify_no_stock', 'yes' ) ) {
  579. return;
  580. }
  581. /**
  582. * Determine if the current product should trigger a no stock notification
  583. *
  584. * @param int $product_id - The out of stock product id
  585. *
  586. * @since 4.6.0
  587. */
  588. if ( false === apply_filters( 'woocommerce_should_send_no_stock_notification', true, $product->get_id() ) ) {
  589. return;
  590. }
  591. $subject = sprintf( '[%s] %s', $this->get_blogname(), __( 'Product out of stock', 'woocommerce' ) );
  592. /* translators: %s: product name */
  593. $message = sprintf( __( '%s is out of stock.', 'woocommerce' ), html_entity_decode( wp_strip_all_tags( $product->get_formatted_name() ), ENT_QUOTES, get_bloginfo( 'charset' ) ) );
  594. wp_mail(
  595. apply_filters( 'woocommerce_email_recipient_no_stock', get_option( 'woocommerce_stock_email_recipient' ), $product, null ),
  596. apply_filters( 'woocommerce_email_subject_no_stock', $subject, $product, null ),
  597. apply_filters( 'woocommerce_email_content_no_stock', $message, $product ),
  598. apply_filters( 'woocommerce_email_headers', '', 'no_stock', $product, null ),
  599. apply_filters( 'woocommerce_email_attachments', array(), 'no_stock', $product, null )
  600. );
  601. }
  602. /**
  603. * Backorder notification email.
  604. *
  605. * @param array $args Arguments.
  606. */
  607. public function backorder( $args ) {
  608. $args = wp_parse_args(
  609. $args,
  610. array(
  611. 'product' => '',
  612. 'quantity' => '',
  613. 'order_id' => '',
  614. )
  615. );
  616. $order = wc_get_order( $args['order_id'] );
  617. if (
  618. ! $args['product'] ||
  619. ! is_object( $args['product'] ) ||
  620. ! $args['quantity'] ||
  621. ! $order
  622. ) {
  623. return;
  624. }
  625. $subject = sprintf( '[%s] %s', $this->get_blogname(), __( 'Product backorder', 'woocommerce' ) );
  626. /* translators: 1: product quantity 2: product name 3: order number */
  627. $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() );
  628. wp_mail(
  629. apply_filters( 'woocommerce_email_recipient_backorder', get_option( 'woocommerce_stock_email_recipient' ), $args, null ),
  630. apply_filters( 'woocommerce_email_subject_backorder', $subject, $args, null ),
  631. apply_filters( 'woocommerce_email_content_backorder', $message, $args ),
  632. apply_filters( 'woocommerce_email_headers', '', 'backorder', $args, null ),
  633. apply_filters( 'woocommerce_email_attachments', array(), 'backorder', $args, null )
  634. );
  635. }
  636. /**
  637. * Adds Schema.org markup for order in JSON-LD format.
  638. *
  639. * @deprecated 3.0.0
  640. * @see WC_Structured_Data::generate_order_data()
  641. *
  642. * @since 2.6.0
  643. * @param WC_Order $order Order instance.
  644. * @param bool $sent_to_admin If should sent to admin.
  645. * @param bool $plain_text If is plain text email.
  646. */
  647. public function order_schema_markup( $order, $sent_to_admin = false, $plain_text = false ) {
  648. wc_deprecated_function( 'WC_Emails::order_schema_markup', '3.0', 'WC_Structured_Data::generate_order_data' );
  649. WC()->structured_data->generate_order_data( $order, $sent_to_admin, $plain_text );
  650. WC()->structured_data->output_structured_data();
  651. }
  652. }