PluginProbe ʕ •ᴥ•ʔ
WooCommerce / 10.2.1
WooCommerce v10.2.1
10.8.1 10.8.0 10.8.0-rc.1 10.8.0-beta.2 10.8.0-beta.1 7.8.0-beta.1 7.8.0-beta.2 7.8.0-rc.1 7.8.0-rc.2 7.8.1 7.8.2 7.8.3 7.8.4 7.9.0 7.9.0-beta.1 7.9.0-beta.2 7.9.0-rc.2 7.9.0-rc.3 7.9.1 7.9.2 8.0.0 8.0.0-beta.1 8.0.0-beta.2 8.0.0-rc.1 8.0.0-rc.2 8.0.1 8.0.2 8.0.3 8.0.4 8.0.5 8.1.0 8.1.0-beta.1 8.1.0-rc.1 8.1.0-rc.2 8.1.1 8.1.2 8.1.3 8.1.4 8.2.0 8.2.0-beta.1 8.2.0-rc.1 8.2.0-rc.2 8.2.1 8.2.2 8.2.3 8.2.4 8.2.5 8.3.0 8.3.0-beta.1 8.3.0-rc.1 8.3.0-rc.2 8.3.1 8.3.2 8.3.3 8.3.4 8.4.0 8.4.0-beta.1 8.4.0-rc.1 8.4.1 8.4.2 8.4.3 8.5.0 8.5.0-beta.1 8.5.0-rc.1 8.5.1 8.5.2 8.5.3 8.5.4 8.5.5 8.6.0 8.6.0-beta.1 8.6.0-rc.1 8.6.1 8.6.2 8.6.3 8.6.4 8.7.0 8.7.0-beta.1 8.7.0-beta.2 8.7.0-rc.1 8.7.1 8.7.2 8.7.3 8.8.0 8.8.0-beta.1 8.8.0-rc.1 8.8.1 8.8.2 8.8.3 8.8.4 8.8.5 8.8.6 8.8.7 8.9.0 8.9.0-beta.1 8.9.0-rc.1 8.9.1 8.9.2 8.9.3 8.9.4 8.9.5 9.0.0 9.0.0-beta.1 9.0.0-beta.2 9.0.0-rc.1 9.0.1 9.0.2 9.0.3 9.0.4 9.1.0 9.1.0-beta.1 9.1.0-rc.1 9.1.1 9.1.2 9.1.3 9.1.4 9.1.5 9.1.6 9.2.0 9.2.0-beta.1 9.2.0-rc.1 9.2.1 9.2.2 9.2.3 9.2.4 9.2.5 9.3.0 9.3.0-beta.1 9.3.0-rc.1 9.3.1 9.3.2 9.3.3 9.3.4 9.3.5 9.3.6 9.4.0 9.4.0-beta.1 9.4.0-beta.2 9.4.0-rc.1 9.4.0-rc.2 9.4.0-rc.3 9.4.0-rc.4 9.4.1 9.4.2 9.4.3 9.4.4 9.4.5 9.5.0 9.5.0-beta.1 9.5.0-beta.2 9.5.0-rc.1 9.5.1 9.5.2 9.5.3 9.5.4 9.6.0 9.6.0-beta.1 9.6.0-beta.2 9.6.0-rc.1 9.6.1 9.6.2 9.6.3 9.6.4 9.7.0 9.7.0-beta.1 9.7.0-rc.1 9.7.1 9.7.2 9.7.3 9.8.0 9.8.0-beta.1 9.8.0-rc.1 9.8.1 9.8.2 9.8.3 9.8.4 9.8.5 9.8.6 9.8.7 9.9.0 9.9.0-beta.1 9.9.0-rc.1 9.9.1 9.9.2 9.9.3 9.9.4 9.9.5 9.9.6 9.9.7 3.7.3 7.1.2 3.8.0 7.2.0 3.8.0-beta.1 7.2.0-beta.1 3.8.0-rc.1 7.2.0-beta.2 3.8.0-rc.2 7.2.0-rc.1 3.8.1 7.2.0-rc.2 3.8.2 7.2.1 3.8.3 7.2.2 3.9.0 7.2.3 3.9.0-beta.1 7.2.4 3.9.0-beta.2 7.3.0 3.9.0-rc.1 7.3.0-beta.1 3.9.0-rc.2 7.3.0-beta.2 3.9.0-rc.3 7.3.0-rc.1 3.9.0-rc.4 7.3.0-rc.2 3.9.1 7.3.1 3.9.2 7.4.0 3.9.3 7.4.0-beta.1 3.9.4 7.4.0-beta.2 3.9.5 7.4.0-rc.1 4.0.0 7.4.0-rc.2 4.0.0-beta.1 7.4.1 4.0.0-rc.1 7.4.2 4.0.0-rc.2 7.5.0 4.0.1 7.5.0-beta.1 4.0.2 7.5.0-beta.2 4.0.3 7.5.0-rc.1 4.0.4 7.5.1 4.1.0 7.5.2 4.1.0-beta.1 7.6.0 4.1.0-beta.2 7.6.0-beta.1 4.1.0-rc.1 7.6.0-beta.2 4.1.0-rc.2 7.6.0-rc.1 4.1.1 7.6.0-rc.2 4.1.2 7.6.0-rc.3 4.1.3 7.6.1 4.1.4 7.6.2 4.2.0 7.7.0 4.2.0-RC.1 7.7.0-beta.1 4.2.0-RC.2 7.7.0-beta.2 4.2.0-beta.1 7.7.0-rc.1 4.2.1 7.7.1 4.2.2 7.7.2 4.2.3 7.7.3 4.2.4 7.8.0 4.2.5 4.3.0 4.3.0-beta.1 4.3.0-rc.1 4.3.0-rc.2 4.3.0-rc.3 4.3.1 4.3.2 4.3.3 4.3.4 4.3.5 4.3.6 4.4.0 4.4.0-beta.1 4.4.0-rc.1 4.4.1 4.4.2 4.4.3 4.4.4 4.5.0 4.5.0-beta.1 4.5.0-rc.1 4.5.0-rc.3 4.5.1 4.5.2 4.5.3 4.5.4 4.5.5 4.6.0 4.6.0-beta.1 4.6.0-rc.1 4.6.1 4.6.2 4.6.3 4.6.4 4.6.5 4.7.0 4.7.0-beta.1 4.7.0-beta.2 4.7.0-rc.1 4.7.1 4.7.1-beta.1 4.7.2 4.7.3 4.7.4 4.8.0 4.8.0-beta.1 4.8.0-rc.1 4.8.0-rc.2 4.8.1 4.8.2 4.8.3 4.9.0 4.9.0-beta.1 4.9.0-rc.1 4.9.0-rc.2 4.9.1 4.9.2 4.9.3 4.9.4 4.9.5 5.0.0 5.0.0-beta.1 5.0.0-beta.2 5.0.0-rc.1 5.0.0-rc.2 5.0.0-rc.3 5.0.1 5.0.2 5.0.3 5.1.0 5.1.0-beta.1 5.1.0-rc.1 trunk 5.1.1 10.0.0 5.1.2 10.0.0-rc.1 5.1.3 10.0.0-rc.2 5.2.0 10.0.1 5.2.0-beta.1 10.0.2 5.2.0-rc.1 10.0.3 5.2.0-rc.2 10.0.4 5.2.1 10.0.5 5.2.2 10.0.6 5.2.3 10.1.0 5.2.4 10.1.0-rc.1 5.2.5 10.1.0-rc.2 5.3.0 10.1.0-rc.3 5.3.0-beta.1 10.1.0-rc.4 5.3.0-rc.1 10.1.1 5.3.0-rc.2 10.1.2 5.3.1 10.1.3 5.3.2 10.1.4 5.3.3 10.2.0 5.4.0 10.2.0-beta.1 5.4.0-beta.1 10.2.0-beta.2 5.4.0-rc.1 10.2.0-rc.1 5.4.1 10.2.1 5.4.2 10.2.2 5.4.3 10.2.3 5.4.4 10.2.4 5.4.5 10.3.0 5.5.0 10.3.0-beta.1 5.5.0-beta.1 10.3.0-beta.2 5.5.0-rc.1 10.3.0-rc.1 5.5.0-rc.2 10.3.0-rc.2 5.5.1 10.3.1 5.5.2 10.3.2 5.5.3 10.3.3 5.5.4 10.3.4 5.5.5 10.3.5 5.6.0 10.3.6 5.6.0-beta.1 10.3.7 5.6.0-rc.1 10.3.8 5.6.0-rc.2 10.4.0 5.6.1 10.4.0-beta.1 5.6.2 10.4.0-beta.2 5.6.3 10.4.0-rc.1 5.7.0 10.4.1 5.7.0-beta.1 10.4.2 5.7.0-rc.1 10.4.3 5.7.1 10.4.4 5.7.2 10.5.0 5.7.3 10.5.0-beta.1 5.8.0 10.5.0-beta.2 5.8.0-beta.1 10.5.0-rc.1 5.8.0-beta.2 10.5.0-rc.2 5.8.0-rc.1 10.5.0-rc.3 5.8.1 10.5.1 5.8.2 10.5.2 5.9.0 10.5.3 5.9.0-beta.1 10.6.0 5.9.0-rc.1 10.6.0-beta.1 5.9.0-rc.2 10.6.0-beta.2 5.9.1 10.6.0-rc.1 5.9.2 10.6.1 6.0.0 10.6.2 6.0.0-beta.1 10.7.0 6.0.0-rc.1 10.7.0-beta.1 6.0.1 10.7.0-beta.2 6.0.2 10.7.0-rc.1 6.1.0 3.0.0 6.1.0-beta.1 3.0.1 6.1.0-rc.1 3.0.2 6.1.0-rc.2 3.0.3 6.1.1 3.0.4 6.1.2 3.0.5 6.1.3 3.0.6 6.2.0 3.0.7 6.2.0-beta.1 3.0.8 6.2.0-rc.1 3.0.9 6.2.0-rc.2 3.1.0 6.2.1 3.1.1 6.2.2 3.1.2 6.2.3 3.2.0 6.3.0 3.2.1 6.3.0-beta.1 3.2.2 6.3.0-rc.1 3.2.3 6.3.0-rc.2 3.2.4 6.3.1 3.2.5 6.3.2 3.2.6 6.4.0 3.3.0 6.4.0-beta.1 3.3.1 6.4.0-rc.1 3.3.2 6.4.1 3.3.2-rc.1 6.4.2 3.3.3 6.5.0 3.3.4 6.5.0-beta.1 3.3.5 6.5.0-rc.1 3.3.6 6.5.0-rc.2 3.4.0 6.5.1 3.4.0-beta.1 6.5.2 3.4.0-rc.2 6.6.0 3.4.1 6.6.0-beta.1 3.4.2 6.6.0-rc.1 3.4.3 6.6.0-rc.2 3.4.4 6.6.1 3.4.5 6.6.2 3.4.6 6.7.0 3.4.7 6.7.0-beta.1 3.4.8 6.7.0-beta.2 3.5.0 6.7.0-rc.1 3.5.0-beta.1 6.7.1 3.5.0-rc.1 6.8.0 3.5.0-rc.2 6.8.0-beta.1 3.5.1 6.8.0-beta.2 3.5.10 6.8.0-rc.1 3.5.2 6.8.1 3.5.3 6.8.2 3.5.4 6.8.3 3.5.5 6.9.0 3.5.6 6.9.0-beta.1 3.5.7 6.9.0-beta.2 3.5.8 6.9.0-rc.1 3.5.9 6.9.1 3.6.0 6.9.2 3.6.0-beta.1 6.9.3 3.6.0-rc.1 6.9.4 3.6.0-rc.2 6.9.5 3.6.0-rc.3 7.0.0 3.6.1 7.0.0-beta.1 3.6.2 7.0.0-beta.2 3.6.3 7.0.0-beta.3 3.6.4 7.0.0-rc.1 3.6.5 7.0.0-rc.2 3.6.6 7.0.1 3.6.7 7.0.2 3.7.0 7.1.0 3.7.0-beta.1 7.1.0-beta.1 3.7.0-rc.1 7.1.0-beta.2 3.7.0-rc.2 7.1.0-rc.1 3.7.1 7.1.0-rc.2 3.7.2 7.1.1
woocommerce / includes / class-wc-emails.php
woocommerce / includes Last commit date
abstracts 9 months ago admin 9 months ago blocks 10 months ago cli 11 months ago customizer 11 months ago data-stores 9 months ago emails 9 months ago export 1 year ago gateways 9 months ago import 11 months ago integrations 2 years ago interfaces 1 year ago legacy 1 year ago libraries 1 year ago log-handlers 1 year ago payment-tokens 5 years ago product-usage 1 year ago queue 4 years ago react-admin 9 months ago rest-api 9 months ago shipping 1 year ago shortcodes 10 months ago theme-support 2 years ago tracks 10 months ago traits 5 years ago walkers 5 years ago wccom-site 9 months ago widgets 1 year ago class-wc-ajax.php 10 months ago class-wc-auth.php 1 year ago class-wc-autoloader.php 1 year ago class-wc-background-emailer.php 5 years ago class-wc-background-updater.php 5 years ago class-wc-brands-brand-settings-manager.php 1 year ago class-wc-brands-coupons.php 1 year ago class-wc-brands.php 9 months ago class-wc-breadcrumb.php 5 years ago class-wc-cache-helper.php 10 months ago class-wc-cart-fees.php 2 years ago class-wc-cart-session.php 9 months ago class-wc-cart-totals.php 10 months ago class-wc-cart.php 9 months ago class-wc-checkout.php 1 year ago class-wc-cli.php 9 months ago class-wc-comments.php 9 months ago class-wc-countries.php 1 year ago class-wc-coupon.php 9 months ago class-wc-customer-download-log.php 5 years ago class-wc-customer-download.php 1 year ago class-wc-customer.php 9 months ago class-wc-data-exception.php 8 years ago class-wc-data-store.php 3 years ago class-wc-datetime.php 4 years ago class-wc-deprecated-action-hooks.php 2 years ago class-wc-deprecated-filter-hooks.php 3 years ago class-wc-discounts.php 10 months ago class-wc-download-handler.php 1 year ago class-wc-emails.php 10 months ago class-wc-embed.php 1 year ago class-wc-form-handler.php 9 months ago class-wc-frontend-scripts.php 9 months ago class-wc-geo-ip.php 11 months ago class-wc-geolite-integration.php 6 years ago class-wc-geolocation.php 1 year ago class-wc-https.php 2 years ago class-wc-install.php 9 months ago class-wc-integrations.php 5 years ago class-wc-log-levels.php 2 years ago class-wc-logger.php 1 year ago class-wc-meta-data.php 4 years ago class-wc-order-factory.php 2 years ago class-wc-order-item-coupon.php 4 years ago class-wc-order-item-fee.php 1 year ago class-wc-order-item-meta.php 4 years ago class-wc-order-item-product.php 1 year ago class-wc-order-item-shipping.php 1 year ago class-wc-order-item-tax.php 4 years ago class-wc-order-item.php 1 year ago class-wc-order-query.php 4 years ago class-wc-order-refund.php 1 year ago class-wc-order.php 9 months ago class-wc-payment-gateways.php 9 months ago class-wc-payment-tokens.php 3 years ago class-wc-post-data.php 9 months ago class-wc-post-types.php 1 year ago class-wc-privacy-background-process.php 1 year ago class-wc-privacy-erasers.php 9 months ago class-wc-privacy-exporters.php 4 years ago class-wc-privacy.php 11 months ago class-wc-product-attribute.php 4 years ago class-wc-product-download.php 2 years ago class-wc-product-external.php 1 year ago class-wc-product-factory.php 1 year ago class-wc-product-grouped.php 10 months ago class-wc-product-query.php 1 year ago class-wc-product-simple.php 10 months ago class-wc-product-variable.php 10 months ago class-wc-product-variation.php 1 year ago class-wc-query.php 1 year ago class-wc-rate-limiter.php 4 years ago class-wc-regenerate-images-request.php 3 years ago class-wc-regenerate-images.php 1 year ago class-wc-register-wp-admin-settings.php 4 years ago class-wc-rest-authentication.php 1 year ago class-wc-rest-exception.php 5 years ago class-wc-session-handler.php 9 months ago class-wc-shipping-rate.php 11 months ago class-wc-shipping-zone.php 5 years ago class-wc-shipping-zones.php 5 years ago class-wc-shipping.php 1 year ago class-wc-shortcodes.php 1 year ago class-wc-structured-data.php 10 months ago class-wc-tax.php 10 months ago class-wc-template-loader.php 9 months ago class-wc-tracker.php 11 months ago class-wc-validation.php 2 years ago class-wc-webhook.php 1 year ago class-woocommerce.php 8 months ago wc-account-functions.php 9 months ago wc-attribute-functions.php 9 months ago wc-brands-functions.php 1 year ago wc-cart-functions.php 9 months ago wc-conditional-functions.php 10 months ago wc-core-functions.php 9 months ago wc-coupon-functions.php 10 months ago wc-deprecated-functions.php 9 months ago wc-formatting-functions.php 8 months ago wc-notice-functions.php 11 months ago wc-order-functions.php 9 months ago wc-order-item-functions.php 3 years ago wc-order-step-logger-functions.php 1 year ago wc-page-functions.php 1 year ago wc-product-functions.php 10 months ago wc-rest-functions.php 10 months ago wc-stock-functions.php 1 year ago wc-template-functions.php 9 months ago wc-template-hooks.php 9 months ago wc-term-functions.php 9 months ago wc-update-functions.php 8 months ago wc-user-functions.php 9 months ago wc-webhook-functions.php 1 year ago wc-widget-functions.php 5 years ago
class-wc-emails.php
1227 lines
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
11 declare( strict_types = 1 );
12
13 use Automattic\Jetpack\Constants;
14 use Automattic\WooCommerce\Blocks\Package;
15 use Automattic\WooCommerce\Blocks\Domain\Services\CheckoutFields;
16 use Automattic\WooCommerce\Enums\ProductType;
17 use Automattic\WooCommerce\Internal\Fulfillments\Fulfillment;
18 use Automattic\WooCommerce\Utilities\FeaturesUtil;
19
20 defined( 'ABSPATH' ) || exit;
21
22 /**
23 * Emails class.
24 */
25 class WC_Emails {
26
27 /**
28 * Array of email notification classes
29 *
30 * @var WC_Email[]
31 */
32 public $emails = array();
33
34 /**
35 * The single instance of the class
36 *
37 * @var WC_Emails
38 */
39 protected static $instance = null;
40
41 /**
42 * Background emailer class.
43 *
44 * @var WC_Background_Emailer
45 */
46 protected static $background_emailer = null;
47
48 /**
49 * Main WC_Emails Instance.
50 *
51 * Ensures only one instance of WC_Emails is loaded or can be loaded.
52 *
53 * @since 2.1
54 * @static
55 * @return WC_Emails Main instance
56 */
57 public static function instance() {
58 if ( is_null( self::$instance ) ) {
59 self::$instance = new self();
60 }
61 return self::$instance;
62 }
63
64 /**
65 * Cloning is forbidden.
66 *
67 * @since 2.1
68 */
69 public function __clone() {
70 wc_doing_it_wrong( __FUNCTION__, __( 'Cloning is forbidden.', 'woocommerce' ), '2.1' );
71 }
72
73 /**
74 * Unserializing instances of this class is forbidden.
75 *
76 * @since 2.1
77 */
78 public function __wakeup() {
79 wc_doing_it_wrong( __FUNCTION__, __( 'Unserializing instances of this class is forbidden.', 'woocommerce' ), '2.1' );
80 }
81
82 /**
83 * Hook in all transactional emails.
84 */
85 public static function init_transactional_emails() {
86 /**
87 * Filter the actions that trigger transactional emails.
88 *
89 * @since 3.0.0
90 * @param array $email_actions Array of actions that trigger transactional emails.
91 */
92 $email_actions = apply_filters(
93 'woocommerce_email_actions',
94 array(
95 'woocommerce_low_stock',
96 'woocommerce_no_stock',
97 'woocommerce_product_on_backorder',
98 'woocommerce_order_status_pending_to_processing',
99 'woocommerce_order_status_pending_to_completed',
100 'woocommerce_order_status_processing_to_cancelled',
101 'woocommerce_order_status_pending_to_failed',
102 'woocommerce_order_status_pending_to_on-hold',
103 'woocommerce_order_status_failed_to_processing',
104 'woocommerce_order_status_failed_to_completed',
105 'woocommerce_order_status_failed_to_on-hold',
106 'woocommerce_order_status_cancelled_to_processing',
107 'woocommerce_order_status_cancelled_to_completed',
108 'woocommerce_order_status_cancelled_to_on-hold',
109 'woocommerce_order_status_on-hold_to_processing',
110 'woocommerce_order_status_on-hold_to_cancelled',
111 'woocommerce_order_status_on-hold_to_failed',
112 'woocommerce_order_status_completed',
113 'woocommerce_order_status_failed',
114 'woocommerce_order_fully_refunded',
115 'woocommerce_order_partially_refunded',
116 'woocommerce_new_customer_note',
117 'woocommerce_created_customer',
118 )
119 );
120
121 /**
122 * Filter whether to defer transactional emails.
123 *
124 * @since 3.0.0
125 * @param bool $defer Whether to defer transactional emails.
126 */
127 if ( apply_filters( 'woocommerce_defer_transactional_emails', false ) ) {
128 self::$background_emailer = new WC_Background_Emailer();
129
130 foreach ( $email_actions as $action ) {
131 add_action( $action, array( __CLASS__, 'queue_transactional_email' ), 10, 10 );
132 }
133 } else {
134 foreach ( $email_actions as $action ) {
135 add_action( $action, array( __CLASS__, 'send_transactional_email' ), 10, 10 );
136 }
137 }
138 }
139
140 /**
141 * Queues transactional email so it's not sent in current request if enabled,
142 * otherwise falls back to send now.
143 *
144 * @param mixed ...$args Optional arguments.
145 */
146 public static function queue_transactional_email( ...$args ) {
147 if ( is_a( self::$background_emailer, 'WC_Background_Emailer' ) ) {
148 self::$background_emailer->push_to_queue(
149 array(
150 'filter' => current_filter(),
151 'args' => func_get_args(),
152 )
153 );
154 } else {
155 self::send_transactional_email( ...$args );
156 }
157 }
158
159 /**
160 * Init the mailer instance and call the notifications for the current filter.
161 *
162 * @internal
163 *
164 * @param string $filter Filter name.
165 * @param array $args Email args (default: []).
166 */
167 public static function send_queued_transactional_email( $filter = '', $args = array() ) {
168 /**
169 * Filter whether to allow sending queued transactional emails.
170 *
171 * @since 3.0.0
172 * @param bool $allow Whether to allow sending queued transactional emails.
173 * @param string $filter Filter name.
174 * @param array $args Email args.
175 */
176 if ( apply_filters( 'woocommerce_allow_send_queued_transactional_email', true, $filter, $args ) ) {
177 self::instance(); // Init self so emails exist.
178
179 // Ensure gateways are loaded in case they need to insert data into the emails.
180 WC()->payment_gateways();
181 WC()->shipping();
182
183 // phpcs:disable WooCommerce.Commenting.CommentHooks.MissingSinceComment
184 /** This action is documented in includes/class-wc-emails.php in the send_transactional_email method. */
185 do_action_ref_array( $filter . '_notification', $args );
186 }
187 }
188
189 /**
190 * Init the mailer instance and call the notifications for the current filter.
191 *
192 * @internal
193 *
194 * @param array $args Email args (default: []).
195 */
196 public static function send_transactional_email( $args = array() ) {
197 try {
198 $args = func_get_args();
199 self::instance(); // Init self so emails exist.
200
201 /**
202 * Action hook for email template classes to trigger the sending of an email.
203 *
204 * The name of the hook is based on the "parent" hook that is currently firing, that this is attached to.
205 * See the WC_Emails::init_transactional_emails method for a list of hooks.
206 *
207 * @since 3.1.0
208 *
209 * @param array $args Args from the parent hook, which may differ depending on the hook.
210 */
211 do_action_ref_array( current_filter() . '_notification', $args );
212 } catch ( Exception $e ) {
213 $error = 'Transactional email triggered fatal error for callback ' . current_filter();
214 $logger = wc_get_logger();
215 $logger->critical(
216 $error . PHP_EOL,
217 array(
218 'source' => 'transactional-emails',
219 )
220 );
221 if ( Constants::is_true( 'WP_DEBUG' ) ) {
222 trigger_error( esc_html( $error ), E_USER_WARNING ); // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_trigger_error
223 }
224 }
225 }
226
227 /**
228 * Constructor for the email class hooks in all emails that can be sent.
229 */
230 public function __construct() {
231 $this->init();
232
233 // Email Header, Footer and content hooks.
234 add_action( 'woocommerce_email_header', array( $this, 'email_header' ) );
235 add_action( 'woocommerce_email_footer', array( $this, 'email_footer' ) );
236 add_action( 'woocommerce_email_order_details', array( $this, 'order_downloads' ), 10, 4 );
237 add_action( 'woocommerce_email_order_details', array( $this, 'order_details' ), 10, 4 );
238 add_action( 'woocommerce_email_order_meta', array( $this, 'order_meta' ), 10, 3 );
239 add_action( 'woocommerce_email_customer_details', array( $this, 'customer_details' ), 10, 3 );
240 add_action( 'woocommerce_email_customer_details', array( $this, 'email_addresses' ), 20, 3 );
241 add_action( 'woocommerce_email_customer_details', array( $this, 'additional_checkout_fields' ), 30, 3 );
242 add_action( 'woocommerce_email_customer_address_section', array( $this, 'additional_address_fields' ), 30, 4 );
243
244 if ( FeaturesUtil::feature_is_enabled( 'fulfillments' ) ) {
245 // Fulfillment details and meta.
246 add_action( 'woocommerce_email_fulfillment_details', array( $this, 'fulfillment_details' ), 10, 5 );
247 add_action( 'woocommerce_email_fulfillment_meta', array( $this, 'fulfillment_meta' ), 30, 4 );
248 }
249
250 // Hooks for sending emails during store events.
251 add_action( 'woocommerce_low_stock_notification', array( $this, 'low_stock' ) );
252 add_action( 'woocommerce_no_stock_notification', array( $this, 'no_stock' ) );
253 add_action( 'woocommerce_product_on_backorder_notification', array( $this, 'backorder' ) );
254 add_action( 'woocommerce_created_customer_notification', array( $this, 'customer_new_account' ), 10, 3 );
255
256 // Hook for replacing {site_title} in email-footer.
257 add_filter( 'woocommerce_email_footer_text', array( $this, 'replace_placeholders' ) );
258
259 /**
260 * Action hook for email classes to hook into.
261 *
262 * @since 3.0.0
263 * @param WC_Emails $this The WC_Emails instance.
264 */
265 do_action( 'woocommerce_email', $this );
266 }
267
268 /**
269 * Init email classes.
270 */
271 public function init() {
272 // Include email classes.
273 include_once __DIR__ . '/emails/class-wc-email.php';
274
275 $this->emails['WC_Email_New_Order'] = include __DIR__ . '/emails/class-wc-email-new-order.php';
276 $this->emails['WC_Email_Cancelled_Order'] = include __DIR__ . '/emails/class-wc-email-cancelled-order.php';
277 $this->emails['WC_Email_Customer_Cancelled_Order'] = include __DIR__ . '/emails/class-wc-email-customer-cancelled-order.php';
278 $this->emails['WC_Email_Failed_Order'] = include __DIR__ . '/emails/class-wc-email-failed-order.php';
279 $this->emails['WC_Email_Customer_Failed_Order'] = include __DIR__ . '/emails/class-wc-email-customer-failed-order.php';
280 $this->emails['WC_Email_Customer_On_Hold_Order'] = include __DIR__ . '/emails/class-wc-email-customer-on-hold-order.php';
281 $this->emails['WC_Email_Customer_Processing_Order'] = include __DIR__ . '/emails/class-wc-email-customer-processing-order.php';
282 $this->emails['WC_Email_Customer_Completed_Order'] = include __DIR__ . '/emails/class-wc-email-customer-completed-order.php';
283 $this->emails['WC_Email_Customer_Refunded_Order'] = include __DIR__ . '/emails/class-wc-email-customer-refunded-order.php';
284 $this->emails['WC_Email_Customer_Invoice'] = include __DIR__ . '/emails/class-wc-email-customer-invoice.php';
285 $this->emails['WC_Email_Customer_Note'] = include __DIR__ . '/emails/class-wc-email-customer-note.php';
286 $this->emails['WC_Email_Customer_Reset_Password'] = include __DIR__ . '/emails/class-wc-email-customer-reset-password.php';
287 $this->emails['WC_Email_Customer_New_Account'] = include __DIR__ . '/emails/class-wc-email-customer-new-account.php';
288
289 if ( FeaturesUtil::feature_is_enabled( 'point_of_sale' ) ) {
290 $this->emails['WC_Email_Customer_POS_Completed_Order'] = include __DIR__ . '/emails/class-wc-email-customer-pos-completed-order.php';
291 $this->emails['WC_Email_Customer_POS_Refunded_Order'] = include __DIR__ . '/emails/class-wc-email-customer-pos-refunded-order.php';
292 }
293
294 if ( FeaturesUtil::feature_is_enabled( 'fulfillments' ) ) {
295 $this->emails['WC_Email_Customer_Fulfillment_Created'] = include __DIR__ . '/emails/class-wc-email-customer-fulfillment-created.php';
296 $this->emails['WC_Email_Customer_Fulfillment_Updated'] = include __DIR__ . '/emails/class-wc-email-customer-fulfillment-updated.php';
297 $this->emails['WC_Email_Customer_Fulfillment_Deleted'] = include __DIR__ . '/emails/class-wc-email-customer-fulfillment-deleted.php';
298 }
299
300 /**
301 * Filter the email classes.
302 *
303 * @since 3.0.0
304 * @param array $emails Email classes.
305 */
306 $this->emails = apply_filters( 'woocommerce_email_classes', $this->emails );
307 }
308
309 /**
310 * Return the email classes - used in admin to load settings.
311 *
312 * @return WC_Email[]
313 */
314 public function get_emails() {
315 return $this->emails;
316 }
317
318 /**
319 * Get from name for email.
320 *
321 * @return string
322 */
323 public function get_from_name() {
324 return wp_specialchars_decode( get_option( 'woocommerce_email_from_name' ), ENT_QUOTES );
325 }
326
327 /**
328 * Get from email address.
329 *
330 * @return string
331 */
332 public function get_from_address() {
333 return sanitize_email( get_option( 'woocommerce_email_from_address' ) );
334 }
335
336 /**
337 * Get the email header.
338 *
339 * @param mixed $email_heading Heading for the email.
340 */
341 public function email_header( $email_heading ) {
342 wc_get_template(
343 'emails/email-header.php',
344 array(
345 'email_heading' => $email_heading,
346 'store_name' => get_bloginfo( 'name', 'display' ),
347 )
348 );
349 }
350
351 /**
352 * Get the email footer.
353 */
354 public function email_footer() {
355 wc_get_template( 'emails/email-footer.php' );
356 }
357
358 /**
359 * Replace placeholder text in strings.
360 *
361 * @since 3.7.0
362 * @param string $text Email footer text.
363 * @return string Email footer text with any replacements done.
364 */
365 public function replace_placeholders( $text ) {
366 $domain = wp_parse_url( home_url(), PHP_URL_HOST );
367
368 return str_replace(
369 array(
370 '{site_title}',
371 '{site_address}',
372 '{site_url}',
373 '{woocommerce}',
374 '{WooCommerce}',
375 '{store_address}',
376 '{store_email}',
377 ),
378 array(
379 $this->get_blogname(),
380 $domain,
381 $domain,
382 '<a href="https://woocommerce.com">WooCommerce</a>',
383 '<a href="https://woocommerce.com">WooCommerce</a>',
384 $this->get_store_address(),
385 $this->get_from_address(),
386 ),
387 $text
388 );
389 }
390
391 /**
392 * Filter callback to replace {site_title} in email footer
393 *
394 * @since 3.3.0
395 * @deprecated 3.7.0
396 * @param string $text Email footer text.
397 * @return string Email footer text with any replacements done.
398 */
399 public function email_footer_replace_site_title( $text ) {
400 wc_deprecated_function( 'WC_Emails::email_footer_replace_site_title', '3.7.0', 'WC_Emails::replace_placeholders' );
401 return $this->replace_placeholders( $text );
402 }
403
404 /**
405 * Wraps a message in the woocommerce mail template.
406 *
407 * @param string $email_heading Heading text.
408 * @param string $message Email message.
409 * @param bool $deprecated Deprecated.
410 *
411 * @return string
412 */
413 public function wrap_message( $email_heading, $message, $deprecated = false ) {
414 if ( $deprecated ) {
415 wc_deprecated_argument( 'WC_Emails::wrap_message', '9.9.0' );
416 }
417
418 ob_start();
419
420 /**
421 * Action hook for email header.
422 *
423 * @since 3.0.0
424 * @param string $email_heading Heading text.
425 * @param null $null Unused.
426 */
427 do_action( 'woocommerce_email_header', $email_heading, null );
428
429 echo wp_kses_post( wpautop( wptexturize( $message ) ) );
430
431 /**
432 * Action hook for email footer.
433 *
434 * @since 3.0.0
435 * @param null $null Unused.
436 */
437 do_action( 'woocommerce_email_footer', null );
438
439 return ob_get_clean();
440 }
441
442 /**
443 * Send the email.
444 *
445 * @param mixed $to Receiver.
446 * @param mixed $subject Email subject.
447 * @param mixed $message Message.
448 * @param string $headers Email headers (default: "Content-Type: text/html\r\n").
449 * @param string $attachments Attachments (default: "").
450 * @return bool
451 */
452 public function send( $to, $subject, $message, $headers = "Content-Type: text/html\r\n", $attachments = '' ) {
453 $email = new WC_Email();
454 return $email->send( $to, $subject, $message, $headers, $attachments );
455 }
456
457 /**
458 * Prepare and send the customer invoice email on demand.
459 *
460 * @param int|WC_Order $order Order instance or ID.
461 */
462 public function customer_invoice( $order ) {
463 $email = $this->emails['WC_Email_Customer_Invoice'];
464
465 if ( ! is_object( $order ) ) {
466 $order = wc_get_order( absint( $order ) );
467 }
468
469 $email->trigger( $order->get_id(), $order );
470 }
471
472 /**
473 * Customer new account welcome email.
474 *
475 * @param int $customer_id Customer ID.
476 * @param array $new_customer_data New customer data.
477 * @param bool $password_generated If password is generated.
478 */
479 public function customer_new_account( $customer_id, $new_customer_data = array(), $password_generated = false ) {
480 if ( ! $customer_id ) {
481 return;
482 }
483 $email = $this->emails['WC_Email_Customer_New_Account'];
484 $email->trigger( $customer_id, $new_customer_data['user_pass'] ?? '', $password_generated );
485 }
486
487 /**
488 * Show the order details table
489 *
490 * @param WC_Order $order Order instance.
491 * @param bool $sent_to_admin If should sent to admin.
492 * @param bool $plain_text If is plain text email.
493 * @param string $email Email address.
494 */
495 public function order_details( $order, $sent_to_admin = false, $plain_text = false, $email = '' ) {
496 if ( $plain_text ) {
497 wc_get_template(
498 'emails/plain/email-order-details.php',
499 array(
500 'order' => $order,
501 'sent_to_admin' => $sent_to_admin,
502 'plain_text' => $plain_text,
503 'email' => $email,
504 )
505 );
506 } else {
507 wc_get_template(
508 'emails/email-order-details.php',
509 array(
510 'order' => $order,
511 'sent_to_admin' => $sent_to_admin,
512 'plain_text' => $plain_text,
513 'email' => $email,
514 )
515 );
516 }
517 }
518
519 /**
520 * Show order downloads in a table.
521 *
522 * @since 3.2.0
523 * @param WC_Order $order Order instance.
524 * @param bool $sent_to_admin If should sent to admin.
525 * @param bool $plain_text If is plain text email.
526 * @param string $email Email address.
527 */
528 public function order_downloads( $order, $sent_to_admin = false, $plain_text = false, $email = '' ) {
529 $show_downloads = $order->has_downloadable_item() && $order->is_download_permitted() && ! $sent_to_admin && ! is_a( $email, 'WC_Email_Customer_Refunded_Order' );
530
531 if ( ! $show_downloads ) {
532 return;
533 }
534
535 $downloads = $order->get_downloadable_items();
536
537 /**
538 * Filter the columns of the order downloads table.
539 *
540 * @since 3.2.0
541 * @since 10.0.0 Added $order parameter.
542 * @param array $columns Array of columns.
543 * @param WC_Order $order Order object.
544 */
545 $columns = apply_filters(
546 'woocommerce_email_downloads_columns',
547 array(
548 'download-product' => __( 'Product', 'woocommerce' ),
549 'download-expires' => __( 'Expires', 'woocommerce' ),
550 'download-file' => __( 'Download', 'woocommerce' ),
551 ),
552 $order
553 );
554
555 if ( $plain_text ) {
556 wc_get_template(
557 'emails/plain/email-downloads.php',
558 array(
559 'order' => $order,
560 'sent_to_admin' => $sent_to_admin,
561 'plain_text' => $plain_text,
562 'email' => $email,
563 'downloads' => $downloads,
564 'columns' => $columns,
565 )
566 );
567 } else {
568 wc_get_template(
569 'emails/email-downloads.php',
570 array(
571 'order' => $order,
572 'sent_to_admin' => $sent_to_admin,
573 'plain_text' => $plain_text,
574 'email' => $email,
575 'downloads' => $downloads,
576 'columns' => $columns,
577 )
578 );
579 }
580 }
581
582 /**
583 * Add order meta to email templates.
584 *
585 * @param WC_Order $order Order instance.
586 * @param bool $sent_to_admin If should sent to admin.
587 * @param bool $plain_text If is plain text email.
588 */
589 public function order_meta( $order, $sent_to_admin = false, $plain_text = false ) {
590 /**
591 * Filter the order meta fields.
592 *
593 * @since 3.0.0
594 * @param array $fields Array of meta fields.
595 * @param bool $sent_to_admin If sent to admin.
596 * @param WC_Order $order Order instance.
597 */
598 $fields = apply_filters( 'woocommerce_email_order_meta_fields', array(), $sent_to_admin, $order );
599
600 /**
601 * Deprecated woocommerce_email_order_meta_keys filter.
602 *
603 * @since 2.3.0
604 * @param array $fields Array of meta fields.
605 * @param bool $sent_to_admin If sent to admin.
606 */
607 $_fields = apply_filters( 'woocommerce_email_order_meta_keys', array(), $sent_to_admin );
608
609 if ( $_fields ) {
610 foreach ( $_fields as $key => $field ) {
611 if ( is_numeric( $key ) ) {
612 $key = $field;
613 }
614
615 $fields[ $key ] = array(
616 'label' => wptexturize( $key ),
617 'value' => wptexturize( $order->get_meta( $field ) ),
618 );
619 }
620 }
621
622 if ( $fields ) {
623
624 if ( $plain_text ) {
625
626 foreach ( $fields as $field ) {
627 if ( isset( $field['label'], $field['value'] ) && $field['value'] ) {
628 echo wp_kses_post( $field['label'] . ': ' . $field['value'] ) . "\n"; // WPCS: XSS ok.
629 }
630 }
631 } else {
632
633 foreach ( $fields as $field ) {
634 if ( isset( $field['label'], $field['value'] ) && $field['value'] ) {
635 echo '<p><strong>' . wp_kses_post( $field['label'] ) . ':</strong> ' . wp_kses_post( $field['value'] ) . '</p>'; // WPCS: XSS ok.
636 }
637 }
638 }
639 }
640 }
641
642 /**
643 * Show the fulfillment details
644 *
645 * @param WC_Order $order Order instance.
646 * @param Fulfillment $fulfillment Fulfillment instance.
647 * @param bool $sent_to_admin If should sent to admin.
648 * @param bool $plain_text If is plain text email.
649 * @param string $email Email address.
650 */
651 public function fulfillment_details( $order, $fulfillment, $sent_to_admin = false, $plain_text = false, $email = '' ) {
652 if ( $plain_text ) {
653 wc_get_template(
654 'emails/plain/email-fulfillment-details.php',
655 array(
656 'order' => $order,
657 'fulfillment' => $fulfillment,
658 'sent_to_admin' => $sent_to_admin,
659 'plain_text' => $plain_text,
660 'email' => $email,
661 )
662 );
663 } else {
664 wc_get_template(
665 'emails/email-fulfillment-details.php',
666 array(
667 'order' => $order,
668 'fulfillment' => $fulfillment,
669 'sent_to_admin' => $sent_to_admin,
670 'plain_text' => $plain_text,
671 'email' => $email,
672 )
673 );
674 }
675 }
676
677 /**
678 * Add fulfillment meta to email templates.
679 *
680 * @param WC_Order $order Order instance.
681 * @param Fulfillment $fulfillment Fulfillment instance.
682 * @param bool $sent_to_admin If should sent to admin.
683 * @param bool $plain_text If is plain text email.
684 */
685 public function fulfillment_meta( $order, $fulfillment, $sent_to_admin = false, $plain_text = false ) {
686 $fields = $fulfillment->get_meta_data();
687 $public_fields = array_filter(
688 $fields,
689 function ( $field ) {
690 return ! str_starts_with( $field->key, '_' );
691 }
692 );
693
694 if ( 0 < count( $public_fields ) ) {
695
696 foreach ( $public_fields as $field ) {
697 if ( isset( $field->key ) && isset( $field->value ) && $field->value ) {
698 /**
699 * Allows developers to translate the fulfillment meta key for display in emails.
700 *
701 * @since 10.1.0
702 */
703 $meta_key_translation = apply_filters( 'woocommerce_fulfillment_translate_meta_key', $field->key );
704 if ( $plain_text ) {
705 echo esc_attr( $meta_key_translation ) . ': ' . esc_attr( $field->value ) . PHP_EOL;
706 } else {
707 echo '<p><strong>' . esc_attr( $meta_key_translation ) . ':</strong> ' . esc_attr( $field->value ) . '</p>';
708 }
709 }
710 }
711 }
712 }
713
714 /**
715 * Is customer detail field valid?
716 *
717 * @param array $field Field data to check if is valid.
718 * @return boolean
719 */
720 public function customer_detail_field_is_valid( $field ) {
721 return isset( $field['label'] ) && ! empty( $field['value'] );
722 }
723
724 /**
725 * Allows developers to add additional customer details to templates.
726 *
727 * In versions prior to 3.2 this was used for notes, phone and email but this data has moved.
728 *
729 * @param WC_Order $order Order instance.
730 * @param bool $sent_to_admin If should sent to admin.
731 * @param bool $plain_text If is plain text email.
732 */
733 public function customer_details( $order, $sent_to_admin = false, $plain_text = false ) {
734 if ( ! is_a( $order, 'WC_Order' ) ) {
735 return;
736 }
737
738 /**
739 * Filter the customer details fields.
740 *
741 * @since 3.2.0
742 * @param array $fields Array of customer details fields.
743 * @param bool $sent_to_admin If sent to admin.
744 * @param WC_Order $order Order instance.
745 */
746 $fields = array_filter( apply_filters( 'woocommerce_email_customer_details_fields', array(), $sent_to_admin, $order ), array( $this, 'customer_detail_field_is_valid' ) );
747
748 if ( ! empty( $fields ) ) {
749 if ( $plain_text ) {
750 wc_get_template( 'emails/plain/email-customer-details.php', array( 'fields' => $fields ) );
751 } else {
752 wc_get_template( 'emails/email-customer-details.php', array( 'fields' => $fields ) );
753 }
754 }
755 }
756
757 /**
758 * Get the email addresses.
759 *
760 * @param WC_Order $order Order instance.
761 * @param bool $sent_to_admin If should sent to admin.
762 * @param bool $plain_text If is plain text email.
763 */
764 public function email_addresses( $order, $sent_to_admin = false, $plain_text = false ) {
765 if ( ! is_a( $order, 'WC_Order' ) ) {
766 return;
767 }
768 if ( $plain_text ) {
769 wc_get_template(
770 'emails/plain/email-addresses.php',
771 array(
772 'order' => $order,
773 'sent_to_admin' => $sent_to_admin,
774 )
775 );
776 } else {
777 wc_get_template(
778 'emails/email-addresses.php',
779 array(
780 'order' => $order,
781 'sent_to_admin' => $sent_to_admin,
782 )
783 );
784 }
785 }
786
787 /**
788 * Renders any additional fields captured during block-based checkout.
789 *
790 * @param WC_Order $order Order instance.
791 * @param bool $sent_to_admin If email is sent to admin.
792 * @param bool $plain_text If this is a plain text email.
793 */
794 public function additional_checkout_fields( $order, $sent_to_admin = false, $plain_text = false ) {
795 if ( ! is_a( $order, 'WC_Order' ) ) {
796 return;
797 }
798
799 /**
800 * Service class managing checkout fields and its related extensibility points.
801 *
802 * @var CheckoutFields $checkout_fields
803 */
804 $checkout_fields = Package::container()->get( CheckoutFields::class );
805 $fields = array_merge(
806 $checkout_fields->get_order_additional_fields_with_values( $order, 'contact', 'other', 'view' ),
807 $checkout_fields->get_order_additional_fields_with_values( $order, 'order', 'other', 'view' ),
808 );
809
810 $context = array(
811 'caller' => 'WC_Email::additional_checkout_fields',
812 'order' => $order,
813 'sent_to_admin' => $sent_to_admin,
814 'plain_text' => $plain_text,
815 );
816
817 $fields = $checkout_fields->filter_fields_for_order_confirmation( $fields, $context );
818
819 if ( ! $fields ) {
820 return;
821 }
822
823 if ( $plain_text ) {
824 echo "\n" . esc_html( wc_strtoupper( __( 'Additional information', 'woocommerce' ) ) ) . "\n\n";
825 foreach ( $fields as $field ) {
826 printf( "%s: %s\n", wp_kses_post( $field['label'] ), wp_kses_post( $field['value'] ) );
827 }
828 } else {
829 echo '<h2>' . esc_html__( 'Additional information', 'woocommerce' ) . '</h2>';
830 echo '<ul class="additional-fields" style="margin-bottom: 40px;">';
831 foreach ( $fields as $field ) {
832 printf( '<li><strong>%s</strong>: %s</li>', wp_kses_post( $field['label'] ), wp_kses_post( $field['value'] ) );
833 }
834 echo '</ul>';
835 }
836 }
837
838 /**
839 * Renders any additional address fields captured during block-based checkout.
840 *
841 * @param string $address_type Address type.
842 * @param WC_Order $order Order instance.
843 * @param bool $sent_to_admin If email is sent to admin.
844 * @param bool $plain_text If this is a plain text email.
845 */
846 public function additional_address_fields( $address_type, $order, $sent_to_admin = false, $plain_text = false ) {
847 if ( ! is_a( $order, 'WC_Order' ) ) {
848 return;
849 }
850
851 /**
852 * Service class managing checkout fields and its related extensibility points.
853 *
854 * @var CheckoutFields $checkout_fields
855 */
856 $checkout_fields = Package::container()->get( CheckoutFields::class );
857 $fields = $checkout_fields->get_order_additional_fields_with_values( $order, 'address', $address_type, 'view' );
858
859 $context = array(
860 'caller' => 'WC_Email::additional_address_fields',
861 'address_type' => $address_type,
862 'order' => $order,
863 'sent_to_admin' => $sent_to_admin,
864 'plain_text' => $plain_text,
865 );
866
867 $fields = $checkout_fields->filter_fields_for_order_confirmation( $fields, $context );
868
869 if ( ! $fields ) {
870 return;
871 }
872
873 foreach ( $fields as $field ) {
874 if ( $plain_text ) {
875 printf( "%s: %s\n", wp_kses_post( $field['label'] ), wp_kses_post( $field['value'] ) );
876 } else {
877 printf( '<br><strong>%s</strong>: %s', wp_kses_post( $field['label'] ), wp_kses_post( $field['value'] ) );
878 }
879 }
880 }
881
882 /**
883 * Get blog name formatted for emails.
884 *
885 * @return string
886 */
887 private function get_blogname() {
888 return wp_specialchars_decode( get_option( 'blogname' ), ENT_QUOTES );
889 }
890
891 /**
892 * Get store address formatted for emails.
893 *
894 * @return string
895 */
896 public function get_store_address() {
897 add_filter(
898 'woocommerce_formatted_address_force_country_display',
899 array( $this, 'get_store_address_force_country_display' ),
900 5
901 );
902 $result = wp_specialchars_decode(
903 WC()->countries->get_formatted_address(
904 array(
905 'address_1' => WC()->countries->get_base_address(),
906 'address_2' => WC()->countries->get_base_address_2(),
907 'city' => WC()->countries->get_base_city(),
908 'state' => WC()->countries->get_base_state(),
909 'country' => WC()->countries->get_base_country(),
910 'postcode' => WC()->countries->get_base_postcode(),
911 )
912 )
913 );
914 // Replace newlines by commas.
915 $result = preg_replace( '/<br\/?>/i', ', ', $result );
916 remove_filter(
917 'woocommerce_formatted_address_force_country_display',
918 array( $this, 'get_store_address_force_country_display' )
919 );
920 return $result;
921 }
922
923 /**
924 * Force country display, used by WC_Emails::get_store address() method
925 *
926 * @return bool
927 */
928 public function get_store_address_force_country_display() {
929 return true;
930 }
931
932 /**
933 * Add email sender filters.
934 */
935 private function add_email_sender_filters() {
936 add_filter( 'wp_mail_from', array( $this, 'get_from_address' ) );
937 add_filter( 'wp_mail_from_name', array( $this, 'get_from_name' ) );
938 }
939
940 /**
941 * Remove email sender filters.
942 */
943 private function remove_email_sender_filters() {
944 remove_filter( 'wp_mail_from', array( $this, 'get_from_address' ) );
945 remove_filter( 'wp_mail_from_name', array( $this, 'get_from_name' ) );
946 }
947
948 /**
949 * Low stock notification email.
950 *
951 * @param WC_Product $product Product instance.
952 */
953 public function low_stock( $product ) {
954 if ( 'no' === get_option( 'woocommerce_notify_low_stock', 'yes' ) ) {
955 return;
956 }
957
958 /**
959 * Determine if the current product should trigger a low stock notification
960 *
961 * @param int $product_id - The low stock product id
962 *
963 * @since 4.7.0
964 */
965 if ( false === apply_filters( 'woocommerce_should_send_low_stock_notification', true, $product->get_id() ) ) {
966 return;
967 }
968
969 // If this is a variation but stock is managed at the parent level, use the parent product for the notification.
970 if ( $product->is_type( 'variation' ) && 'parent' === $product->get_manage_stock() ) {
971 $parent_product = wc_get_product( $product->get_parent_id() );
972 if ( $parent_product ) {
973 $product = $parent_product;
974 }
975 }
976
977 $subject = sprintf( '[%s] %s', $this->get_blogname(), __( 'Product low in stock', 'woocommerce' ) );
978 $message = sprintf(
979 /* translators: 1: product name 2: items in stock */
980 __( '%1$s is low in stock. There are %2$d left.', 'woocommerce' ),
981 html_entity_decode( wp_strip_all_tags( $product->get_formatted_name() ), ENT_QUOTES, get_bloginfo( 'charset' ) ),
982 html_entity_decode( wp_strip_all_tags( $product->get_stock_quantity() ) )
983 );
984
985 $this->add_email_sender_filters();
986
987 wp_mail(
988 /**
989 * Filter the recipient of the low stock notification email.
990 *
991 * @since 3.0.0
992 * @param string $recipient The recipient email address.
993 * @param WC_Product $product Product instance.
994 * @param null $null Unused.
995 */
996 apply_filters( 'woocommerce_email_recipient_low_stock', get_option( 'woocommerce_stock_email_recipient' ), $product, null ),
997 /**
998 * Filter the subject of the low stock notification email.
999 *
1000 * @since 3.0.0
1001 * @param string $subject The email subject.
1002 * @param WC_Product $product Product instance.
1003 * @param null $null Unused.
1004 */
1005 apply_filters( 'woocommerce_email_subject_low_stock', $subject, $product, null ),
1006 /**
1007 * Filter the content of the low stock notification email.
1008 *
1009 * @since 3.0.0
1010 * @param string $message The email content.
1011 * @param WC_Product $product Product instance.
1012 * @param null $null Unused.
1013 */
1014 apply_filters( 'woocommerce_email_content_low_stock', $message, $product ),
1015 /**
1016 * Filter the headers of the low stock notification email.
1017 *
1018 * @since 3.0.0
1019 * @param string $headers The email headers.
1020 * @param WC_Product $product Product instance.
1021 * @param null $null Unused.
1022 */
1023 apply_filters( 'woocommerce_email_headers', '', 'low_stock', $product, null ),
1024 /**
1025 * Filter the attachments of the low stock notification email.
1026 *
1027 * @since 3.0.0
1028 * @param array $attachments The email attachments.
1029 * @param WC_Product $product Product instance.
1030 * @param null $null Unused.
1031 */
1032 apply_filters( 'woocommerce_email_attachments', array(), 'low_stock', $product, null )
1033 );
1034
1035 $this->remove_email_sender_filters();
1036 }
1037
1038 /**
1039 * No stock notification email.
1040 *
1041 * @param WC_Product $product Product instance.
1042 */
1043 public function no_stock( $product ) {
1044 if ( 'no' === get_option( 'woocommerce_notify_no_stock', 'yes' ) ) {
1045 return;
1046 }
1047
1048 /**
1049 * Determine if the current product should trigger a no stock notification
1050 *
1051 * @param int $product_id - The out of stock product id
1052 *
1053 * @since 4.6.0
1054 */
1055 if ( false === apply_filters( 'woocommerce_should_send_no_stock_notification', true, $product->get_id() ) ) {
1056 return;
1057 }
1058
1059 // If this is a variation but stock is managed at the parent level, use the parent product for the notification.
1060 if ( $product->is_type( ProductType::VARIATION ) && 'parent' === $product->get_manage_stock() ) {
1061 $parent_product = wc_get_product( $product->get_parent_id() );
1062 if ( $parent_product ) {
1063 $product = $parent_product;
1064 }
1065 }
1066
1067 $subject = sprintf( '[%s] %s', $this->get_blogname(), __( 'Product out of stock', 'woocommerce' ) );
1068 /* translators: %s: product name */
1069 $message = sprintf( __( '%s is out of stock.', 'woocommerce' ), html_entity_decode( wp_strip_all_tags( $product->get_formatted_name() ), ENT_QUOTES, get_bloginfo( 'charset' ) ) );
1070
1071 $this->add_email_sender_filters();
1072
1073 wp_mail(
1074 /**
1075 * Filter the recipient of the no stock notification email.
1076 *
1077 * @since 3.0.0
1078 * @param string $recipient The recipient email address.
1079 * @param WC_Product $product Product instance.
1080 * @param null $null Unused.
1081 */
1082 apply_filters( 'woocommerce_email_recipient_no_stock', get_option( 'woocommerce_stock_email_recipient' ), $product, null ),
1083 /**
1084 * Filter the subject of the no stock notification email.
1085 *
1086 * @since 3.0.0
1087 * @param string $subject The email subject.
1088 * @param WC_Product $product Product instance.
1089 * @param null $null Unused.
1090 */
1091 apply_filters( 'woocommerce_email_subject_no_stock', $subject, $product, null ),
1092 /**
1093 * Filter the content of the no stock notification email.
1094 *
1095 * @since 3.0.0
1096 * @param string $message The email content.
1097 * @param WC_Product $product Product instance.
1098 * @param null $null Unused.
1099 */
1100 apply_filters( 'woocommerce_email_content_no_stock', $message, $product ),
1101 /**
1102 * Filter the headers of the no stock notification email.
1103 *
1104 * @since 3.0.0
1105 * @param string $headers The email headers.
1106 * @param WC_Product $product Product instance.
1107 * @param null $null Unused.
1108 */
1109 apply_filters( 'woocommerce_email_headers', '', 'no_stock', $product, null ),
1110 /**
1111 * Filter the attachments of the no stock notification email.
1112 *
1113 * @since 3.0.0
1114 * @param array $attachments The email attachments.
1115 * @param WC_Product $product Product instance.
1116 * @param null $null Unused.
1117 */
1118 apply_filters( 'woocommerce_email_attachments', array(), 'no_stock', $product, null )
1119 );
1120
1121 $this->remove_email_sender_filters();
1122 }
1123
1124 /**
1125 * Backorder notification email.
1126 *
1127 * @param array $args Arguments.
1128 */
1129 public function backorder( $args ) {
1130 $args = wp_parse_args(
1131 $args,
1132 array(
1133 'product' => '',
1134 'quantity' => '',
1135 'order_id' => '',
1136 )
1137 );
1138
1139 $order = wc_get_order( $args['order_id'] );
1140 if (
1141 ! $args['product'] ||
1142 ! is_object( $args['product'] ) ||
1143 ! $args['quantity'] ||
1144 ! $order
1145 ) {
1146 return;
1147 }
1148
1149 $stock_before = $args['quantity'] + $args['product']->get_stock_quantity();
1150 $backordered_quantity = $args['quantity'] - max( 0, $stock_before );
1151
1152 $subject = sprintf( '[%s] %s', $this->get_blogname(), __( 'Product backorder', 'woocommerce' ) );
1153 /* translators: 1: backordered quantity 2: product name 3: order number */
1154 $message = sprintf( __( '%1$s units of %2$s have been backordered in order #%3$s.', 'woocommerce' ), $backordered_quantity, html_entity_decode( wp_strip_all_tags( $args['product']->get_formatted_name() ), ENT_QUOTES, get_bloginfo( 'charset' ) ), $order->get_order_number() );
1155
1156 $this->add_email_sender_filters();
1157
1158 wp_mail(
1159 /**
1160 * Filter the recipient of the backorder notification email.
1161 *
1162 * @since 3.0.0
1163 * @param string $recipient The recipient email address.
1164 * @param array $args Arguments.
1165 * @param null $null Unused.
1166 */
1167 apply_filters( 'woocommerce_email_recipient_backorder', get_option( 'woocommerce_stock_email_recipient' ), $args, null ),
1168 /**
1169 * Filter the subject of the backorder notification email.
1170 *
1171 * @since 3.0.0
1172 * @param string $subject The email subject.
1173 * @param array $args Arguments.
1174 * @param null $null Unused.
1175 */
1176 apply_filters( 'woocommerce_email_subject_backorder', $subject, $args, null ),
1177 /**
1178 * Filter the content of the backorder notification email.
1179 *
1180 * @since 3.0.0
1181 * @param string $message The email content.
1182 * @param array $args Arguments.
1183 * @param null $null Unused.
1184 */
1185 apply_filters( 'woocommerce_email_content_backorder', $message, $args ),
1186 /**
1187 * Filter the headers of the backorder notification email.
1188 *
1189 * @since 3.0.0
1190 * @param string $headers The email headers.
1191 * @param array $args Arguments.
1192 * @param null $null Unused.
1193 */
1194 apply_filters( 'woocommerce_email_headers', '', 'backorder', $args, null ),
1195 /**
1196 * Filter the attachments of the backorder notification email.
1197 *
1198 * @since 3.0.0
1199 * @param array $attachments The email attachments.
1200 * @param array $args Arguments.
1201 * @param null $null Unused.
1202 */
1203 apply_filters( 'woocommerce_email_attachments', array(), 'backorder', $args, null )
1204 );
1205
1206 $this->remove_email_sender_filters();
1207 }
1208
1209 /**
1210 * Adds Schema.org markup for order in JSON-LD format.
1211 *
1212 * @deprecated 3.0.0
1213 * @see WC_Structured_Data::generate_order_data()
1214 *
1215 * @since 2.6.0
1216 * @param WC_Order $order Order instance.
1217 * @param bool $sent_to_admin If should sent to admin.
1218 * @param bool $plain_text If is plain text email.
1219 */
1220 public function order_schema_markup( $order, $sent_to_admin = false, $plain_text = false ) {
1221 wc_deprecated_function( 'WC_Emails::order_schema_markup', '3.0', 'WC_Structured_Data::generate_order_data' );
1222
1223 WC()->structured_data->generate_order_data( $order, $sent_to_admin, $plain_text );
1224 WC()->structured_data->output_structured_data();
1225 }
1226 }
1227