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

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