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

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