Completed
Push — master ( 078d82...c7b9e4 )
by Stephanie
02:38
created

FrmEmail::set_charset()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 2
nc 1
nop 0
dl 0
loc 3
rs 10
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
			$this->maybe_add_ip( $mail_body );
300
			$mail_body .= __( 'User-Agent (Browser/OS)', 'formidable' ) . ': ' . FrmEntriesHelper::get_browser( $data['browser'] ) . "\r\n";
0 ignored issues
show
introduced by
Expected a sanitizing function (see Codex for 'Data Validation'), but instead saw 'FrmEntriesHelper'
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
	private function maybe_add_ip( &$mail_body ) {
316
		if ( ! empty( $this->entry->ip ) ) {
317
			$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...
318
		}
319
	}
320
321
	/**
322
	 * Set the attachments for an email message
323
	 *
324
	 * @since 2.03.04
325
	 */
326
	private function set_attachments() {
327
		$args = array(
328
			'entry'     => $this->entry,
329
			'email_key' => $this->email_key,
330
		);
331
332
		$this->attachments = apply_filters( 'frm_notification_attachment', array(), $this->form, $args );
333
	}
334
335
	/**
336
	 * Check if an email should send
337
	 *
338
	 * @since 2.03.04
339
	 *
340
	 * @return bool|mixed|void
341
	 */
342
	public function should_send() {
343
		if ( ! $this->has_recipients() ) {
344
			$send = false;
345
		} else {
346
347
			/**
348
			 * Stop an email based on the message, subject, recipient,
349
			 * or any information included in the email header
350
			 *
351
			 * @since 2.2.8
352
			 */
353
			$send = apply_filters( 'frm_send_email', true, array(
354
				'message'   => $this->message,
355
				'subject'   => $this->subject,
356
				'recipient' => $this->to,
357
				'header'    => $this->package_header(),
358
			) );
359
		}
360
361
		return $send;
362
	}
363
364
	/**
365
	 * Check if an email has any recipients
366
	 *
367
	 * @since 2.03.04
368
	 *
369
	 * @return bool
370
	 */
371
	private function has_recipients() {
372
		if ( empty( $this->to ) && empty( $this->cc ) && empty( $this->bcc ) ) {
373
			return false;
374
		} else {
375
			return true;
376
		}
377
	}
378
379
	/**
380
	 * Send an email
381
	 *
382
	 * @since 2.03.04
383
	 *
384
	 * @return bool
385
	 */
386
	public function send() {
387
		$this->remove_buddypress_filters();
388
		$this->add_mandrill_filter();
389
390
		$sent = false;
391
		if ( count( $this->to ) > 1 && $this->is_single_recipient ) {
392
			foreach ( $this->to as $recipient ) {
393
				$sent = $this->send_single( $recipient );
394
			}
395
		} else {
396
			$sent = $this->send_single( $this->to );
397
		}
398
399
		$this->remove_mandrill_filter();
400
401
		return $sent;
402
	}
403
404
	/**
405
	 * Send a single email
406
	 *
407
	 * @since 2.03.04
408
	 *
409
	 * @param array|string $recipient
410
	 *
411
	 * @return bool
412
	 */
413
	private function send_single( $recipient ) {
414
		$header = apply_filters( 'frm_email_header', $this->package_header(), array(
415
			'to_email' => $recipient,
416
			'subject'  => $this->subject,
417
		) );
418
419
		$subject = $this->encode_subject( $this->subject );
420
421
		$sent = wp_mail( $recipient, $subject, $this->message, $header, $this->attachments );
422
423
		if ( ! $sent ) {
424
			$header    = 'From: ' . $this->from . "\r\n";
425
			$recipient = implode( ',', (array) $recipient );
426
			$sent      = mail( $recipient, $subject, $this->message, $header );
427
		}
428
429
		do_action( 'frm_notification', $recipient, $subject, $this->message );
430
431
		return $sent;
432
	}
433
434
	/**
435
	 * Package the email header
436
	 *
437
	 * @since 2.03.04
438
	 *
439
	 * @return array
440
	 */
441
	private function package_header() {
442
		$header   = array();
443
444
		if ( ! empty( $this->cc ) ) {
445
			$header[] = 'CC: ' . implode( ',', $this->cc );
446
		}
447
448
		if ( ! empty( $this->bcc ) ) {
449
			$header[] = 'BCC: ' . implode( ',', $this->bcc );
450
		}
451
452
		$header[] = 'From: ' . $this->from;
453
		$header[] = 'Reply-To: ' . $this->reply_to;
454
		$header[] = 'Content-Type: ' . $this->content_type . '; charset="' . esc_attr( $this->charset ) . '"';
455
456
		return $header;
457
	}
458
459
	/**
460
	 * Get the userID field ID and key for email settings
461
	 *
462
	 * @since 2.03.04
463
	 *
464
	 * @param $form_id
465
	 *
466
	 * @return array
467
	 */
468
	private function get_user_id_args( $form_id ) {
469
		$user_id_args = array(
470
			'field_id'  => '',
471
			'field_key' => '',
472
		);
473
474
		$user_id_args['field_id'] = FrmEmailHelper::get_user_id_field_for_form( $form_id );
475
		if ( $user_id_args['field_id'] ) {
476
			$user_id_args['field_key'] = FrmField::get_key_by_id( $user_id_args['field_id'] );
477
		}
478
479
		return $user_id_args;
480
	}
481
482
	/**
483
	 * Prepare the to, cc, bcc, reply_to, and from setting
484
	 *
485
	 * @since 2.03.04
486
	 *
487
	 * @param string $value
488
	 * @param array $user_id_args
489
	 *
490
	 * @return string
491
	 */
492
	private function prepare_email_setting( $value, $user_id_args ) {
493
		if ( strpos( $value, '[' . $user_id_args['field_id'] . ']' ) !== false ) {
494
			$value = str_replace( '[' . $user_id_args['field_id'] . ']', '[' . $user_id_args['field_id'] . ' show="user_email"]', $value );
495
		} else if ( strpos( $value, '[' . $user_id_args['field_key'] . ']' ) !== false ) {
496
			$value = str_replace( '[' . $user_id_args['field_key'] . ']', '[' . $user_id_args['field_key'] . ' show="user_email"]', $value );
497
		}
498
499
		$value = FrmFieldsHelper::basic_replace_shortcodes( $value, $this->form, $this->entry );
500
501
		// Remove brackets and add a space in case there isn't one
502
		$value = str_replace( '<', ' ', $value );
503
		$value = str_replace( array( '"', '>' ), '', $value );
504
505
		return $value;
506
	}
507
508
	/**
509
	 * Extract the emails from cc and bcc. Allow separation by , or ;.
510
	 * Trim the emails here as well
511
	 *
512
	 * @since 2.03.04
513
	 *
514
	 * @param string $emails
515
	 * @return array|string $emails
516
	 */
517
	private function explode_emails( $emails ) {
518
		$emails = ( ! empty( $emails ) ? preg_split( '/(,|;)/', $emails ) : '' );
519
		if ( is_array( $emails ) ) {
520
			$emails = array_map( 'trim', $emails );
521
		} else {
522
			$emails = trim( $emails );
523
		}
524
525
		return $emails;
526
	}
527
528
	/**
529
	 * Format the recipients( to, cc, bcc)
530
	 *
531
	 * @param array $recipients
532
	 *
533
	 * @return array
534
	 */
535
	private function format_recipients( $recipients ) {
536
		if ( empty( $recipients ) ) {
537
			return $recipients;
538
		}
539
540
		foreach ( $recipients as $key => $val ) {
541
			$val = trim( $val );
542
543
			if ( is_email( $val ) ) {
544
				// If a plain email is used, no formatting is needed
545
				continue;
546
			} else {
547
				$parts = explode( ' ', $val );
548
				$email = end( $parts );
549
550
				if ( is_email( $email ) ) {
551
					// If user enters a name and email
552
					$name = trim( str_replace( $email, '', $val ) );
553
				} else {
554
					// If user enters a name without an email
555
					unset( $recipients[ $key ] );
556
					continue;
557
				}
558
			}
559
560
			$recipients[ $key ] = $name . ' <' . $email . '>';
561
		}
562
563
		return $recipients;
564
	}
565
566
	/**
567
	 * Format the From header
568
	 *
569
	 * @param string $from
570
	 *
571
	 * @return string
572
	 */
573
	private function format_from( $from ) {
574
		$from = trim( $from );
575
576
		if ( is_email( $from ) ) {
577
			// If a plain email is used, add the site name so "WordPress" doesn't get added
578
			$from_name  = wp_specialchars_decode( FrmAppHelper::site_name(), ENT_QUOTES );
579
			$from_email = $from;
580
		} else {
581
			list( $from_name, $from_email ) = $this->get_name_and_email_for_sender( $from );
582
		}
583
584
		// if sending the email from a yahoo address, change it to the WordPress default
585
		if ( strpos( $from_email, '@yahoo.com' ) ) {
586
587
			// Get the site domain and get rid of www.
588
			$sitename = strtolower( FrmAppHelper::get_server_value( 'SERVER_NAME' ) );
589
			if ( substr( $sitename, 0, 4 ) == 'www.' ) {
590
				$sitename = substr( $sitename, 4 );
591
			}
592
593
			$from_email = 'wordpress@' . $sitename;
594
		}
595
596
		$from = $from_name . ' <' . $from_email . '>';
597
598
		return $from;
599
	}
600
601
	/**
602
	 * Format the Reply To property
603
	 *
604
	 * @since 2.03.04
605
	 *
606
	 * @param string $reply_to
607
	 *
608
	 * @return string
609
	 */
610
	private function format_reply_to( $reply_to ) {
611
		$reply_to = trim( $reply_to );
612
613
		if ( empty( $reply_to ) ) {
614
			return $this->from;
615
		} else if ( is_email( $reply_to ) ) {
616
			return $reply_to;
617
		} else {
618
			list( $name, $email ) = $this->get_name_and_email_for_sender( $reply_to );
619
		}
620
621
		return $name . ' <' . $email . '>';
622
	}
623
624
	/**
625
	 * Get the name and email for the From or Reply To header
626
	 *
627
	 * @since 2.03.04
628
	 *
629
	 * @param string $sender
630
	 *
631
	 * @return array
632
	 */
633
	private function get_name_and_email_for_sender( $sender ) {
634
		$parts = explode( ' ', $sender );
635
		$end   = end( $parts );
636
637
		if ( is_email( $end ) ) {
638
			$name = trim( str_replace( $end, '', $sender ) );
639
		} else {
640
			// Only a name was entered in the From or Reply To field
641
			$name = $sender;
642
			$end  = get_option( 'admin_email' );
643
		}
644
645
		return array( $name, $end );
646
	}
647
648
	/**
649
	 * Remove phone numbers from To addresses
650
	 * Send the phone numbers to the frm_send_to_not_email hook
651
	 *
652
	 * @since 2.03.04
653
	 */
654
	private function handle_phone_numbers() {
655
656
		foreach ( $this->to as $key => $recipient ) {
657
			if ( $recipient != '[admin_email]' && ! is_email( $recipient ) ) {
658
				$recipient = explode( ' ', $recipient );
659
660
				if ( is_email( end( $recipient ) ) ) {
661
					continue;
662
				}
663
664
				do_action( 'frm_send_to_not_email', array(
665
					'e'           => $recipient,
666
					'subject'     => $this->subject,
667
					'mail_body'   => $this->message,
668
					'reply_to'    => $this->reply_to,
669
					'from'        => $this->from,
670
					'plain_text'  => $this->is_plain_text,
671
					'attachments' => $this->attachments,
672
					'form'        => $this->form,
673
					'email_key'   => $key,
674
				) );
675
676
				// Remove phone number from to addresses
677
				unset( $this->to[ $key ] );
678
			}
679
		}
680
	}
681
682
	/**
683
	 * Package an array of FrmEmail properties
684
	 *
685
	 * @since 2.03.04
686
	 *
687
	 * @return array
688
	 */
689
	public function package_atts() {
690
		return array(
691
			'to_email'    => $this->to,
692
			'cc'          => $this->cc,
693
			'bcc'         => $this->bcc,
694
			'from'        => $this->from,
695
			'reply_to'    => $this->reply_to,
696
			'subject'     => $this->subject,
697
			'message'     => $this->message,
698
			'attachments' => $this->attachments,
699
			'plain_text'  => $this->is_plain_text,
700
			'form'        => $this->form,
701
			'entry'       => $this->entry,
702
		);
703
	}
704
705
	/**
706
	 * Remove the Buddypress email filters
707
	 *
708
	 * @since 2.03.04
709
	 */
710
	private function remove_buddypress_filters() {
711
		remove_filter( 'wp_mail_from', 'bp_core_email_from_address_filter' );
712
		remove_filter( 'wp_mail_from_name', 'bp_core_email_from_name_filter' );
713
	}
714
715
	/**
716
	 * Add Mandrill line break filter
717
	 * Remove line breaks in HTML emails to prevent conflicts with Mandrill
718
	 *
719
	 * @since 2.03.04
720
	 */
721
	private function add_mandrill_filter() {
722
		if ( ! $this->is_plain_text ) {
723
			add_filter( 'mandrill_nl2br', 'FrmEmailHelper::remove_mandrill_br' );
724
		}
725
	}
726
727
	/**
728
	 * Remove Mandrill line break filter
729
	 *
730
	 * @since 2.03.04
731
	 */
732
	private function remove_mandrill_filter() {
733
		remove_filter( 'mandrill_nl2br', 'FrmEmailHelper::remove_mandrill_br' );
734
	}
735
736
	/**
737
	 * Encode the email subject
738
	 *
739
	 * @param string $subject
740
	 *
741
	 * @return string
742
	 */
743
	private function encode_subject( $subject ) {
744
		if ( apply_filters( 'frm_encode_subject', 1, $subject ) ) {
745
			$subject = '=?' . $this->charset . '?B?' . base64_encode( $subject ) . '?=';
746
		}
747
748
		return $subject;
749
	}
750
751
}