Skip to content

Commit 08837e6

Browse files
Fix email notification reply to (#603)
1 parent 6bdce23 commit 08837e6

File tree

2 files changed

+73
-32
lines changed

2 files changed

+73
-32
lines changed

api/app/Notifications/Forms/FormEmailNotification.php

+10-32
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@ class FormEmailNotification extends Notification implements ShouldQueue
1919

2020
public FormSubmitted $event;
2121
public string $mailer;
22-
private array $formattedData;
2322

2423
/**
2524
* Create a new notification instance.
@@ -30,7 +29,6 @@ public function __construct(FormSubmitted $event, private $integrationData, stri
3029
{
3130
$this->event = $event;
3231
$this->mailer = $mailer;
33-
$this->formattedData = $this->formatSubmissionData();
3432
}
3533

3634
/**
@@ -54,7 +52,7 @@ public function toMail($notifiable)
5452
{
5553
return (new MailMessage())
5654
->mailer($this->mailer)
57-
->replyTo($this->getReplyToEmail($notifiable->routes['mail']))
55+
->replyTo($this->getReplyToEmail($this->event->form->creator->email))
5856
->from($this->getFromEmail(), $this->getSenderName())
5957
->subject($this->getSubject())
6058
->withSymfonyMessage(function (Email $message) {
@@ -63,13 +61,15 @@ public function toMail($notifiable)
6361
->markdown('mail.form.email-notification', $this->getMailData());
6462
}
6563

66-
private function formatSubmissionData(): array
64+
private function formatSubmissionData($createLinks = true): array
6765
{
6866
$formatter = (new FormSubmissionFormatter($this->event->form, $this->event->data))
69-
->createLinks()
7067
->outputStringsOnly()
7168
->useSignedUrlForFiles();
7269

70+
if ($createLinks) {
71+
$formatter->createLinks();
72+
}
7373
if ($this->integrationData->include_hidden_fields_submission_data ?? false) {
7474
$formatter->showHiddenFields();
7575
}
@@ -98,27 +98,25 @@ private function getSenderName(): string
9898
private function getReplyToEmail($default): string
9999
{
100100
$replyTo = $this->integrationData->reply_to ?? null;
101-
102101
if ($replyTo) {
103102
$parsedReplyTo = $this->parseReplyTo($replyTo);
104103
if ($parsedReplyTo && $this->validateEmail($parsedReplyTo)) {
105104
return $parsedReplyTo;
106105
}
107106
}
108-
109-
return $this->getRespondentEmail() ?? $default;
107+
return $default;
110108
}
111109

112110
private function parseReplyTo(string $replyTo): ?string
113111
{
114-
$parser = new MentionParser($replyTo, $this->formattedData);
112+
$parser = new MentionParser($replyTo, $this->formatSubmissionData(false));
115113
return $parser->parse();
116114
}
117115

118116
private function getSubject(): string
119117
{
120118
$defaultSubject = 'New form submission';
121-
$parser = new MentionParser($this->integrationData->subject ?? $defaultSubject, $this->formattedData);
119+
$parser = new MentionParser($this->integrationData->subject ?? $defaultSubject, $this->formatSubmissionData(false));
122120
return $parser->parse();
123121
}
124122

@@ -150,7 +148,7 @@ private function getMailData(): array
150148
{
151149
return [
152150
'emailContent' => $this->getEmailContent(),
153-
'fields' => $this->formattedData,
151+
'fields' => $this->formatSubmissionData(),
154152
'form' => $this->event->form,
155153
'integrationData' => $this->integrationData,
156154
'noBranding' => $this->event->form->no_branding,
@@ -160,7 +158,7 @@ private function getMailData(): array
160158

161159
private function getEmailContent(): string
162160
{
163-
$parser = new MentionParser($this->integrationData->email_content ?? '', $this->formattedData);
161+
$parser = new MentionParser($this->integrationData->email_content ?? '', $this->formatSubmissionData());
164162
return $parser->parse();
165163
}
166164

@@ -170,26 +168,6 @@ private function getEncodedSubmissionId(): ?string
170168
return $submissionId ? Hashids::encode($submissionId) : null;
171169
}
172170

173-
private function getRespondentEmail(): ?string
174-
{
175-
$emailFields = ['email', 'e-mail', 'mail'];
176-
177-
foreach ($this->formattedData as $field => $value) {
178-
if (in_array(strtolower($field), $emailFields) && $this->validateEmail($value)) {
179-
return $value;
180-
}
181-
}
182-
183-
// If no email field found, search for any field containing a valid email
184-
foreach ($this->formattedData as $value) {
185-
if ($this->validateEmail($value)) {
186-
return $value;
187-
}
188-
}
189-
190-
return null;
191-
}
192-
193171
public static function validateEmail($email): bool
194172
{
195173
return (bool)filter_var($email, FILTER_VALIDATE_EMAIL);

api/tests/Feature/Forms/EmailNotificationTest.php

+63
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
$notifiable->route('mail', 'test@test.com');
2727
$renderedMail = $mailable->toMail($notifiable);
2828
expect($renderedMail->subject)->toBe('New form submission');
29+
expect($renderedMail->replyTo[0][0])->toBe('reply@example.com');
2930
expect(trim($renderedMail->render()))->toContain('Test body');
3031
});
3132

@@ -163,3 +164,65 @@ function (FormEmailNotification $notification, $channels, $notifiable) {
163164
expect($renderedMail->subject)->toBe('Custom Subject');
164165
expect(trim($renderedMail->render()))->toContain('Custom content');
165166
});
167+
168+
it('send email with mention as reply to', function () {
169+
$user = $this->actingAsUser();
170+
$workspace = $this->createUserWorkspace($user);
171+
$form = $this->createForm($user, $workspace);
172+
173+
$emailProperty = collect($form->properties)->first(function ($property) {
174+
return $property['type'] == 'email';
175+
});
176+
177+
$integrationData = $this->createFormIntegration('email', $form->id, [
178+
'send_to' => 'test@test.com',
179+
'sender_name' => 'OpnForm',
180+
'subject' => 'New form submission',
181+
'email_content' => 'Hello there 👋 <br>Test body',
182+
'include_submission_data' => true,
183+
'include_hidden_fields_submission_data' => false,
184+
'reply_to' => '<span mention-field-id="' . $emailProperty['id'] . '" mention-field-name="' . $emailProperty['name'] . '" mention-fallback="" contenteditable="false" mention="true">' . $emailProperty['name'] . '</span>'
185+
]);
186+
187+
$formData = [
188+
$emailProperty['id'] => 'reply@example.com',
189+
];
190+
191+
$event = new \App\Events\Forms\FormSubmitted($form, $formData);
192+
$mailable = new FormEmailNotification($event, $integrationData, 'mail');
193+
$notifiable = new AnonymousNotifiable();
194+
$notifiable->route('mail', 'test@test.com');
195+
$renderedMail = $mailable->toMail($notifiable);
196+
expect($renderedMail->replyTo[0][0])->toBe('reply@example.com');
197+
});
198+
199+
it('send email with empty reply to', function () {
200+
$user = $this->actingAsUser();
201+
$workspace = $this->createUserWorkspace($user);
202+
$form = $this->createForm($user, $workspace);
203+
204+
$emailProperty = collect($form->properties)->first(function ($property) {
205+
return $property['type'] == 'email';
206+
});
207+
208+
$integrationData = $this->createFormIntegration('email', $form->id, [
209+
'send_to' => 'test@test.com',
210+
'sender_name' => 'OpnForm',
211+
'subject' => 'New form submission',
212+
'email_content' => 'Hello there 👋 <br>Test body',
213+
'include_submission_data' => true,
214+
'include_hidden_fields_submission_data' => false,
215+
'reply_to' => null,
216+
]);
217+
218+
$formData = [
219+
$emailProperty['id'] => 'reply@example.com',
220+
];
221+
222+
$event = new \App\Events\Forms\FormSubmitted($form, $formData);
223+
$mailable = new FormEmailNotification($event, $integrationData, 'mail');
224+
$notifiable = new AnonymousNotifiable();
225+
$notifiable->route('mail', 'test@test.com');
226+
$renderedMail = $mailable->toMail($notifiable);
227+
expect($renderedMail->replyTo[0][0])->toBe($form->creator->email);
228+
});

0 commit comments

Comments
 (0)