11
11
use Illuminate \Notifications \Notification ;
12
12
use Illuminate \Support \Str ;
13
13
use Vinkla \Hashids \Facades \Hashids ;
14
+ use Symfony \Component \Mime \Email ;
14
15
15
16
class FormEmailNotification extends Notification implements ShouldQueue
16
17
{
17
18
use Queueable;
18
19
19
20
public FormSubmitted $ event ;
20
21
public string $ mailer ;
21
- private $ formattedData ;
22
+ private array $ formattedData ;
22
23
23
24
/**
24
25
* Create a new notification instance.
@@ -29,15 +30,7 @@ public function __construct(FormSubmitted $event, private $integrationData, stri
29
30
{
30
31
$ this ->event = $ event ;
31
32
$ this ->mailer = $ mailer ;
32
-
33
- $ formatter = (new FormSubmissionFormatter ($ event ->form , $ event ->data ))
34
- ->createLinks ()
35
- ->outputStringsOnly ()
36
- ->useSignedUrlForFiles ();
37
- if ($ this ->integrationData ->include_hidden_fields_submission_data ?? false ) {
38
- $ formatter ->showHiddenFields ();
39
- }
40
- $ this ->formattedData = $ formatter ->getFieldsWithValue ();
33
+ $ this ->formattedData = $ this ->formatSubmissionData ();
41
34
}
42
35
43
36
/**
@@ -62,36 +55,52 @@ public function toMail($notifiable)
62
55
return (new MailMessage ())
63
56
->mailer ($ this ->mailer )
64
57
->replyTo ($ this ->getReplyToEmail ($ notifiable ->routes ['mail ' ]))
65
- ->from ($ this ->getFromEmail (), $ this ->integrationData -> sender_name ?? config ( ' app.name ' ))
58
+ ->from ($ this ->getFromEmail (), $ this ->getSenderName ( ))
66
59
->subject ($ this ->getSubject ())
67
- ->markdown ('mail.form.email-notification ' , [
68
- 'emailContent ' => $ this ->getEmailContent (),
69
- 'fields ' => $ this ->formattedData ,
70
- 'form ' => $ this ->event ->form ,
71
- 'integrationData ' => $ this ->integrationData ,
72
- 'noBranding ' => $ this ->event ->form ->no_branding ,
73
- 'submission_id ' => (isset ($ this ->event ->data ['submission_id ' ]) && $ this ->event ->data ['submission_id ' ]) ? Hashids::encode ($ this ->event ->data ['submission_id ' ]) : null ,
74
- ]);
60
+ ->withSymfonyMessage (function (Email $ message ) {
61
+ $ this ->addCustomHeaders ($ message );
62
+ })
63
+ ->markdown ('mail.form.email-notification ' , $ this ->getMailData ());
75
64
}
76
65
77
- private function getFromEmail ()
66
+ private function formatSubmissionData (): array
78
67
{
79
- if (config ('app.self_hosted ' )) {
80
- return config ('mail.from.address ' );
68
+ $ formatter = (new FormSubmissionFormatter ($ this ->event ->form , $ this ->event ->data ))
69
+ ->createLinks ()
70
+ ->outputStringsOnly ()
71
+ ->useSignedUrlForFiles ();
72
+
73
+ if ($ this ->integrationData ->include_hidden_fields_submission_data ?? false ) {
74
+ $ formatter ->showHiddenFields ();
81
75
}
82
76
83
- $ originalFromAddress = Str::of (config ('mail.from.address ' ))->explode ('@ ' );
77
+ return $ formatter ->getFieldsWithValue ();
78
+ }
79
+
80
+ private function getFromEmail (): string
81
+ {
82
+ if (
83
+ config ('app.self_hosted ' )
84
+ && isset ($ this ->integrationData ->sender_email )
85
+ && $ this ->validateEmail ($ this ->integrationData ->sender_email )
86
+ ) {
87
+ return $ this ->integrationData ->sender_email ;
88
+ }
84
89
85
- return $ originalFromAddress -> first () . ' + ' . time () . ' @ ' . $ originalFromAddress -> last ( );
90
+ return config ( ' mail.from.address ' );
86
91
}
87
92
88
- private function getReplyToEmail ($ default )
93
+ private function getSenderName (): string
94
+ {
95
+ return $ this ->integrationData ->sender_name ?? config ('app.name ' );
96
+ }
97
+
98
+ private function getReplyToEmail ($ default ): string
89
99
{
90
100
$ replyTo = $ this ->integrationData ->reply_to ?? null ;
91
101
92
102
if ($ replyTo ) {
93
- $ parser = new MentionParser ($ replyTo , $ this ->formattedData );
94
- $ parsedReplyTo = $ parser ->parse ();
103
+ $ parsedReplyTo = $ this ->parseReplyTo ($ replyTo );
95
104
if ($ parsedReplyTo && $ this ->validateEmail ($ parsedReplyTo )) {
96
105
return $ parsedReplyTo ;
97
106
}
@@ -100,40 +109,78 @@ private function getReplyToEmail($default)
100
109
return $ this ->getRespondentEmail () ?? $ default ;
101
110
}
102
111
103
- private function getSubject ()
112
+ private function parseReplyTo ( string $ replyTo ): ? string
104
113
{
105
- $ parser = new MentionParser ($ this -> integrationData -> subject ?? ' New form submission ' , $ this ->formattedData );
114
+ $ parser = new MentionParser ($ replyTo , $ this ->formattedData );
106
115
return $ parser ->parse ();
107
116
}
108
117
109
- private function getRespondentEmail ()
118
+ private function getSubject (): string
110
119
{
111
- // Make sure we only have one email field in the form
112
- $ emailFields = collect ($ this ->event ->form ->properties )->filter (function ($ field ) {
113
- $ hidden = $ field ['hidden ' ] ?? false ;
114
-
115
- return !$ hidden && $ field ['type ' ] == 'email ' ;
116
- });
117
- if ($ emailFields ->count () != 1 ) {
118
- return null ;
119
- }
120
+ $ defaultSubject = 'New form submission ' ;
121
+ $ parser = new MentionParser ($ this ->integrationData ->subject ?? $ defaultSubject , $ this ->formattedData );
122
+ return $ parser ->parse ();
123
+ }
120
124
121
- if (isset ($ this ->event ->data [$ emailFields ->first ()['id ' ]])) {
122
- $ email = $ this ->event ->data [$ emailFields ->first ()['id ' ]];
123
- if ($ this ->validateEmail ($ email )) {
124
- return $ email ;
125
- }
126
- }
125
+ private function addCustomHeaders (Email $ message ): void
126
+ {
127
+ $ formId = $ this ->event ->form ->id ;
128
+ $ submissionId = $ this ->event ->data ['submission_id ' ] ?? 'unknown ' ;
129
+ $ domain = Str::after (config ('app.url ' ), ':// ' );
127
130
128
- return null ;
131
+ $ uniquePart = substr (md5 ($ formId . $ submissionId ), 0 , 8 );
132
+ $ messageId = "form- {$ formId }- {$ uniquePart }@ {$ domain }" ;
133
+ $ references = "form- {$ formId }@ {$ domain }" ;
134
+
135
+ $ message ->getHeaders ()->remove ('Message-ID ' );
136
+ $ message ->getHeaders ()->addIdHeader ('Message-ID ' , $ messageId );
137
+ $ message ->getHeaders ()->addTextHeader ('References ' , $ references );
129
138
}
130
139
131
- private function getEmailContent ()
140
+ private function getMailData (): array
141
+ {
142
+ return [
143
+ 'emailContent ' => $ this ->getEmailContent (),
144
+ 'fields ' => $ this ->formattedData ,
145
+ 'form ' => $ this ->event ->form ,
146
+ 'integrationData ' => $ this ->integrationData ,
147
+ 'noBranding ' => $ this ->event ->form ->no_branding ,
148
+ 'submission_id ' => $ this ->getEncodedSubmissionId (),
149
+ ];
150
+ }
151
+
152
+ private function getEmailContent (): string
132
153
{
133
154
$ parser = new MentionParser ($ this ->integrationData ->email_content ?? '' , $ this ->formattedData );
134
155
return $ parser ->parse ();
135
156
}
136
157
158
+ private function getEncodedSubmissionId (): ?string
159
+ {
160
+ $ submissionId = $ this ->event ->data ['submission_id ' ] ?? null ;
161
+ return $ submissionId ? Hashids::encode ($ submissionId ) : null ;
162
+ }
163
+
164
+ private function getRespondentEmail (): ?string
165
+ {
166
+ $ emailFields = ['email ' , 'e-mail ' , 'mail ' ];
167
+
168
+ foreach ($ this ->formattedData as $ field => $ value ) {
169
+ if (in_array (strtolower ($ field ), $ emailFields ) && $ this ->validateEmail ($ value )) {
170
+ return $ value ;
171
+ }
172
+ }
173
+
174
+ // If no email field found, search for any field containing a valid email
175
+ foreach ($ this ->formattedData as $ value ) {
176
+ if ($ this ->validateEmail ($ value )) {
177
+ return $ value ;
178
+ }
179
+ }
180
+
181
+ return null ;
182
+ }
183
+
137
184
public static function validateEmail ($ email ): bool
138
185
{
139
186
return (bool )filter_var ($ email , FILTER_VALIDATE_EMAIL );
0 commit comments