PluginProbe
Formidable Forms – WordPress Form Builder for Contact Forms, Calculators, Quizzes & More / 5.2.03
Formidable Forms – WordPress Form Builder for Contact Forms, Calculators, Quizzes & More v5.2.03
6.35 6.34 6.33.1 6.33 6.32.1 6.32 6.31 6.25 6.25.1 6.26 6.26.1 6.27 6.28 6.29 6.3 6.3.1 6.3.2 6.30 6.4 6.4.1 6.4.2 6.5 6.5.1 6.5.2 6.5.3 All 141 releases
formidable / classes / models / FrmEmail.php

FrmEmail.php in Formidable Forms – WordPress Form Builder for Contact Forms, Calculators, Quizzes & More 5.2.03, at classes/models/FrmEmail.php

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