PluginProbe
Formidable Forms – WordPress Form Builder for Contact Forms, Calculators, Quizzes & More / 5.5.1
Formidable Forms – WordPress Form Builder for Contact Forms, Calculators, Quizzes & More v5.5.1
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.5.1, at classes/models/FrmEmail.php

872 lines 19.1 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 * @return void
233 */
234 private function set_from( $user_id_args ) {
235 if ( empty( $this->settings['from'] ) ) {
236 $from = get_option( 'admin_email' );
237 } else {
238 $from = $this->prepare_email_setting( $this->settings['from'], $user_id_args );
239 }
240
241 $this->from = $this->format_from( $from );
242 }
243
244 /**
245 * Set the Reply To addresses
246 *
247 * @since 2.03.04
248 *
249 * @param array $user_id_args
250 */
251 private function set_reply_to( $user_id_args ) {
252 $this->reply_to = trim( $this->settings['reply_to'] );
253
254 if ( $this->reply_to ) {
255 $this->reply_to = $this->prepare_email_setting( $this->settings['reply_to'], $user_id_args );
256 }
257
258 if ( ! $this->reply_to ) {
259 $this->reply_to = $this->get_email_from_name( $this->from );
260 }
261
262 $this->reply_to = $this->format_reply_to( $this->reply_to );
263 }
264
265 /**
266 * Set the is_plain_text property
267 * This should be set before the message
268 *
269 * @since 2.03.04
270 */
271 private function set_is_plain_text() {
272 if ( $this->settings['plain_text'] ) {
273 $this->is_plain_text = true;
274 }
275 }
276
277 /**
278 * Set the include_user_info property
279 * This should be set before the message
280 *
281 * @since 2.03.04
282 */
283 private function set_include_user_info() {
284 if ( isset( $this->settings['inc_user_info'] ) ) {
285 $this->include_user_info = $this->settings['inc_user_info'];
286 }
287 }
288
289 /**
290 * Set the is_single_recipient property
291 *
292 * @since 2.03.04
293 *
294 * @param $action
295 */
296 private function set_is_single_recipient( $action ) {
297 $args = array(
298 'form' => $this->form,
299 'entry' => $this->entry,
300 'action' => $action,
301 );
302
303 /**
304 * Send a separate email for email address in the "to" section
305 *
306 * @since 2.2.13
307 */
308 $this->is_single_recipient = apply_filters( 'frm_send_separate_emails', false, $args );
309 }
310
311 /**
312 * Set the charset
313 *
314 * @since 2.03.04
315 */
316 private function set_charset() {
317 $this->charset = get_option( 'blog_charset' );
318 }
319
320 /**
321 * Set the content type
322 *
323 * @since 2.03.04
324 */
325 private function set_content_type() {
326 if ( $this->is_plain_text ) {
327 $this->content_type = 'text/plain';
328 }
329 }
330
331 /**
332 * Set the subject
333 *
334 * @since 2.03.04
335 *
336 * @return void
337 */
338 private function set_subject() {
339 if ( empty( $this->settings['email_subject'] ) ) {
340 /* translators: %1$s: Form name, %2$s: Site name */
341 $this->subject = sprintf( __( '%1$s Form submitted on %2$s', 'formidable' ), $this->form->name, '[sitename]' );
342 } else {
343 $this->subject = $this->settings['email_subject'];
344 }
345
346 // This also replaces [sitename] shortcode in default.
347 $this->subject = FrmFieldsHelper::basic_replace_shortcodes( $this->subject, $this->form, $this->entry );
348
349 $args = array(
350 'form' => $this->form,
351 'entry' => $this->entry,
352 'email_key' => $this->email_key,
353 );
354 $this->subject = apply_filters( 'frm_email_subject', $this->subject, $args );
355 $this->subject = wp_specialchars_decode( strip_tags( stripslashes( $this->subject ) ), ENT_QUOTES );
356 }
357
358 /**
359 * Set the email message
360 *
361 * @since 2.03.04
362 */
363 private function set_message() {
364 $this->message = FrmFieldsHelper::basic_replace_shortcodes( $this->settings['email_message'], $this->form, $this->entry );
365
366 $prev_mail_body = $this->message;
367 $pass_entry = clone $this->entry; // make a copy to prevent changes by reference
368 $mail_body = FrmEntriesHelper::replace_default_message(
369 $prev_mail_body,
370 array(
371 'id' => $this->entry->id,
372 'entry' => $pass_entry,
373 'plain_text' => $this->is_plain_text,
374 'user_info' => $this->include_user_info,
375 )
376 );
377
378 // Add the user info if it isn't already included
379 if ( $this->include_user_info && $prev_mail_body === $mail_body ) {
380 $data = $this->entry->description;
381 $mail_body .= "\r\n\r\n" . __( 'User Information', 'formidable' ) . "\r\n";
382 $this->maybe_add_ip( $mail_body );
383 $mail_body .= __( 'User-Agent (Browser/OS)', 'formidable' ) . ': ' . FrmEntriesHelper::get_browser( $data['browser'] ) . "\r\n";
384 $mail_body .= __( 'Referrer', 'formidable' ) . ': ' . $data['referrer'] . "\r\n";
385 }
386
387 $this->message = $mail_body;
388
389 $this->message = do_shortcode( $this->message );
390
391 if ( $this->is_plain_text ) {
392 $this->message = wp_specialchars_decode( strip_tags( $this->message ), ENT_QUOTES );
393 }
394
395 $this->message = apply_filters( 'frm_email_message', $this->message, $this->package_atts() );
396 }
397
398 private function maybe_add_ip( &$mail_body ) {
399 if ( ! empty( $this->entry->ip ) ) {
400 $mail_body .= __( 'IP Address', 'formidable' ) . ': ' . $this->entry->ip . "\r\n";
401 }
402 }
403
404 /**
405 * Set the attachments for an email message
406 *
407 * @since 2.03.04
408 * @since 5.0.16 added new action_id key to $args.
409 *
410 * @return void
411 */
412 private function set_attachments() {
413 $args = array(
414 'entry' => $this->entry,
415 'email_key' => $this->email_key,
416 'settings' => $this->settings,
417 'action_id' => $this->action_id,
418 );
419
420 $this->attachments = apply_filters( 'frm_notification_attachment', array(), $this->form, $args );
421 }
422
423 /**
424 * Check if an email should send
425 *
426 * @since 2.03.04
427 *
428 * @return bool|mixed|void
429 */
430 public function should_send() {
431 if ( ! $this->has_recipients() ) {
432 $send = false;
433 } else {
434
435 $filter_args = array(
436 'message' => $this->message,
437 'subject' => $this->subject,
438 'recipient' => $this->to,
439 'header' => $this->package_header(),
440 );
441
442 /**
443 * Stop an email based on the message, subject, recipient,
444 * or any information included in the email header
445 *
446 * @since 2.2.8
447 */
448 $send = apply_filters( 'frm_send_email', true, $filter_args );
449 }
450
451 return $send;
452 }
453
454 /**
455 * Check if an email has any recipients
456 *
457 * @since 2.03.04
458 *
459 * @return bool
460 */
461 private function has_recipients() {
462 if ( empty( $this->to ) && empty( $this->cc ) && empty( $this->bcc ) ) {
463 return false;
464 } else {
465 return true;
466 }
467 }
468
469 /**
470 * Send an email
471 *
472 * @since 2.03.04
473 *
474 * @return bool
475 */
476 public function send() {
477 $this->remove_buddypress_filters();
478 $this->add_mandrill_filter();
479
480 $sent = false;
481 if ( count( $this->to ) > 1 && $this->is_single_recipient ) {
482 foreach ( $this->to as $recipient ) {
483 $sent = $this->send_single( $recipient );
484 }
485 } else {
486 $sent = $this->send_single( $this->to );
487 }
488
489 $this->remove_mandrill_filter();
490
491 return $sent;
492 }
493
494 /**
495 * Send a single email
496 *
497 * @since 2.03.04
498 *
499 * @param array|string $recipient
500 *
501 * @return bool
502 */
503 private function send_single( $recipient ) {
504 $header = apply_filters(
505 'frm_email_header',
506 $this->package_header(),
507 array(
508 'to_email' => $recipient,
509 'subject' => $this->subject,
510 )
511 );
512
513 $subject = $this->encode_subject( $this->subject );
514
515 $sent = wp_mail( $recipient, $subject, $this->message, $header, $this->attachments );
516
517 if ( ! $sent ) {
518 if ( is_array( $header ) ) {
519 $header = implode( "\r\n", $header );
520 }
521 $recipient = implode( ',', (array) $recipient );
522 $sent = mail( $recipient, $subject, $this->message, $header );
523 }
524
525 do_action( 'frm_notification', $recipient, $subject, $this->message );
526
527 return $sent;
528 }
529
530 /**
531 * Package the email header
532 *
533 * @since 2.03.04
534 *
535 * @return array
536 */
537 private function package_header() {
538 $header = array();
539
540 if ( ! empty( $this->cc ) ) {
541 $header[] = 'CC: ' . implode( ',', $this->cc );
542 }
543
544 if ( ! empty( $this->bcc ) ) {
545 $header[] = 'BCC: ' . implode( ',', $this->bcc );
546 }
547
548 $header[] = 'From: ' . $this->from;
549 $header[] = 'Reply-To: ' . $this->reply_to;
550 $header[] = 'Content-Type: ' . $this->content_type . '; charset="' . esc_attr( $this->charset ) . '"';
551
552 return $header;
553 }
554
555 /**
556 * Get the userID field ID and key for email settings
557 *
558 * @since 2.03.04
559 *
560 * @param $form_id
561 *
562 * @return array
563 */
564 private function get_user_id_args( $form_id ) {
565 $user_id_args = array(
566 'field_id' => '',
567 'field_key' => '',
568 );
569
570 $user_id_args['field_id'] = FrmEmailHelper::get_user_id_field_for_form( $form_id );
571 if ( $user_id_args['field_id'] ) {
572 $user_id_args['field_key'] = FrmField::get_key_by_id( $user_id_args['field_id'] );
573 }
574
575 return $user_id_args;
576 }
577
578 /**
579 * Prepare the to, cc, bcc, reply_to, and from setting
580 *
581 * @since 2.03.04
582 *
583 * @param string $value
584 * @param array $user_id_args
585 *
586 * @return string
587 */
588 private function prepare_email_setting( $value, $user_id_args ) {
589 if ( strpos( $value, '[' . $user_id_args['field_id'] . ']' ) !== false ) {
590 $value = str_replace( '[' . $user_id_args['field_id'] . ']', '[' . $user_id_args['field_id'] . ' show="user_email"]', $value );
591 } elseif ( strpos( $value, '[' . $user_id_args['field_key'] . ']' ) !== false ) {
592 $value = str_replace( '[' . $user_id_args['field_key'] . ']', '[' . $user_id_args['field_key'] . ' show="user_email"]', $value );
593 }
594
595 $value = FrmFieldsHelper::basic_replace_shortcodes( $value, $this->form, $this->entry );
596
597 // Remove brackets and add a space in case there isn't one
598 $value = str_replace( '<', ' ', $value );
599 $value = str_replace( array( '"', '>' ), '', $value );
600
601 return $value;
602 }
603
604 /**
605 * Extract the emails from cc and bcc. Allow separation by , or ;.
606 * Trim the emails here as well
607 *
608 * @since 2.03.04
609 *
610 * @param string $emails
611 *
612 * @return array|string $emails
613 */
614 private function explode_emails( $emails ) {
615 $emails = ( ! empty( $emails ) ? preg_split( '/(,|;)/', $emails ) : '' );
616 if ( is_array( $emails ) ) {
617 $emails = array_map( 'trim', $emails );
618 } else {
619 $emails = trim( $emails );
620 }
621
622 return $emails;
623 }
624
625 /**
626 * Format the recipients( to, cc, bcc)
627 *
628 * @param array $recipients
629 *
630 * @return array
631 */
632 private function format_recipients( $recipients ) {
633 if ( empty( $recipients ) ) {
634 return $recipients;
635 }
636
637 foreach ( $recipients as $key => $val ) {
638 $val = trim( $val );
639
640 if ( is_email( $val ) ) {
641 // If a plain email is used, no formatting is needed
642 continue;
643 } else {
644 $parts = explode( ' ', $val );
645 $email = end( $parts );
646
647 if ( is_email( $email ) ) {
648 // If user enters a name and email
649 $name = trim( str_replace( $email, '', $val ) );
650 } else {
651 // If user enters a name without an email
652 unset( $recipients[ $key ] );
653 continue;
654 }
655 }
656
657 $recipients[ $key ] = $this->format_from_email( $name, $email );
658 }
659
660 return $recipients;
661 }
662
663 /**
664 * Format the From header
665 *
666 * @param string $from
667 *
668 * @return string
669 */
670 private function format_from( $from ) {
671 $from = trim( $from );
672
673 if ( is_email( $from ) ) {
674 // If a plain email is used, add the site name so "WordPress" doesn't get added
675 $from_name = wp_specialchars_decode( FrmAppHelper::site_name(), ENT_QUOTES );
676 $from_email = $from;
677 } else {
678 list( $from_name, $from_email ) = $this->get_name_and_email_for_sender( $from );
679 }
680
681 // if sending the email from a yahoo address, change it to the WordPress default
682 if ( strpos( $from_email, '@yahoo.com' ) ) {
683
684 // Get the site domain and get rid of www.
685 $sitename = strtolower( FrmAppHelper::get_server_value( 'SERVER_NAME' ) );
686 if ( substr( $sitename, 0, 4 ) === 'www.' ) {
687 $sitename = substr( $sitename, 4 );
688 }
689
690 $from_email = 'wordpress@' . $sitename;
691 }
692
693 return $this->format_from_email( $from_name, $from_email );
694 }
695
696 /**
697 * Format the Reply To property
698 *
699 * @since 2.03.04
700 *
701 * @param string $reply_to
702 *
703 * @return string
704 */
705 private function format_reply_to( $reply_to ) {
706 $reply_to = trim( $reply_to );
707
708 if ( ! is_email( $reply_to ) ) {
709 list( $name, $email ) = $this->get_name_and_email_for_sender( $reply_to );
710 $reply_to = $this->format_from_email( $name, $email );
711 }
712
713 return $reply_to;
714 }
715
716 /**
717 * Get only the email if the name and email have been combined
718 *
719 * @since 3.0.06
720 */
721 private function get_email_from_name( $name ) {
722 $email = trim( trim( $name, '>' ), '<' );
723 if ( strpos( $email, '<' ) !== false ) {
724 $parts = explode( '<', $email );
725 $email = trim( $parts[1], '>' );
726 }
727
728 return $email;
729 }
730
731 /**
732 * Get the name and email for the From or Reply To header
733 *
734 * @since 2.03.04
735 *
736 * @param string $sender
737 *
738 * @return array
739 */
740 private function get_name_and_email_for_sender( $sender ) {
741 $parts = explode( ' ', $sender );
742 $end = end( $parts );
743
744 if ( is_email( $end ) ) {
745 $name = trim( str_replace( $end, '', $sender ) );
746 } else {
747 // Only a name was entered in the From or Reply To field
748 $name = $sender;
749 $end = get_option( 'admin_email' );
750 }
751
752 return array( $name, $end );
753 }
754
755 /**
756 * @since 3.0.06
757 */
758 private function format_from_email( $name, $email ) {
759 if ( '' !== $name ) {
760 $email = $name . ' <' . $email . '>';
761 }
762
763 return $email;
764 }
765
766 /**
767 * Remove phone numbers from To addresses
768 * Send the phone numbers to the frm_send_to_not_email hook
769 *
770 * @since 2.03.04
771 */
772 private function handle_phone_numbers() {
773
774 foreach ( $this->to as $key => $recipient ) {
775 if ( '[admin_email]' !== $recipient && ! is_email( $recipient ) ) {
776 $recipient = explode( ' ', $recipient );
777
778 if ( is_email( end( $recipient ) ) ) {
779 continue;
780 }
781
782 do_action(
783 'frm_send_to_not_email',
784 array(
785 'e' => $recipient,
786 'subject' => $this->subject,
787 'mail_body' => $this->message,
788 'reply_to' => $this->reply_to,
789 'from' => $this->from,
790 'plain_text' => $this->is_plain_text,
791 'attachments' => $this->attachments,
792 'form' => $this->form,
793 'email_key' => $key,
794 )
795 );
796
797 // Remove phone number from to addresses
798 unset( $this->to[ $key ] );
799 }
800 }
801 }
802
803 /**
804 * Package an array of FrmEmail properties
805 *
806 * @since 2.03.04
807 *
808 * @return array
809 */
810 public function package_atts() {
811 return array(
812 'to_email' => $this->to,
813 'cc' => $this->cc,
814 'bcc' => $this->bcc,
815 'from' => $this->from,
816 'reply_to' => $this->reply_to,
817 'subject' => $this->subject,
818 'message' => $this->message,
819 'attachments' => $this->attachments,
820 'plain_text' => $this->is_plain_text,
821 'form' => $this->form,
822 'entry' => $this->entry,
823 );
824 }
825
826 /**
827 * Remove the Buddypress email filters
828 *
829 * @since 2.03.04
830 */
831 private function remove_buddypress_filters() {
832 remove_filter( 'wp_mail_from', 'bp_core_email_from_address_filter' );
833 remove_filter( 'wp_mail_from_name', 'bp_core_email_from_name_filter' );
834 }
835
836 /**
837 * Add Mandrill line break filter
838 * Remove line breaks in HTML emails to prevent conflicts with Mandrill
839 *
840 * @since 2.03.04
841 */
842 private function add_mandrill_filter() {
843 if ( ! $this->is_plain_text ) {
844 add_filter( 'mandrill_nl2br', 'FrmEmailHelper::remove_mandrill_br' );
845 }
846 }
847
848 /**
849 * Remove Mandrill line break filter
850 *
851 * @since 2.03.04
852 */
853 private function remove_mandrill_filter() {
854 remove_filter( 'mandrill_nl2br', 'FrmEmailHelper::remove_mandrill_br' );
855 }
856
857 /**
858 * Encode the email subject
859 *
860 * @param string $subject
861 *
862 * @return string
863 */
864 private function encode_subject( $subject ) {
865 if ( apply_filters( 'frm_encode_subject', false, $subject ) ) {
866 $subject = '=?' . $this->charset . '?B?' . base64_encode( $subject ) . '?=';
867 }
868
869 return $subject;
870 }
871 }
872