PluginProbe
WP Offload SES Lite / 1.3
WP Offload SES Lite v1.3
1.8.2 trunk 0.1.2 0.2.1 0.7.2.1 0.8.2 1.0 1.1 1.2 1.2.1 1.2.2 1.3 1.4 1.4.1 1.4.2 1.4.3 1.4.4 1.4.5 1.4.6 1.6.0 1.6.1 1.6.2 1.6.3 1.6.4 1.6.5 All 33 releases
wp-ses / classes / Email.php

Email.php in WP Offload SES Lite 1.3, at classes/Email.php

543 lines 13.4 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Builds an email to be sent.
4 *
5 * @author Delicious Brains
6 * @package WP Offload SES
7 */
8
9 namespace DeliciousBrains\WP_Offload_SES;
10
11 use DeliciousBrains\WP_Offload_SES\WP_Offload_SES;
12 use DeliciousBrains\WP_Offload_SES\Utils;
13
14 /**
15 * Class Email
16 *
17 * @since 1.0.0
18 */
19 class Email {
20
21 /**
22 * The PHPMailer class included in WordPress core.
23 *
24 * @var \PHPMailer 5.2.10
25 */
26 private $mail;
27
28 /**
29 * The from address.
30 *
31 * @var string
32 */
33 private $from;
34
35 /**
36 * The from name.
37 *
38 * @var string
39 */
40 private $from_name;
41
42 /**
43 * The charset to use in the email.
44 *
45 * @var string
46 */
47 private $charset;
48
49 /**
50 * The content type of the email.
51 *
52 * @var string
53 */
54 private $content_type;
55
56 /**
57 * Init WP_Offload_SES_Email class
58 *
59 * @param string|array $to The recipient of the email.
60 * @param string $subject The subject of the email.
61 * @param string $message The email message.
62 * @param string|array $headers Headers to include in the email.
63 * @param string|array $attachments Attachments to include in the email.
64 */
65 public function __construct( $to, $subject, $message, $headers, $attachments ) {
66 require_once ABSPATH . WPINC . '/class-phpmailer.php';
67 $this->mail = new \PHPMailer( true );
68 $this->to( $to );
69 $this->subject( $subject );
70 $this->body( $message );
71 $this->headers( $headers );
72 $this->attachments( $attachments );
73 }
74
75 /**
76 * Maybe convert to array
77 *
78 * @param string|array $value The value to process.
79 *
80 * @return array
81 */
82 private function maybe_convert_to_array( $value ) {
83 if ( ! is_array( $value ) ) {
84 return explode( ',', $value );
85 }
86
87 return $value;
88 }
89
90 /**
91 * Maybe split recipient
92 *
93 * @param string $recipient the recipient to process.
94 *
95 * @return array
96 */
97 private function maybe_split_recipient( $recipient ) {
98 // Break $recipient into name and address parts if in the format "Foo <bar@baz.com>".
99 $recipient_name = '';
100
101 if ( preg_match( '/(.*)<(.+)>/', $recipient, $matches ) ) {
102 if ( 3 === count( $matches ) ) {
103 $recipient_name = $matches[1];
104 $recipient = $matches[2];
105 }
106 }
107
108 return array(
109 'name' => $recipient_name,
110 'email' => $recipient,
111 );
112 }
113
114 /**
115 * Maybe log an unverified sender.
116 *
117 * @param string $email The email address to add if unverified.
118 */
119 private function maybe_log_unverified_sender( $email ) {
120 global $wp_offload_ses;
121
122 if ( ! $wp_offload_ses->is_verified_email_address( $email ) ) {
123 $unverified_senders = get_transient( 'wposes_unverified_senders' );
124
125 if ( false === $unverified_senders ) {
126 $unverified_senders = array();
127 }
128
129 if ( ! in_array( $email, $unverified_senders, true ) ) {
130 $unverified_senders[] = $email;
131 set_transient( 'wposes_unverified_senders', $unverified_senders );
132 }
133 }
134 }
135
136 /**
137 * Set to
138 *
139 * @param string|array $to The recipient of the email.
140 */
141 private function to( $to ) {
142 $to = $this->maybe_convert_to_array( $to );
143
144 foreach ( $to as $recipient ) {
145 try {
146 $recipient = $this->maybe_split_recipient( $recipient );
147 $this->mail->AddAddress( $recipient['email'], $recipient['name'] );
148 } catch ( \Exception $e ) {
149 continue;
150 }
151 }
152 }
153
154 /**
155 * Set from
156 */
157 private function from() {
158 /** @var WP_Offload_SES $wp_offload_ses */
159 global $wp_offload_ses;
160
161 $wp_default_email = Utils::get_wordpress_default_email( false );
162
163 if ( is_null( $this->from ) || $wp_default_email === $this->from ) {
164
165 if ( false !== $wp_offload_ses->settings->get_setting( 'default-email', false ) ) {
166 $this->from = $wp_offload_ses->settings->get_setting( 'default-email' );
167 } else {
168 $this->from = $wp_default_email;
169 }
170 }
171
172 if ( is_null( $this->from_name ) ) {
173 $this->from_name = 'WordPress';
174
175 if ( false !== $wp_offload_ses->settings->get_setting( 'default-email-name', false ) ) {
176 $this->from_name = $wp_offload_ses->settings->get_setting( 'default-email-name' );
177 }
178 }
179
180 $this->mail->From = apply_filters( 'wp_mail_from', $this->from );
181 $this->mail->FromName = apply_filters( 'wp_mail_from_name', $this->from_name );
182
183 // Log the email address if it isn't verified.
184 $this->maybe_log_unverified_sender( $this->mail->From );
185 }
186
187 /**
188 * Set subject
189 *
190 * @param string $subject The subject of the email.
191 */
192 private function subject( $subject ) {
193 $this->mail->Subject = $subject;
194 }
195
196 /**
197 * Set body
198 *
199 * @param string $body The body of the email.
200 */
201 private function body( $body ) {
202 $this->mail->Body = $body;
203 }
204
205 /**
206 * Set headers
207 *
208 * @param string|array $headers Headers to include in the email.
209 */
210 private function headers( $headers ) {
211 if ( empty( $headers ) ) {
212 return;
213 }
214
215 // Handle newline-delimited string list of headers.
216 if ( ! is_array( $headers ) ) {
217 $headers = trim( str_replace( "\r\n", "\n", $headers ) );
218 $headers = explode( "\n", $headers );
219 }
220
221 foreach ( $headers as $header ) {
222 list( $name, $content ) = explode( ':', trim( $header ), 2 );
223 $name = trim( $name );
224 $content = trim( $content );
225 $this->handle_headers( $name, $content );
226 }
227 }
228
229 /**
230 * Handle headers
231 *
232 * @param string $name The name of the header.
233 * @param string $content The header content.
234 */
235 private function handle_headers( $name, $content ) {
236 switch ( strtolower( $name ) ) {
237 case 'from':
238 $this->header_from( $content );
239 break;
240 case 'content-type':
241 $this->header_content_type( $content );
242 break;
243 case 'cc':
244 $this->header_cc( $content, 'cc' );
245 break;
246 case 'bcc':
247 $this->header_cc( $content, 'bcc' );
248 break;
249 case 'reply-to':
250 $this->header_reply_to( $content );
251 break;
252 default:
253 if ( 'mime-version' === strtolower( $name ) ) {
254 // PHPMailer adds mime-version, skip
255 break;
256 }
257
258 $this->mail->addCustomHeader( $name, $content );
259 }
260 }
261
262 /**
263 * Header from
264 *
265 * @param string $content The from header content.
266 */
267 private function header_from( $content ) {
268 $recipient = $this->maybe_split_recipient( $content );
269 $this->from = $recipient['email'];
270 $this->from_name = $recipient['name'];
271 }
272
273 /**
274 * Header content type
275 *
276 * @param string $content Header content.
277 */
278 private function header_content_type( $content ) {
279 if ( false !== strpos( $content, ';' ) ) {
280 list( $content_type, $charset ) = explode( ';', $content );
281 $this->content_type = trim( $content_type );
282
283 if ( false !== stripos( $charset, 'charset=' ) ) {
284 $this->charset = trim( str_replace( array( 'charset=', '"' ), '', $charset ) );
285 } elseif ( false !== stripos( $content_type, 'multipart' ) && false !== stripos( $charset, 'boundary=' ) ) {
286 $boundary = trim( str_replace( array( 'BOUNDARY=', 'boundary=', '"' ), '', $charset ) );
287 $this->mail->AddCustomHeader( sprintf( "Content-Type: %s;\n\t boundary=\"%s\"", $content_type, $boundary ) );
288 }
289 } elseif ( '' !== trim( $content ) ) {
290 $this->content_type = trim( $content );
291 }
292 }
293
294 /**
295 * Header Cc
296 *
297 * @param string $content Header content.
298 * @param string $type Cc by default.
299 */
300 private function header_cc( $content, $type = 'cc' ) {
301 $recipient = $this->maybe_split_recipient( $content );
302
303 try {
304 switch ( $type ) {
305 case 'cc':
306 $this->mail->addCC( $recipient['email'], $recipient['name'] );
307 break;
308 case 'bcc':
309 $this->mail->addBCC( $recipient['email'], $recipient['name'] );
310 break;
311 }
312 } catch ( \Exception $e ) {
313 return;
314 }
315 }
316
317 /**
318 * Header Reply-To
319 *
320 * @param string $content Header content.
321 */
322 private function header_reply_to( $content ) {
323 $reply_to = $this->maybe_convert_to_array( $content );
324
325 foreach ( $reply_to as $recipient ) {
326 try {
327 $recipient = $this->maybe_split_recipient( $recipient );
328 $this->mail->addReplyTo( $recipient['email'], $recipient['name'] );
329 } catch ( \Exception $e ) {
330 continue;
331 }
332 }
333 }
334
335 /**
336 * Set attachments
337 *
338 * @param string|array $attachments Attachments to add.
339 */
340 private function attachments( $attachments ) {
341 if ( ! empty( $attachments ) ) {
342
343 // Handle newline-delimited string list of multiple file names
344 if ( ! is_array( $attachments ) ) {
345 $attachments = explode( "\n", str_replace( "\r\n", "\n", $attachments ) );
346 }
347
348 foreach ( $attachments as $attachment ) {
349 try {
350 $this->mail->AddAttachment( $attachment );
351 } catch ( \Exception $e ) {
352 continue;
353 }
354 }
355 }
356 }
357
358 /**
359 * Set content type
360 */
361 private function content_type() {
362 if ( is_null( $this->content_type ) ) {
363 $this->content_type = apply_filters( 'wp_mail_content_type', 'text/plain' );
364 }
365
366 $this->mail->ContentType = $this->content_type;
367
368 if ( 'text/html' === $this->mail->ContentType ) {
369 $this->mail->IsHTML( true );
370 }
371 }
372
373 /**
374 * Set charset
375 */
376 private function charset() {
377 if ( is_null( $this->charset ) ) {
378 $this->charset = get_bloginfo( 'charset' );
379 }
380
381 $this->mail->CharSet = apply_filters( 'wp_mail_charset', $this->charset );
382 }
383
384 /**
385 * Sets the Sender/return path.
386 */
387 private function return_path() {
388 /** @var WP_Offload_SES $wp_offload_ses */
389 global $wp_offload_ses;
390
391 $return_path = $wp_offload_ses->settings->get_setting( 'return-path', false );
392
393 if ( ! $return_path ) {
394 $return_path = $this->mail->From;
395 }
396
397 $this->mail->addCustomHeader( 'Return-Path', $return_path );
398 }
399
400 /**
401 * Maybe override the Reply-To header.
402 */
403 private function maybe_override_reply_to() {
404 /** @var WP_Offload_SES $wp_offload_ses */
405 global $wp_offload_ses;
406
407 $reply_to = $wp_offload_ses->settings->get_setting( 'reply-to', '' );
408
409 if ( '' !== $reply_to && 'headers' !== $reply_to ) {
410 $this->mail->clearReplyTos();
411 $this->mail->addReplyTo( $reply_to );
412 }
413 }
414
415 /**
416 * Prepare
417 *
418 * Use PHPMailer to generate correct email format
419 *
420 * @return string
421 * @throws \phpmailerException
422 */
423 public function prepare( $email_id = null ) {
424 /** @var WP_Offload_SES $wp_offload_ses */
425 global $wp_offload_ses;
426
427 $this->from();
428 $this->content_type();
429 $this->charset();
430 $this->return_path();
431 $this->maybe_override_reply_to();
432
433 if ( null !== $email_id && 'text/html' === $this->mail->ContentType ) {
434 $this->mail->Body = make_clickable( $this->mail->Body );
435 $this->mail->Body = $wp_offload_ses->get_email_events()->filter_email_content( $email_id, $this->mail->Body );
436 }
437
438 // Fires after PHPMailer is initalized.
439 do_action_ref_array( 'phpmailer_init', array( &$this->mail ) );
440
441 /**
442 * This will need to be updated if WordPress updates to
443 * PHPMailer 6.0 (or if we include our own version of it).
444 */
445 try {
446 $this->mail->preSend();
447 } catch ( \phpmailerException $exception ) {
448 return $exception;
449 } catch ( \Exception $exception ) {
450 return $exception;
451 }
452
453 return $this->mail->getSentMIMEMessage();
454 }
455
456 /**
457 * View the email. Returns HTML used in modal.
458 *
459 * @param array $email_data Additional info about the email.
460 *
461 * @return void
462 */
463 public function view( $email_data = array() ) {
464 global $wp_offload_ses;
465
466 $this->from();
467 $this->content_type();
468 $this->charset();
469 $this->return_path();
470
471 $to = implode( ', ', array_keys( $this->mail->getAllRecipientAddresses() ) );
472 $opens = '';
473 $clicks = '';
474 $status = '';
475 $sent = '';
476 $actions = $wp_offload_ses->get_email_action_links( $email_data['id'], $email_data['status'] );
477 unset( $actions['view'] );
478
479 // Maybe add HTML line breaks.
480 if ( 'text/html' !== $this->mail->ContentType ) {
481 $this->mail->Body = nl2br( $this->mail->Body );
482 }
483
484 if ( isset( $email_data['status_i18n'] ) ) {
485 $status = sprintf( __( 'Status: %s', 'wp-offload-ses' ), $email_data['status_i18n'] );
486 }
487
488 if ( isset( $email_data['sent'] ) && $email_data['sent'] ) {
489 $formatted = Utils::get_date_and_time( $email_data['sent'] );
490 $sent = sprintf( __( 'Sent: %1$s at %2$s', 'wp-offload-ses' ), $formatted['date'], $formatted['time'] );
491 }
492
493 if ( isset( $email_data['open_count'] ) && (int) $email_data['open_count'] ) {
494 $formatted = Utils::get_date_and_time( $email_data['last_opened'] );
495
496 if ( $formatted ) {
497 $opens = sprintf(
498 __( 'Opens: %1$d (Last Opened %2$s at %3$s)', 'wp-offload-ses' ),
499 $email_data['open_count'],
500 $formatted['date'],
501 $formatted['time']
502 );
503 }
504 }
505
506 if ( isset( $email_data['click_count'] ) && (int) $email_data['click_count'] ) {
507 $formatted = Utils::get_date_and_time( $email_data['last_clicked'] );
508
509 if ( $formatted ) {
510 $clicks = sprintf(
511 __( 'Clicks: %1$d (Last Clicked %2$s at %3$s)', 'wp-offload-ses' ),
512 $email_data['click_count'],
513 $formatted['date'],
514 $formatted['time']
515 );
516 }
517 }
518 ?>
519 <div id="wposes-email-wrap">
520 <h3 id="wposes-email-subject"><?php echo esc_html( $this->mail->Subject ); ?></h3>
521 <span id="wposes-email-from"><?php printf( __( 'From: %1$s &lt;%2$s&gt;', 'wp-offload-ses' ), $this->mail->FromName, $this->mail->From ); ?></span>
522 <span id="wposes-email-to"><?php printf( __( 'To: %s', 'wp-offload-ses' ), $to ); ?></span>
523 <span id="wposes-email-sent"><?php echo $sent; ?></span>
524
525 <div id="wposes-email-content"><?php echo $this->mail->Body; ?></div>
526
527 <div class="actions select">
528 <span id="wposes-email-info" style="float: left;">
529 <span id="wposes-email-status"><?php echo $status; ?></span>
530 <span id="wposes-email-opens"><?php echo $opens; ?></span>
531 <span id="wposes-email-clicks"><?php echo $clicks; ?></span>
532 </span>
533
534 <span id="wposes-email-actions" style="float: right">
535 <?php echo implode( ' | ', $actions ); ?>
536 </span>
537 </div>
538 </div>
539 <?php
540 }
541
542 }
543