PluginProbe ʕ •ᴥ•ʔ
WooCommerce / 3.9.0-rc.2
WooCommerce v3.9.0-rc.2
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 / emails / class-wc-email.php
woocommerce / includes / emails Last commit date
class-wc-email-cancelled-order.php 6 years ago class-wc-email-customer-completed-order.php 6 years ago class-wc-email-customer-invoice.php 6 years ago class-wc-email-customer-new-account.php 6 years ago class-wc-email-customer-note.php 6 years ago class-wc-email-customer-on-hold-order.php 6 years ago class-wc-email-customer-processing-order.php 6 years ago class-wc-email-customer-refunded-order.php 6 years ago class-wc-email-customer-reset-password.php 6 years ago class-wc-email-failed-order.php 6 years ago class-wc-email-new-order.php 6 years ago class-wc-email.php 6 years ago
class-wc-email.php
1062 lines
1 <?php
2 /**
3 * Class WC_Email file.
4 *
5 * @package WooCommerce\Emails
6 */
7
8 if ( ! defined( 'ABSPATH' ) ) {
9 exit;
10 }
11
12 if ( class_exists( 'WC_Email', false ) ) {
13 return;
14 }
15
16 /**
17 * Email Class
18 *
19 * WooCommerce Email Class which is extended by specific email template classes to add emails to WooCommerce
20 *
21 * @class WC_Email
22 * @version 2.5.0
23 * @package WooCommerce/Classes/Emails
24 * @extends WC_Settings_API
25 */
26 class WC_Email extends WC_Settings_API {
27
28 /**
29 * Email method ID.
30 *
31 * @var String
32 */
33 public $id;
34
35 /**
36 * Email method title.
37 *
38 * @var string
39 */
40 public $title;
41
42 /**
43 * 'yes' if the method is enabled.
44 *
45 * @var string yes, no
46 */
47 public $enabled;
48
49 /**
50 * Description for the email.
51 *
52 * @var string
53 */
54 public $description;
55
56 /**
57 * Default heading.
58 *
59 * Supported for backwards compatibility but we recommend overloading the
60 * get_default_x methods instead so localization can be done when needed.
61 *
62 * @var string
63 */
64 public $heading = '';
65
66 /**
67 * Default subject.
68 *
69 * Supported for backwards compatibility but we recommend overloading the
70 * get_default_x methods instead so localization can be done when needed.
71 *
72 * @var string
73 */
74 public $subject = '';
75
76 /**
77 * Plain text template path.
78 *
79 * @var string
80 */
81 public $template_plain;
82
83 /**
84 * HTML template path.
85 *
86 * @var string
87 */
88 public $template_html;
89
90 /**
91 * Template path.
92 *
93 * @var string
94 */
95 public $template_base;
96
97 /**
98 * Recipients for the email.
99 *
100 * @var string
101 */
102 public $recipient;
103
104 /**
105 * Object this email is for, for example a customer, product, or email.
106 *
107 * @var object|bool
108 */
109 public $object;
110
111 /**
112 * Mime boundary (for multipart emails).
113 *
114 * @var string
115 */
116 public $mime_boundary;
117
118 /**
119 * Mime boundary header (for multipart emails).
120 *
121 * @var string
122 */
123 public $mime_boundary_header;
124
125 /**
126 * True when email is being sent.
127 *
128 * @var bool
129 */
130 public $sending;
131
132 /**
133 * True when the email notification is sent manually only.
134 *
135 * @var bool
136 */
137 protected $manual = false;
138
139 /**
140 * True when the email notification is sent to customers.
141 *
142 * @var bool
143 */
144 protected $customer_email = false;
145
146 /**
147 * List of preg* regular expression patterns to search for,
148 * used in conjunction with $plain_replace.
149 * https://raw.github.com/ushahidi/wp-silcc/master/class.html2text.inc
150 *
151 * @var array $plain_search
152 * @see $plain_replace
153 */
154 public $plain_search = array(
155 "/\r/", // Non-legal carriage return.
156 '/&(nbsp|#0*160);/i', // Non-breaking space.
157 '/&(quot|rdquo|ldquo|#0*8220|#0*8221|#0*147|#0*148);/i', // Double quotes.
158 '/&(apos|rsquo|lsquo|#0*8216|#0*8217);/i', // Single quotes.
159 '/&gt;/i', // Greater-than.
160 '/&lt;/i', // Less-than.
161 '/&#0*38;/i', // Ampersand.
162 '/&amp;/i', // Ampersand.
163 '/&(copy|#0*169);/i', // Copyright.
164 '/&(trade|#0*8482|#0*153);/i', // Trademark.
165 '/&(reg|#0*174);/i', // Registered.
166 '/&(mdash|#0*151|#0*8212);/i', // mdash.
167 '/&(ndash|minus|#0*8211|#0*8722);/i', // ndash.
168 '/&(bull|#0*149|#0*8226);/i', // Bullet.
169 '/&(pound|#0*163);/i', // Pound sign.
170 '/&(euro|#0*8364);/i', // Euro sign.
171 '/&(dollar|#0*36);/i', // Dollar sign.
172 '/&[^&\s;]+;/i', // Unknown/unhandled entities.
173 '/[ ]{2,}/', // Runs of spaces, post-handling.
174 );
175
176 /**
177 * List of pattern replacements corresponding to patterns searched.
178 *
179 * @var array $plain_replace
180 * @see $plain_search
181 */
182 public $plain_replace = array(
183 '', // Non-legal carriage return.
184 ' ', // Non-breaking space.
185 '"', // Double quotes.
186 "'", // Single quotes.
187 '>', // Greater-than.
188 '<', // Less-than.
189 '&', // Ampersand.
190 '&', // Ampersand.
191 '(c)', // Copyright.
192 '(tm)', // Trademark.
193 '(R)', // Registered.
194 '--', // mdash.
195 '-', // ndash.
196 '*', // Bullet.
197 '£', // Pound sign.
198 'EUR', // Euro sign. € ?.
199 '$', // Dollar sign.
200 '', // Unknown/unhandled entities.
201 ' ', // Runs of spaces, post-handling.
202 );
203
204 /**
205 * Strings to find/replace in subjects/headings.
206 *
207 * @var array
208 */
209 protected $placeholders = array();
210
211 /**
212 * Strings to find in subjects/headings.
213 *
214 * @deprecated 3.2.0 in favour of placeholders
215 * @var array
216 */
217 public $find = array();
218
219 /**
220 * Strings to replace in subjects/headings.
221 *
222 * @deprecated 3.2.0 in favour of placeholders
223 * @var array
224 */
225 public $replace = array();
226
227 /**
228 * Constructor.
229 */
230 public function __construct() {
231 // Find/replace.
232 $this->placeholders = array_merge(
233 array(
234 '{site_title}' => $this->get_blogname(),
235 '{site_address}' => wp_parse_url( home_url(), PHP_URL_HOST ),
236 ),
237 $this->placeholders
238 );
239
240 // Init settings.
241 $this->init_form_fields();
242 $this->init_settings();
243
244 // Default template base if not declared in child constructor.
245 if ( is_null( $this->template_base ) ) {
246 $this->template_base = WC()->plugin_path() . '/templates/';
247 }
248
249 $this->email_type = $this->get_option( 'email_type' );
250 $this->enabled = $this->get_option( 'enabled' );
251
252 add_action( 'phpmailer_init', array( $this, 'handle_multipart' ) );
253 add_action( 'woocommerce_update_options_email_' . $this->id, array( $this, 'process_admin_options' ) );
254 }
255
256 /**
257 * Handle multipart mail.
258 *
259 * @param PHPMailer $mailer PHPMailer object.
260 * @return PHPMailer
261 */
262 public function handle_multipart( $mailer ) {
263 if ( $this->sending && 'multipart' === $this->get_email_type() ) {
264 $mailer->AltBody = wordwrap( // phpcs:ignore WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase
265 preg_replace( $this->plain_search, $this->plain_replace, wp_strip_all_tags( $this->get_content_plain() ) )
266 );
267 $this->sending = false;
268 }
269 return $mailer;
270 }
271
272 /**
273 * Format email string.
274 *
275 * @param mixed $string Text to replace placeholders in.
276 * @return string
277 */
278 public function format_string( $string ) {
279 $find = array_keys( $this->placeholders );
280 $replace = array_values( $this->placeholders );
281
282 // If using legacy find replace, add those to our find/replace arrays first. @todo deprecate in 4.0.0.
283 $find = array_merge( (array) $this->find, $find );
284 $replace = array_merge( (array) $this->replace, $replace );
285
286 // Take care of blogname which is no longer defined as a valid placeholder.
287 $find[] = '{blogname}';
288 $replace[] = $this->get_blogname();
289
290 // If using the older style filters for find and replace, ensure the array is associative and then pass through filters. @todo deprecate in 4.0.0.
291 if ( has_filter( 'woocommerce_email_format_string_replace' ) || has_filter( 'woocommerce_email_format_string_find' ) ) {
292 $legacy_find = $this->find;
293 $legacy_replace = $this->replace;
294
295 foreach ( $this->placeholders as $find => $replace ) {
296 $legacy_key = sanitize_title( str_replace( '_', '-', trim( $find, '{}' ) ) );
297 $legacy_find[ $legacy_key ] = $find;
298 $legacy_replace[ $legacy_key ] = $replace;
299 }
300
301 $string = str_replace( apply_filters( 'woocommerce_email_format_string_find', $legacy_find, $this ), apply_filters( 'woocommerce_email_format_string_replace', $legacy_replace, $this ), $string );
302 }
303
304 /**
305 * Filter for main find/replace.
306 *
307 * @since 3.2.0
308 */
309 return apply_filters( 'woocommerce_email_format_string', str_replace( $find, $replace, $string ), $this );
310 }
311
312 /**
313 * Set the locale to the store locale for customer emails to make sure emails are in the store language.
314 */
315 public function setup_locale() {
316 if ( $this->is_customer_email() && apply_filters( 'woocommerce_email_setup_locale', true ) ) {
317 wc_switch_to_site_locale();
318 }
319 }
320
321 /**
322 * Restore the locale to the default locale. Use after finished with setup_locale.
323 */
324 public function restore_locale() {
325 if ( $this->is_customer_email() && apply_filters( 'woocommerce_email_restore_locale', true ) ) {
326 wc_restore_locale();
327 }
328 }
329
330 /**
331 * Get email subject.
332 *
333 * @since 3.1.0
334 * @return string
335 */
336 public function get_default_subject() {
337 return $this->subject;
338 }
339
340 /**
341 * Get email heading.
342 *
343 * @since 3.1.0
344 * @return string
345 */
346 public function get_default_heading() {
347 return $this->heading;
348 }
349
350 /**
351 * Default content to show below main email content.
352 *
353 * @since 3.7.0
354 * @return string
355 */
356 public function get_default_additional_content() {
357 return '';
358 }
359
360 /**
361 * Return content from the additional_content field.
362 *
363 * Displayed above the footer.
364 *
365 * @since 3.7.0
366 * @return string
367 */
368 public function get_additional_content() {
369 $content = $this->get_option( 'additional_content', '' );
370
371 return apply_filters( 'woocommerce_email_additional_content_' . $this->id, $this->format_string( $content ), $this->object, $this );
372 }
373
374 /**
375 * Get email subject.
376 *
377 * @return string
378 */
379 public function get_subject() {
380 return apply_filters( 'woocommerce_email_subject_' . $this->id, $this->format_string( $this->get_option( 'subject', $this->get_default_subject() ) ), $this->object, $this );
381 }
382
383 /**
384 * Get email heading.
385 *
386 * @return string
387 */
388 public function get_heading() {
389 return apply_filters( 'woocommerce_email_heading_' . $this->id, $this->format_string( $this->get_option( 'heading', $this->get_default_heading() ) ), $this->object, $this );
390 }
391
392 /**
393 * Get valid recipients.
394 *
395 * @return string
396 */
397 public function get_recipient() {
398 $recipient = apply_filters( 'woocommerce_email_recipient_' . $this->id, $this->recipient, $this->object, $this );
399 $recipients = array_map( 'trim', explode( ',', $recipient ) );
400 $recipients = array_filter( $recipients, 'is_email' );
401 return implode( ', ', $recipients );
402 }
403
404 /**
405 * Get email headers.
406 *
407 * @return string
408 */
409 public function get_headers() {
410 $header = 'Content-Type: ' . $this->get_content_type() . "\r\n";
411
412 if ( in_array( $this->id, array( 'new_order', 'cancelled_order', 'failed_order' ), true ) ) {
413 if ( $this->object && $this->object->get_billing_email() && ( $this->object->get_billing_first_name() || $this->object->get_billing_last_name() ) ) {
414 $header .= 'Reply-to: ' . $this->object->get_billing_first_name() . ' ' . $this->object->get_billing_last_name() . ' <' . $this->object->get_billing_email() . ">\r\n";
415 }
416 } elseif ( $this->get_from_address() && $this->get_from_name() ) {
417 $header .= 'Reply-to: ' . $this->get_from_name() . ' <' . $this->get_from_address() . ">\r\n";
418 }
419
420 return apply_filters( 'woocommerce_email_headers', $header, $this->id, $this->object, $this );
421 }
422
423 /**
424 * Get email attachments.
425 *
426 * @return array
427 */
428 public function get_attachments() {
429 return apply_filters( 'woocommerce_email_attachments', array(), $this->id, $this->object, $this );
430 }
431
432 /**
433 * Return email type.
434 *
435 * @return string
436 */
437 public function get_email_type() {
438 return $this->email_type && class_exists( 'DOMDocument' ) ? $this->email_type : 'plain';
439 }
440
441 /**
442 * Get email content type.
443 *
444 * @return string
445 */
446 public function get_content_type() {
447 switch ( $this->get_email_type() ) {
448 case 'html':
449 return 'text/html';
450 case 'multipart':
451 return 'multipart/alternative';
452 default:
453 return 'text/plain';
454 }
455 }
456
457 /**
458 * Return the email's title
459 *
460 * @return string
461 */
462 public function get_title() {
463 return apply_filters( 'woocommerce_email_title', $this->title, $this );
464 }
465
466 /**
467 * Return the email's description
468 *
469 * @return string
470 */
471 public function get_description() {
472 return apply_filters( 'woocommerce_email_description', $this->description, $this );
473 }
474
475 /**
476 * Proxy to parent's get_option and attempt to localize the result using gettext.
477 *
478 * @param string $key Option key.
479 * @param mixed $empty_value Value to use when option is empty.
480 * @return string
481 */
482 public function get_option( $key, $empty_value = null ) {
483 $value = parent::get_option( $key, $empty_value );
484 return apply_filters( 'woocommerce_email_get_option', $value, $this, $value, $key, $empty_value );
485 }
486
487 /**
488 * Checks if this email is enabled and will be sent.
489 *
490 * @return bool
491 */
492 public function is_enabled() {
493 return apply_filters( 'woocommerce_email_enabled_' . $this->id, 'yes' === $this->enabled, $this->object, $this );
494 }
495
496 /**
497 * Checks if this email is manually sent
498 *
499 * @return bool
500 */
501 public function is_manual() {
502 return $this->manual;
503 }
504
505 /**
506 * Checks if this email is customer focussed.
507 *
508 * @return bool
509 */
510 public function is_customer_email() {
511 return $this->customer_email;
512 }
513
514 /**
515 * Get WordPress blog name.
516 *
517 * @return string
518 */
519 public function get_blogname() {
520 return wp_specialchars_decode( get_option( 'blogname' ), ENT_QUOTES );
521 }
522
523 /**
524 * Get email content.
525 *
526 * @return string
527 */
528 public function get_content() {
529 $this->sending = true;
530
531 if ( 'plain' === $this->get_email_type() ) {
532 $email_content = wordwrap( preg_replace( $this->plain_search, $this->plain_replace, wp_strip_all_tags( $this->get_content_plain() ) ), 70 );
533 } else {
534 $email_content = $this->get_content_html();
535 }
536
537 return $email_content;
538 }
539
540 /**
541 * Apply inline styles to dynamic content.
542 *
543 * We only inline CSS for html emails, and to do so we use Emogrifier library (if supported).
544 *
545 * @param string|null $content Content that will receive inline styles.
546 * @return string
547 */
548 public function style_inline( $content ) {
549 if ( in_array( $this->get_content_type(), array( 'text/html', 'multipart/alternative' ), true ) ) {
550 ob_start();
551 wc_get_template( 'emails/email-styles.php' );
552 $css = apply_filters( 'woocommerce_email_styles', ob_get_clean(), $this );
553
554 if ( $this->supports_emogrifier() ) {
555 $emogrifier_class = '\\Pelago\\Emogrifier';
556 if ( ! class_exists( $emogrifier_class ) ) {
557 include_once dirname( dirname( __FILE__ ) ) . '/libraries/class-emogrifier.php';
558 }
559 try {
560 $emogrifier = new $emogrifier_class( $content, $css );
561 $content = $emogrifier->emogrify();
562 } catch ( Exception $e ) {
563 $logger = wc_get_logger();
564 $logger->error( $e->getMessage(), array( 'source' => 'emogrifier' ) );
565 }
566 } else {
567 $content = '<style type="text/css">' . $css . '</style>' . $content;
568 }
569 }
570 return $content;
571 }
572
573 /**
574 * Return if emogrifier library is supported.
575 *
576 * @since 3.5.0
577 * @return bool
578 */
579 protected function supports_emogrifier() {
580 return class_exists( 'DOMDocument' ) && version_compare( PHP_VERSION, '5.5', '>=' );
581 }
582
583 /**
584 * Get the email content in plain text format.
585 *
586 * @return string
587 */
588 public function get_content_plain() {
589 return ''; }
590
591 /**
592 * Get the email content in HTML format.
593 *
594 * @return string
595 */
596 public function get_content_html() {
597 return ''; }
598
599 /**
600 * Get the from name for outgoing emails.
601 *
602 * @return string
603 */
604 public function get_from_name() {
605 $from_name = apply_filters( 'woocommerce_email_from_name', get_option( 'woocommerce_email_from_name' ), $this );
606 return wp_specialchars_decode( esc_html( $from_name ), ENT_QUOTES );
607 }
608
609 /**
610 * Get the from address for outgoing emails.
611 *
612 * @return string
613 */
614 public function get_from_address() {
615 $from_address = apply_filters( 'woocommerce_email_from_address', get_option( 'woocommerce_email_from_address' ), $this );
616 return sanitize_email( $from_address );
617 }
618
619 /**
620 * Send an email.
621 *
622 * @param string $to Email to.
623 * @param string $subject Email subject.
624 * @param string $message Email message.
625 * @param string $headers Email headers.
626 * @param array $attachments Email attachments.
627 * @return bool success
628 */
629 public function send( $to, $subject, $message, $headers, $attachments ) {
630 add_filter( 'wp_mail_from', array( $this, 'get_from_address' ) );
631 add_filter( 'wp_mail_from_name', array( $this, 'get_from_name' ) );
632 add_filter( 'wp_mail_content_type', array( $this, 'get_content_type' ) );
633
634 $message = apply_filters( 'woocommerce_mail_content', $this->style_inline( $message ) );
635 $mail_callback = apply_filters( 'woocommerce_mail_callback', 'wp_mail', $this );
636 $mail_callback_params = apply_filters( 'woocommerce_mail_callback_params', array( $to, $subject, $message, $headers, $attachments ), $this );
637 $return = $mail_callback( ...$mail_callback_params );
638
639 remove_filter( 'wp_mail_from', array( $this, 'get_from_address' ) );
640 remove_filter( 'wp_mail_from_name', array( $this, 'get_from_name' ) );
641 remove_filter( 'wp_mail_content_type', array( $this, 'get_content_type' ) );
642
643 return $return;
644 }
645
646 /**
647 * Initialise Settings Form Fields - these are generic email options most will use.
648 */
649 public function init_form_fields() {
650 /* translators: %s: list of placeholders */
651 $placeholder_text = sprintf( __( 'Available placeholders: %s', 'woocommerce' ), '<code>' . esc_html( implode( '</code>, <code>', array_keys( $this->placeholders ) ) ) . '</code>' );
652 $this->form_fields = array(
653 'enabled' => array(
654 'title' => __( 'Enable/Disable', 'woocommerce' ),
655 'type' => 'checkbox',
656 'label' => __( 'Enable this email notification', 'woocommerce' ),
657 'default' => 'yes',
658 ),
659 'subject' => array(
660 'title' => __( 'Subject', 'woocommerce' ),
661 'type' => 'text',
662 'desc_tip' => true,
663 'description' => $placeholder_text,
664 'placeholder' => $this->get_default_subject(),
665 'default' => '',
666 ),
667 'heading' => array(
668 'title' => __( 'Email heading', 'woocommerce' ),
669 'type' => 'text',
670 'desc_tip' => true,
671 'description' => $placeholder_text,
672 'placeholder' => $this->get_default_heading(),
673 'default' => '',
674 ),
675 'additional_content' => array(
676 'title' => __( 'Additional content', 'woocommerce' ),
677 'description' => __( 'Text to appear below the main email content.', 'woocommerce' ) . ' ' . $placeholder_text,
678 'css' => 'width:400px; height: 75px;',
679 'placeholder' => __( 'N/A', 'woocommerce' ),
680 'type' => 'textarea',
681 'default' => $this->get_default_additional_content(),
682 'desc_tip' => true,
683 ),
684 'email_type' => array(
685 'title' => __( 'Email type', 'woocommerce' ),
686 'type' => 'select',
687 'description' => __( 'Choose which format of email to send.', 'woocommerce' ),
688 'default' => 'html',
689 'class' => 'email_type wc-enhanced-select',
690 'options' => $this->get_email_type_options(),
691 'desc_tip' => true,
692 ),
693 );
694 }
695
696 /**
697 * Email type options.
698 *
699 * @return array
700 */
701 public function get_email_type_options() {
702 $types = array( 'plain' => __( 'Plain text', 'woocommerce' ) );
703
704 if ( class_exists( 'DOMDocument' ) ) {
705 $types['html'] = __( 'HTML', 'woocommerce' );
706 $types['multipart'] = __( 'Multipart', 'woocommerce' );
707 }
708
709 return $types;
710 }
711
712 /**
713 * Admin Panel Options Processing.
714 */
715 public function process_admin_options() {
716 // Save regular options.
717 parent::process_admin_options();
718
719 $post_data = $this->get_post_data();
720
721 // Save templates.
722 if ( isset( $post_data['template_html_code'] ) ) {
723 $this->save_template( $post_data['template_html_code'], $this->template_html );
724 }
725 if ( isset( $post_data['template_plain_code'] ) ) {
726 $this->save_template( $post_data['template_plain_code'], $this->template_plain );
727 }
728 }
729
730 /**
731 * Get template.
732 *
733 * @param string $type Template type. Can be either 'template_html' or 'template_plain'.
734 * @return string
735 */
736 public function get_template( $type ) {
737 $type = basename( $type );
738
739 if ( 'template_html' === $type ) {
740 return $this->template_html;
741 } elseif ( 'template_plain' === $type ) {
742 return $this->template_plain;
743 }
744 return '';
745 }
746
747 /**
748 * Save the email templates.
749 *
750 * @since 2.4.0
751 * @param string $template_code Template code.
752 * @param string $template_path Template path.
753 */
754 protected function save_template( $template_code, $template_path ) {
755 if ( current_user_can( 'edit_themes' ) && ! empty( $template_code ) && ! empty( $template_path ) ) {
756 $saved = false;
757 $file = get_stylesheet_directory() . '/' . WC()->template_path() . $template_path;
758 $code = wp_unslash( $template_code );
759
760 if ( is_writeable( $file ) ) { // phpcs:ignore WordPress.VIP.FileSystemWritesDisallow.file_ops_is_writeable
761 $f = fopen( $file, 'w+' ); // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_read_fopen
762
763 if ( false !== $f ) {
764 fwrite( $f, $code ); // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_read_fwrite
765 fclose( $f ); // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_read_fclose
766 $saved = true;
767 }
768 }
769
770 if ( ! $saved ) {
771 $redirect = add_query_arg( 'wc_error', rawurlencode( __( 'Could not write to template file.', 'woocommerce' ) ) );
772 wp_safe_redirect( $redirect );
773 exit;
774 }
775 }
776 }
777
778 /**
779 * Get the template file in the current theme.
780 *
781 * @param string $template Template name.
782 *
783 * @return string
784 */
785 public function get_theme_template_file( $template ) {
786 return get_stylesheet_directory() . '/' . apply_filters( 'woocommerce_template_directory', 'woocommerce', $template ) . '/' . $template;
787 }
788
789 /**
790 * Move template action.
791 *
792 * @param string $template_type Template type.
793 */
794 protected function move_template_action( $template_type ) {
795 $template = $this->get_template( $template_type );
796 if ( ! empty( $template ) ) {
797 $theme_file = $this->get_theme_template_file( $template );
798
799 if ( wp_mkdir_p( dirname( $theme_file ) ) && ! file_exists( $theme_file ) ) {
800
801 // Locate template file.
802 $core_file = $this->template_base . $template;
803 $template_file = apply_filters( 'woocommerce_locate_core_template', $core_file, $template, $this->template_base, $this->id );
804
805 // Copy template file.
806 copy( $template_file, $theme_file );
807
808 /**
809 * Action hook fired after copying email template file.
810 *
811 * @param string $template_type The copied template type
812 * @param string $email The email object
813 */
814 do_action( 'woocommerce_copy_email_template', $template_type, $this );
815
816 ?>
817 <div class="updated">
818 <p><?php echo esc_html__( 'Template file copied to theme.', 'woocommerce' ); ?></p>
819 </div>
820 <?php
821 }
822 }
823 }
824
825 /**
826 * Delete template action.
827 *
828 * @param string $template_type Template type.
829 */
830 protected function delete_template_action( $template_type ) {
831 $template = $this->get_template( $template_type );
832
833 if ( $template ) {
834 if ( ! empty( $template ) ) {
835 $theme_file = $this->get_theme_template_file( $template );
836
837 if ( file_exists( $theme_file ) ) {
838 unlink( $theme_file ); // phpcs:ignore WordPress.VIP.FileSystemWritesDisallow.file_ops_unlink
839
840 /**
841 * Action hook fired after deleting template file.
842 *
843 * @param string $template The deleted template type
844 * @param string $email The email object
845 */
846 do_action( 'woocommerce_delete_email_template', $template_type, $this );
847 ?>
848 <div class="updated">
849 <p><?php echo esc_html__( 'Template file deleted from theme.', 'woocommerce' ); ?></p>
850 </div>
851 <?php
852 }
853 }
854 }
855 }
856
857 /**
858 * Admin actions.
859 */
860 protected function admin_actions() {
861 // Handle any actions.
862 if (
863 ( ! empty( $this->template_html ) || ! empty( $this->template_plain ) )
864 && ( ! empty( $_GET['move_template'] ) || ! empty( $_GET['delete_template'] ) )
865 && 'GET' === $_SERVER['REQUEST_METHOD'] // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotValidated
866 ) {
867 if ( empty( $_GET['_wc_email_nonce'] ) || ! wp_verify_nonce( wc_clean( wp_unslash( $_GET['_wc_email_nonce'] ) ), 'woocommerce_email_template_nonce' ) ) {
868 wp_die( esc_html__( 'Action failed. Please refresh the page and retry.', 'woocommerce' ) );
869 }
870
871 if ( ! current_user_can( 'edit_themes' ) ) {
872 wp_die( esc_html__( 'You don&#8217;t have permission to do this.', 'woocommerce' ) );
873 }
874
875 if ( ! empty( $_GET['move_template'] ) ) {
876 $this->move_template_action( wc_clean( wp_unslash( $_GET['move_template'] ) ) );
877 }
878
879 if ( ! empty( $_GET['delete_template'] ) ) {
880 $this->delete_template_action( wc_clean( wp_unslash( $_GET['delete_template'] ) ) );
881 }
882 }
883 }
884
885 /**
886 * Admin Options.
887 *
888 * Setup the email settings screen.
889 * Override this in your email.
890 *
891 * @since 1.0.0
892 */
893 public function admin_options() {
894 // Do admin actions.
895 $this->admin_actions();
896 ?>
897 <h2><?php echo esc_html( $this->get_title() ); ?> <?php wc_back_link( __( 'Return to emails', 'woocommerce' ), admin_url( 'admin.php?page=wc-settings&tab=email' ) ); ?></h2>
898
899 <?php echo wpautop( wp_kses_post( $this->get_description() ) ); // phpcs:ignore WordPress.XSS.EscapeOutput.OutputNotEscaped ?>
900
901 <?php
902 /**
903 * Action hook fired before displaying email settings.
904 *
905 * @param string $email The email object
906 */
907 do_action( 'woocommerce_email_settings_before', $this );
908 ?>
909
910 <table class="form-table">
911 <?php $this->generate_settings_html(); ?>
912 </table>
913
914 <?php
915 /**
916 * Action hook fired after displaying email settings.
917 *
918 * @param string $email The email object
919 */
920 do_action( 'woocommerce_email_settings_after', $this );
921 ?>
922
923 <?php
924
925 if ( current_user_can( 'edit_themes' ) && ( ! empty( $this->template_html ) || ! empty( $this->template_plain ) ) ) {
926 ?>
927 <div id="template">
928 <?php
929 $templates = array(
930 'template_html' => __( 'HTML template', 'woocommerce' ),
931 'template_plain' => __( 'Plain text template', 'woocommerce' ),
932 );
933
934 foreach ( $templates as $template_type => $title ) :
935 $template = $this->get_template( $template_type );
936
937 if ( empty( $template ) ) {
938 continue;
939 }
940
941 $local_file = $this->get_theme_template_file( $template );
942 $core_file = $this->template_base . $template;
943 $template_file = apply_filters( 'woocommerce_locate_core_template', $core_file, $template, $this->template_base, $this->id );
944 $template_dir = apply_filters( 'woocommerce_template_directory', 'woocommerce', $template );
945 ?>
946 <div class="template <?php echo esc_attr( $template_type ); ?>">
947 <h4><?php echo wp_kses_post( $title ); ?></h4>
948
949 <?php if ( file_exists( $local_file ) ) : ?>
950 <p>
951 <a href="#" class="button toggle_editor"></a>
952
953 <?php if ( is_writable( $local_file ) ) : // phpcs:ignore WordPress.VIP.FileSystemWritesDisallow.file_ops_is_writable ?>
954 <a href="<?php echo esc_url( wp_nonce_url( remove_query_arg( array( 'move_template', 'saved' ), add_query_arg( 'delete_template', $template_type ) ), 'woocommerce_email_template_nonce', '_wc_email_nonce' ) ); ?>" class="delete_template button">
955 <?php esc_html_e( 'Delete template file', 'woocommerce' ); ?>
956 </a>
957 <?php endif; ?>
958
959 <?php
960 /* translators: %s: Path to template file */
961 printf( esc_html__( 'This template has been overridden by your theme and can be found in: %s.', 'woocommerce' ), '<code>' . esc_html( trailingslashit( basename( get_stylesheet_directory() ) ) . $template_dir . '/' . $template ) . '</code>' );
962 ?>
963 </p>
964
965 <div class="editor" style="display:none">
966 <textarea class="code" cols="25" rows="20"
967 <?php
968 if ( ! is_writable( $local_file ) ) : // phpcs:ignore WordPress.VIP.FileSystemWritesDisallow.file_ops_is_writable
969 ?>
970 readonly="readonly" disabled="disabled"
971 <?php else : ?>
972 data-name="<?php echo esc_attr( $template_type ) . '_code'; ?>"<?php endif; ?>><?php echo esc_html( file_get_contents( $local_file ) ); // phpcs:ignore WordPress.WP.AlternativeFunctions.file_get_contents_file_get_contents ?></textarea>
973 </div>
974 <?php elseif ( file_exists( $template_file ) ) : ?>
975 <p>
976 <a href="#" class="button toggle_editor"></a>
977
978 <?php
979 $emails_dir = get_stylesheet_directory() . '/' . $template_dir . '/emails';
980 $templates_dir = get_stylesheet_directory() . '/' . $template_dir;
981 $theme_dir = get_stylesheet_directory();
982
983 if ( is_dir( $emails_dir ) ) {
984 $target_dir = $emails_dir;
985 } elseif ( is_dir( $templates_dir ) ) {
986 $target_dir = $templates_dir;
987 } else {
988 $target_dir = $theme_dir;
989 }
990
991 if ( is_writable( $target_dir ) ) : // phpcs:ignore WordPress.VIP.FileSystemWritesDisallow.file_ops_is_writable
992 ?>
993 <a href="<?php echo esc_url( wp_nonce_url( remove_query_arg( array( 'delete_template', 'saved' ), add_query_arg( 'move_template', $template_type ) ), 'woocommerce_email_template_nonce', '_wc_email_nonce' ) ); ?>" class="button">
994 <?php esc_html_e( 'Copy file to theme', 'woocommerce' ); ?>
995 </a>
996 <?php endif; ?>
997
998 <?php
999 /* translators: 1: Path to template file 2: Path to theme folder */
1000 printf( esc_html__( 'To override and edit this email template copy %1$s to your theme folder: %2$s.', 'woocommerce' ), '<code>' . esc_html( plugin_basename( $template_file ) ) . '</code>', '<code>' . esc_html( trailingslashit( basename( get_stylesheet_directory() ) ) . $template_dir . '/' . $template ) . '</code>' );
1001 ?>
1002 </p>
1003
1004 <div class="editor" style="display:none">
1005 <textarea class="code" readonly="readonly" disabled="disabled" cols="25" rows="20"><?php echo esc_html( file_get_contents( $template_file ) ); // phpcs:ignore WordPress.WP.AlternativeFunctions.file_get_contents_file_get_contents ?></textarea>
1006 </div>
1007 <?php else : ?>
1008 <p><?php esc_html_e( 'File was not found.', 'woocommerce' ); ?></p>
1009 <?php endif; ?>
1010 </div>
1011 <?php endforeach; ?>
1012 </div>
1013
1014 <?php
1015 wc_enqueue_js(
1016 "jQuery( 'select.email_type' ).change( function() {
1017
1018 var val = jQuery( this ).val();
1019
1020 jQuery( '.template_plain, .template_html' ).show();
1021
1022 if ( val != 'multipart' && val != 'html' ) {
1023 jQuery('.template_html').hide();
1024 }
1025
1026 if ( val != 'multipart' && val != 'plain' ) {
1027 jQuery('.template_plain').hide();
1028 }
1029
1030 }).change();
1031
1032 var view = '" . esc_js( __( 'View template', 'woocommerce' ) ) . "';
1033 var hide = '" . esc_js( __( 'Hide template', 'woocommerce' ) ) . "';
1034
1035 jQuery( 'a.toggle_editor' ).text( view ).toggle( function() {
1036 jQuery( this ).text( hide ).closest(' .template' ).find( '.editor' ).slideToggle();
1037 return false;
1038 }, function() {
1039 jQuery( this ).text( view ).closest( '.template' ).find( '.editor' ).slideToggle();
1040 return false;
1041 } );
1042
1043 jQuery( 'a.delete_template' ).click( function() {
1044 if ( window.confirm('" . esc_js( __( 'Are you sure you want to delete this template file?', 'woocommerce' ) ) . "') ) {
1045 return true;
1046 }
1047
1048 return false;
1049 });
1050
1051 jQuery( '.editor textarea' ).change( function() {
1052 var name = jQuery( this ).attr( 'data-name' );
1053
1054 if ( name ) {
1055 jQuery( this ).attr( 'name', name );
1056 }
1057 });"
1058 );
1059 }
1060 }
1061 }
1062