Completed
Push — master ( c7decf...185c12 )
by Jamie
11s
created

FrmEmail::format_reply_to()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 13
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 3
eloc 9
nc 3
nop 1
dl 0
loc 13
rs 9.4285
c 0
b 0
f 0
1
<?php
2
3
/**
4
 * @since 2.03.04
5
 */
6
class FrmEmail {
7
8
	private $email_key = '';
9
	private $to = array();
10
	private $cc = array();
11
	private $bcc = array();
12
	private $from = '';
13
	private $reply_to = '';
14
	private $subject = '';
15
	private $message = '';
16
	private $attachments = array();
17
18
	private $is_plain_text = false;
19
	private $is_single_recipient = false;
20
	private $include_user_info = false;
21
22
	private $charset = '';
23
	private $content_type = 'text/html';
24
25
	private $settings = array();
26
	private $entry;
27
	private $form;
28
29
	/**
30
	 * FrmEmail constructor
31
	 *
32
	 * @param object $action
33
	 * @param object $entry
34
	 * @param object $form
35
	 */
36
	public function __construct( $action, $entry, $form ) {
37
		$this->set_email_key( $action );
38
		$this->entry    = $entry;
39
		$this->form     = $form;
40
		$this->settings = $action->post_content;
41
42
		$user_id_args = self::get_user_id_args( $form->id );
43
		$this->set_to( $user_id_args );
44
		$this->set_cc( $user_id_args );
45
		$this->set_bcc( $user_id_args );
46
47
		if ( ! $this->has_recipients() ) {
48
			return;
49
		}
50
51
		$this->set_from( $user_id_args );
52
		$this->set_reply_to( $user_id_args );
53
54
		$this->set_include_user_info();
55
		$this->set_is_plain_text();
56
		$this->set_is_single_recipient( $action );
57
58
		$this->set_charset();
59
		$this->set_content_type();
60
61
		$this->set_subject();
62
		$this->set_message();
63
		$this->set_attachments();
64
	}
65
66
	/**
67
	 * Set the email key property
68
	 *
69
	 * @since 2.03.04
70
	 *
71
	 * @param object $action
72
	 */
73
	private function set_email_key( $action ) {
74
		$this->email_key = $action->ID;
75
	}
76
77
	/**
78
	 * Set the to addresses
79
	 *
80
	 * @since 2.03.04
81
	 *
82
	 * @param array $user_id_args
83
	 */
84
	private function set_to( $user_id_args ) {
85
		$to = $this->prepare_email_setting( $this->settings['email_to'], $user_id_args );
86
		$to = $this->explode_emails( $to );
87
88
		$where = array(
89
			'it.field_id !' => 0,
90
			'it.item_id'    => $this->entry->id,
91
		);
92
		$values = FrmEntryMeta::getAll( $where, ' ORDER BY fi.field_order' );
93
		$args   = array(
94
			'email_key' => $this->email_key,
95
			'entry'     => $this->entry,
96
			'form'      => $this->form,
97
		);
98
		$to     = apply_filters( 'frm_to_email', $to, $values, $this->form->id, $args );
99
100
		$this->to = array_unique( (array) $to );
101
102
		if ( empty( $this->to ) ) {
103
			return;
104
		}
105
106
		$this->handle_phone_numbers();
107
108
		$this->to = $this->format_recipients( $this->to );
109
	}
110
111
	/**
112
	 * Set the CC addresses
113
	 *
114
	 * @since 2.03.04
115
	 *
116
	 * @param array $user_id_args
117
	 */
118
	private function set_cc( $user_id_args ) {
119
		$this->cc = $this->prepare_additional_recipients( $this->settings['cc'], $user_id_args );
120
	}
121
122
	/**
123
	 * Set the BCC addresses
124
	 *
125
	 * @since 2.03.04
126
	 *
127
	 * @param array $user_id_args
128
	 */
129
	private function set_bcc( $user_id_args ) {
130
		$this->bcc = $this->prepare_additional_recipients( $this->settings['bcc'], $user_id_args );
131
	}
132
133
	/**
134
	 * Prepare CC and BCC recipients
135
	 *
136
	 * @since 2.03.04
137
	 *
138
	 * @param string $recipients
139
	 * @param array $user_id_args
140
	 *
141
	 * @return array
142
	 */
143
	private function prepare_additional_recipients( $recipients, $user_id_args ) {
144
		$recipients = $this->prepare_email_setting( $recipients, $user_id_args );
145
		$recipients = $this->explode_emails( $recipients );
146
147
		$recipients = array_unique( (array) $recipients );
148
		$recipients = $this->format_recipients( $recipients );
149
150
		return $recipients;
151
	}
152
153
	/**
154
	 * Set the From addresses
155
	 *
156
	 * @since 2.03.04
157
	 *
158
	 * @param array $user_id_args
159
	 */
160
	private function set_from( $user_id_args ) {
161
		if ( empty( $this->settings['from'] ) ) {
162
			$from = get_option( 'admin_email' );
163
		} else {
164
			$from = $this->prepare_email_setting( $this->settings['from'], $user_id_args );
165
		}
166
167
		$this->from = $this->format_from( $from );
168
	}
169
170
	/**
171
	 * Set the Reply To addresses
172
	 *
173
	 * @since 2.03.04
174
	 *
175
	 * @param array $user_id_args
176
	 */
177
	private function set_reply_to( $user_id_args ) {
178
		$this->reply_to = trim( $this->settings['reply_to'] );
179
180
		if ( empty( $this->reply_to ) ) {
181
			$this->reply_to = $this->from;
182
		} else {
183
			$this->reply_to = $this->prepare_email_setting( $this->settings['reply_to'], $user_id_args );
184
			$this->reply_to = $this->format_reply_to( $this->reply_to );
185
		}
186
	}
187
188
	/**
189
	 * Set the is_plain_text property
190
	 * This should be set before the message
191
	 *
192
	 * @since 2.03.04
193
	 */
194
	private function set_is_plain_text() {
195
		if ( $this->settings['plain_text'] ) {
196
			$this->is_plain_text = true;
197
		}
198
	}
199
200
	/**
201
	 * Set the include_user_info property
202
	 * This should be set before the message
203
	 *
204
	 * @since 2.03.04
205
	 */
206
	private function set_include_user_info() {
207
		if ( isset( $this->settings['inc_user_info'] ) ) {
208
			$this->include_user_info = $this->settings['inc_user_info'];
209
		}
210
	}
211
212
	/**
213
	 * Set the is_single_recipient property
214
	 *
215
	 * @since 2.03.04
216
	 *
217
	 * @param $action
218
	 */
219
	private function set_is_single_recipient( $action ) {
220
		$args = array(
221
			'form'   => $this->form,
222
			'entry'  => $this->entry,
223
			'action' => $action,
224
		);
225
226
		/**
227
		 * Send a separate email for email address in the "to" section
228
		 *
229
		 * @since 2.2.13
230
		 */
231
		$this->is_single_recipient = apply_filters( 'frm_send_separate_emails', false, $args );
232
	}
233
234
	/**
235
	 * Set the charset
236
	 *
237
	 * @since 2.03.04
238
	 */
239
	private function set_charset() {
240
		$this->charset = get_option( 'blog_charset' );
241
	}
242
243
	/**
244
	 * Set the content type
245
	 *
246
	 * @since 2.03.04
247
	 */
248
	private function set_content_type() {
249
		if ( $this->is_plain_text ) {
250
			$this->content_type = 'text/plain';
251
		}
252
	}
253
254
	/**
255
	 * Set the subject
256
	 *
257
	 * @since 2.03.04
258
	 */
259
	private function set_subject() {
260
		if ( empty( $this->settings['email_subject'] ) ) {
261
			$this->subject = sprintf( __( '%1$s Form submitted on %2$s', 'formidable' ), $this->form->name, '[sitename]' );
262
		} else {
263
			$this->subject = $this->settings['email_subject'];
264
		}
265
266
		$this->subject = FrmFieldsHelper::basic_replace_shortcodes( $this->subject, $this->form, $this->entry );
267
268
		$args          = array(
269
			'form'      => $this->form,
270
			'entry'     => $this->entry,
271
			'email_key' => $this->email_key,
272
		);
273
		$this->subject = apply_filters( 'frm_email_subject', $this->subject, $args );
274
275
		$this->subject = wp_specialchars_decode( strip_tags( stripslashes( $this->subject ) ), ENT_QUOTES );
276
	}
277
278
	/**
279
	 * Set the email message
280
	 *
281
	 * @since 2.03.04
282
	 */
283
	private function set_message() {
284
		$this->message = FrmFieldsHelper::basic_replace_shortcodes( $this->settings['email_message'], $this->form, $this->entry );
285
286
		$prev_mail_body = $this->message;
287
		$pass_entry     = clone $this->entry; // make a copy to prevent changes by reference
288
		$mail_body      = FrmEntriesHelper::replace_default_message( $prev_mail_body, array(
289
			'id'         => $this->entry->id,
290
			'entry'      => $pass_entry,
291
			'plain_text' => $this->is_plain_text,
292
			'user_info'  => $this->include_user_info,
293
		) );
294
295
		// Add the user info if it isn't already included
296
		if ( $this->include_user_info && $prev_mail_body == $mail_body ) {
297
			$data = maybe_unserialize( $this->entry->description );
298
			$mail_body .= "\r\n\r\n" . __( 'User Information', 'formidable' ) . "\r\n";
299
			$mail_body .= __( 'IP Address', 'formidable' ) . ': ' . $this->entry->ip . "\r\n";
0 ignored issues
show
introduced by
Expected next thing to be a escaping function, not '$this'
Loading history...
300
			$mail_body .= __( 'User-Agent (Browser/OS)', 'formidable' ) . ': ' . FrmEntryFormat::get_browser( $data['browser'] ) . "\r\n";
0 ignored issues
show
introduced by
Expected a sanitizing function (see Codex for 'Data Validation'), but instead saw 'FrmEntryFormat'
Loading history...
301
			$mail_body .= __( 'Referrer', 'formidable' ) . ': ' . $data['referrer'] . "\r\n";
0 ignored issues
show
introduced by
Expected next thing to be a escaping function, not '$data'
Loading history...
302
		}
303
304
		$this->message = $mail_body;
305
306
		$this->message = do_shortcode( $this->message );
307
308
		if ( $this->is_plain_text ) {
309
			$this->message = wp_specialchars_decode( strip_tags( $this->message ), ENT_QUOTES );
310
		}
311
312
		$this->message = apply_filters( 'frm_email_message', $this->message, $this->package_atts() );
313
	}
314
315
	/**
316
	 * Set the attachments for an email message
317
	 *
318
	 * @since 2.03.04
319
	 */
320
	private function set_attachments() {
321
		$args = array(
322
			'entry'     => $this->entry,
323
			'email_key' => $this->email_key,
324
		);
325
326
		$this->attachments = apply_filters( 'frm_notification_attachment', array(), $this->form, $args );
327
	}
328
329
	/**
330
	 * Check if an email should send
331
	 *
332
	 * @since 2.03.04
333
	 *
334
	 * @return bool|mixed|void
335
	 */
336
	public function should_send() {
337
		if ( ! $this->has_recipients() ) {
338
			$send = false;
339
		} else {
340
341
			/**
342
			 * Stop an email based on the message, subject, recipient,
343
			 * or any information included in the email header
344
			 *
345
			 * @since 2.2.8
346
			 */
347
			$send = apply_filters( 'frm_send_email', true, array(
348
				'message'   => $this->message,
349
				'subject'   => $this->subject,
350
				'recipient' => $this->to,
351
				'header'    => $this->package_header(),
352
			) );
353
		}
354
355
		return $send;
356
	}
357
358
	/**
359
	 * Check if an email has any recipients
360
	 *
361
	 * @since 2.03.04
362
	 *
363
	 * @return bool
364
	 */
365
	private function has_recipients() {
366
		if ( empty( $this->to ) && empty( $this->cc ) && empty( $this->bcc ) ) {
367
			return false;
368
		} else {
369
			return true;
370
		}
371
	}
372
373
	/**
374
	 * Send an email
375
	 *
376
	 * @since 2.03.04
377
	 *
378
	 * @return bool
379
	 */
380
	public function send() {
381
		$this->remove_buddypress_filters();
382
		$this->add_mandrill_filter();
383
384
		$sent = false;
385
		if ( count( $this->to ) > 1 && $this->is_single_recipient ) {
386
			foreach ( $this->to as $recipient ) {
387
				$sent = $this->send_single( $recipient );
388
			}
389
		} else {
390
			$sent = $this->send_single( $this->to );
391
		}
392
393
		$this->remove_mandrill_filter();
394
395
		return $sent;
396
	}
397
398
	/**
399
	 * Send a single email
400
	 *
401
	 * @since 2.03.04
402
	 *
403
	 * @param array|string $recipient
404
	 *
405
	 * @return bool
406
	 */
407
	private function send_single( $recipient ) {
408
		$header = apply_filters( 'frm_email_header', $this->package_header(), array(
409
			'to_email' => $recipient,
410
			'subject'  => $this->subject,
411
		) );
412
413
		$subject = $this->encode_subject( $this->subject );
414
415
		$sent = wp_mail( $recipient, $subject, $this->message, $header, $this->attachments );
416
417
		if ( ! $sent ) {
418
			$header    = 'From: ' . $this->from . "\r\n";
419
			$recipient = implode( ',', (array) $recipient );
420
			$sent      = mail( $recipient, $subject, $this->message, $header );
421
		}
422
423
		do_action( 'frm_notification', $recipient, $subject, $this->message );
424
425
		return $sent;
426
	}
427
428
	/**
429
	 * Package the email header
430
	 *
431
	 * @since 2.03.04
432
	 *
433
	 * @return array
434
	 */
435
	private function package_header() {
436
		$header   = array();
437
438
		if ( ! empty( $this->cc ) ) {
439
			$header[] = 'CC: ' . implode( ',', $this->cc );
440
		}
441
442
		if ( ! empty( $this->bcc ) ) {
443
			$header[] = 'BCC: ' . implode( ',', $this->bcc );
444
		}
445
446
		$header[] = 'From: ' . $this->from;
447
		$header[] = 'Reply-To: ' . $this->reply_to;
448
		$header[] = 'Content-Type: ' . $this->content_type . '; charset="' . esc_attr( $this->charset ) . '"';
449
450
		return $header;
451
	}
452
453
	/**
454
	 * Get the userID field ID and key for email settings
455
	 *
456
	 * @since 2.03.04
457
	 *
458
	 * @param $form_id
459
	 *
460
	 * @return array
461
	 */
462
	private function get_user_id_args( $form_id ) {
463
		$user_id_args = array(
464
			'field_id'  => '',
465
			'field_key' => '',
466
		);
467
468
		$user_id_args['field_id'] = FrmEmailHelper::get_user_id_field_for_form( $form_id );
469
		if ( $user_id_args['field_id'] ) {
470
			$user_id_args['field_key'] = FrmField::get_key_by_id( $user_id_args['field_id'] );
471
		}
472
473
		return $user_id_args;
474
	}
475
476
	/**
477
	 * Prepare the to, cc, bcc, reply_to, and from setting
478
	 *
479
	 * @since 2.03.04
480
	 *
481
	 * @param string $value
482
	 * @param array $user_id_args
483
	 *
484
	 * @return string
485
	 */
486
	private function prepare_email_setting( $value, $user_id_args ) {
487
		if ( strpos( $value, '[' . $user_id_args['field_id'] . ']' ) !== false ) {
488
			$value = str_replace( '[' . $user_id_args['field_id'] . ']', '[' . $user_id_args['field_id'] . ' show="user_email"]', $value );
489
		} else if ( strpos( $value, '[' . $user_id_args['field_key'] . ']' ) !== false ) {
490
			$value = str_replace( '[' . $user_id_args['field_key'] . ']', '[' . $user_id_args['field_key'] . ' show="user_email"]', $value );
491
		}
492
493
		$value = FrmFieldsHelper::basic_replace_shortcodes( $value, $this->form, $this->entry );
494
495
		// Remove brackets and add a space in case there isn't one
496
		$value = str_replace( '<', ' ', $value );
497
		$value = str_replace( array( '"', '>' ), '', $value );
498
499
		return $value;
500
	}
501
502
	/**
503
	 * Extract the emails from cc and bcc. Allow separation by , or ;.
504
	 * Trim the emails here as well
505
	 *
506
	 * @since 2.03.04
507
	 *
508
	 * @param string $emails
509
	 * @return array|string $emails
510
	 */
511
	private function explode_emails( $emails ) {
512
		$emails = ( ! empty( $emails ) ? preg_split( '/(,|;)/', $emails ) : '' );
513
		if ( is_array( $emails ) ) {
514
			$emails = array_map( 'trim', $emails );
515
		} else {
516
			$emails = trim( $emails );
517
		}
518
519
		return $emails;
520
	}
521
522
	/**
523
	 * Format the recipients( to, cc, bcc)
524
	 *
525
	 * @param array $recipients
526
	 *
527
	 * @return array
528
	 */
529
	private function format_recipients( $recipients ) {
530
		if ( empty( $recipients ) ) {
531
			return $recipients;
532
		}
533
534
		foreach ( $recipients as $key => $val ) {
535
			$val = trim( $val );
536
537
			if ( is_email( $val ) ) {
538
				// If a plain email is used, no formatting is needed
539
				continue;
540
			} else {
541
				$parts = explode( ' ', $val );
542
				$email = end( $parts );
543
544
				if ( is_email( $email ) ) {
545
					// If user enters a name and email
546
					$name = trim( str_replace( $email, '', $val ) );
547
				} else {
548
					// If user enters a name without an email
549
					unset( $recipients[ $key ] );
550
					continue;
551
				}
552
			}
553
554
			$recipients[ $key ] = $name . ' <' . $email . '>';
555
		}
556
557
		return $recipients;
558
	}
559
560
	/**
561
	 * Format the From header
562
	 *
563
	 * @param string $from
564
	 *
565
	 * @return string
566
	 */
567
	private function format_from( $from ) {
568
		$from = trim( $from );
569
570
		if ( is_email( $from ) ) {
571
			// If a plain email is used, add the site name so "WordPress" doesn't get added
572
			$from_name  = wp_specialchars_decode( FrmAppHelper::site_name(), ENT_QUOTES );
573
			$from_email = $from;
574
		} else {
575
			list( $from_name, $from_email ) = $this->get_name_and_email_for_sender( $from );
576
		}
577
578
		// if sending the email from a yahoo address, change it to the WordPress default
579
		if ( strpos( $from_email, '@yahoo.com' ) ) {
580
581
			// Get the site domain and get rid of www.
582
			$sitename = strtolower( FrmAppHelper::get_server_value( 'SERVER_NAME' ) );
583
			if ( substr( $sitename, 0, 4 ) == 'www.' ) {
584
				$sitename = substr( $sitename, 4 );
585
			}
586
587
			$from_email = 'wordpress@' . $sitename;
588
		}
589
590
		$from = $from_name . ' <' . $from_email . '>';
591
592
		return $from;
593
	}
594
595
	/**
596
	 * Format the Reply To property
597
	 *
598
	 * @since 2.03.04
599
	 *
600
	 * @param string $reply_to
601
	 *
602
	 * @return string
603
	 */
604
	private function format_reply_to( $reply_to ) {
605
		$reply_to = trim( $reply_to );
606
607
		if ( empty( $reply_to ) ) {
608
			return $this->from;
609
		} else if ( is_email( $reply_to ) ) {
610
			return $reply_to;
611
		} else {
612
			list( $name, $email ) = $this->get_name_and_email_for_sender( $reply_to );
613
		}
614
615
		return $name . ' <' . $email . '>';
616
	}
617
618
	/**
619
	 * Get the name and email for the From or Reply To header
620
	 *
621
	 * @since 2.03.04
622
	 *
623
	 * @param string $sender
624
	 *
625
	 * @return array
626
	 */
627
	private function get_name_and_email_for_sender( $sender ) {
628
		$parts = explode( ' ', $sender );
629
		$end   = end( $parts );
630
631
		if ( is_email( $end ) ) {
632
			$name = trim( str_replace( $end, '', $sender ) );
633
		} else {
634
			// Only a name was entered in the From or Reply To field
635
			$name = $sender;
636
			$end  = get_option( 'admin_email' );
637
		}
638
639
		return array( $name, $end );
640
	}
641
642
	/**
643
	 * Remove phone numbers from To addresses
644
	 * Send the phone numbers to the frm_send_to_not_email hook
645
	 *
646
	 * @since 2.03.04
647
	 */
648
	private function handle_phone_numbers() {
649
650
		foreach ( $this->to as $key => $recipient ) {
651
			if ( $recipient != '[admin_email]' && ! is_email( $recipient ) ) {
652
				$recipient = explode( ' ', $recipient );
653
654
				if ( is_email( end( $recipient ) ) ) {
655
					continue;
656
				}
657
658
				do_action( 'frm_send_to_not_email', array(
659
					'e'           => $recipient,
660
					'subject'     => $this->subject,
661
					'mail_body'   => $this->message,
662
					'reply_to'    => $this->reply_to,
663
					'from'        => $this->from,
664
					'plain_text'  => $this->is_plain_text,
665
					'attachments' => $this->attachments,
666
					'form'        => $this->form,
667
					'email_key'   => $key,
668
				) );
669
670
				// Remove phone number from to addresses
671
				unset( $this->to[ $key ] );
672
			}
673
		}
674
	}
675
676
	/**
677
	 * Package an array of FrmEmail properties
678
	 *
679
	 * @since 2.03.04
680
	 *
681
	 * @return array
682
	 */
683
	public function package_atts() {
684
		return array(
685
			'to_email'    => $this->to,
686
			'cc'          => $this->cc,
687
			'bcc'         => $this->bcc,
688
			'from'        => $this->from,
689
			'reply_to'    => $this->reply_to,
690
			'subject'     => $this->subject,
691
			'message'     => $this->message,
692
			'attachments' => $this->attachments,
693
			'plain_text'  => $this->is_plain_text,
694
		);
695
	}
696
697
	/**
698
	 * Remove the Buddypress email filters
699
	 *
700
	 * @since 2.03.04
701
	 */
702
	private function remove_buddypress_filters() {
703
		remove_filter( 'wp_mail_from', 'bp_core_email_from_address_filter' );
704
		remove_filter( 'wp_mail_from_name', 'bp_core_email_from_name_filter' );
705
	}
706
707
	/**
708
	 * Add Mandrill line break filter
709
	 * Remove line breaks in HTML emails to prevent conflicts with Mandrill
710
	 *
711
	 * @since 2.03.04
712
	 */
713
	private function add_mandrill_filter() {
714
		if ( ! $this->is_plain_text ) {
715
			add_filter( 'mandrill_nl2br', 'FrmEmailHelper::remove_mandrill_br' );
716
		}
717
	}
718
719
	/**
720
	 * Remove Mandrill line break filter
721
	 *
722
	 * @since 2.03.04
723
	 */
724
	private function remove_mandrill_filter() {
725
		remove_filter( 'mandrill_nl2br', 'FrmEmailHelper::remove_mandrill_br' );
726
	}
727
728
	/**
729
	 * Encode the email subject
730
	 *
731
	 * @param string $subject
732
	 *
733
	 * @return string
734
	 */
735
	private function encode_subject( $subject ) {
736
		if ( apply_filters( 'frm_encode_subject', 1, $subject ) ) {
737
			$subject = '=?' . $this->charset . '?B?' . base64_encode( $subject ) . '?=';
738
		}
739
740
		return $subject;
741
	}
742
743
}