Apply Mentions everywhere (#595)
* variables and mentions * fix lint * add missing changes * fix tests * update quilly, fix bugs * fix lint * apply fixes * apply fixes * Fix MentionParser * Apply Mentions everywhere * Fix MentionParserTest * Small refactoring * Fixing quill import issues * Polished email integration, added customer sender mail * Add missing changes * improve migration command --------- Co-authored-by: Frank <csskfaves@gmail.com> Co-authored-by: Julien Nahum <julien@nahum.net>
This commit is contained in:
188
api/app/Notifications/Forms/FormEmailNotification.php
Normal file
188
api/app/Notifications/Forms/FormEmailNotification.php
Normal file
@@ -0,0 +1,188 @@
|
||||
<?php
|
||||
|
||||
namespace App\Notifications\Forms;
|
||||
|
||||
use App\Events\Forms\FormSubmitted;
|
||||
use App\Open\MentionParser;
|
||||
use App\Service\Forms\FormSubmissionFormatter;
|
||||
use Illuminate\Bus\Queueable;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\Notifications\Messages\MailMessage;
|
||||
use Illuminate\Notifications\Notification;
|
||||
use Illuminate\Support\Str;
|
||||
use Vinkla\Hashids\Facades\Hashids;
|
||||
use Symfony\Component\Mime\Email;
|
||||
|
||||
class FormEmailNotification extends Notification implements ShouldQueue
|
||||
{
|
||||
use Queueable;
|
||||
|
||||
public FormSubmitted $event;
|
||||
public string $mailer;
|
||||
private array $formattedData;
|
||||
|
||||
/**
|
||||
* Create a new notification instance.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __construct(FormSubmitted $event, private $integrationData, string $mailer)
|
||||
{
|
||||
$this->event = $event;
|
||||
$this->mailer = $mailer;
|
||||
$this->formattedData = $this->formatSubmissionData();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the notification's delivery channels.
|
||||
*
|
||||
* @param mixed $notifiable
|
||||
* @return array
|
||||
*/
|
||||
public function via($notifiable)
|
||||
{
|
||||
return ['mail'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the mail representation of the notification.
|
||||
*
|
||||
* @param mixed $notifiable
|
||||
* @return \Illuminate\Notifications\Messages\MailMessage
|
||||
*/
|
||||
public function toMail($notifiable)
|
||||
{
|
||||
return (new MailMessage())
|
||||
->mailer($this->mailer)
|
||||
->replyTo($this->getReplyToEmail($notifiable->routes['mail']))
|
||||
->from($this->getFromEmail(), $this->getSenderName())
|
||||
->subject($this->getSubject())
|
||||
->withSymfonyMessage(function (Email $message) {
|
||||
$this->addCustomHeaders($message);
|
||||
})
|
||||
->markdown('mail.form.email-notification', $this->getMailData());
|
||||
}
|
||||
|
||||
private function formatSubmissionData(): array
|
||||
{
|
||||
$formatter = (new FormSubmissionFormatter($this->event->form, $this->event->data))
|
||||
->createLinks()
|
||||
->outputStringsOnly()
|
||||
->useSignedUrlForFiles();
|
||||
|
||||
if ($this->integrationData->include_hidden_fields_submission_data ?? false) {
|
||||
$formatter->showHiddenFields();
|
||||
}
|
||||
|
||||
return $formatter->getFieldsWithValue();
|
||||
}
|
||||
|
||||
private function getFromEmail(): string
|
||||
{
|
||||
if (
|
||||
config('app.self_hosted')
|
||||
&& isset($this->integrationData->sender_email)
|
||||
&& $this->validateEmail($this->integrationData->sender_email)
|
||||
) {
|
||||
return $this->integrationData->sender_email;
|
||||
}
|
||||
|
||||
return config('mail.from.address');
|
||||
}
|
||||
|
||||
private function getSenderName(): string
|
||||
{
|
||||
return $this->integrationData->sender_name ?? config('app.name');
|
||||
}
|
||||
|
||||
private function getReplyToEmail($default): string
|
||||
{
|
||||
$replyTo = $this->integrationData->reply_to ?? null;
|
||||
|
||||
if ($replyTo) {
|
||||
$parsedReplyTo = $this->parseReplyTo($replyTo);
|
||||
if ($parsedReplyTo && $this->validateEmail($parsedReplyTo)) {
|
||||
return $parsedReplyTo;
|
||||
}
|
||||
}
|
||||
|
||||
return $this->getRespondentEmail() ?? $default;
|
||||
}
|
||||
|
||||
private function parseReplyTo(string $replyTo): ?string
|
||||
{
|
||||
$parser = new MentionParser($replyTo, $this->formattedData);
|
||||
return $parser->parse();
|
||||
}
|
||||
|
||||
private function getSubject(): string
|
||||
{
|
||||
$defaultSubject = 'New form submission';
|
||||
$parser = new MentionParser($this->integrationData->subject ?? $defaultSubject, $this->formattedData);
|
||||
return $parser->parse();
|
||||
}
|
||||
|
||||
private function addCustomHeaders(Email $message): void
|
||||
{
|
||||
$formId = $this->event->form->id;
|
||||
$submissionId = $this->event->data['submission_id'] ?? 'unknown';
|
||||
$domain = Str::after(config('app.url'), '://');
|
||||
|
||||
$uniquePart = substr(md5($formId . $submissionId), 0, 8);
|
||||
$messageId = "form-{$formId}-{$uniquePart}@{$domain}";
|
||||
$references = "form-{$formId}@{$domain}";
|
||||
|
||||
$message->getHeaders()->remove('Message-ID');
|
||||
$message->getHeaders()->addIdHeader('Message-ID', $messageId);
|
||||
$message->getHeaders()->addTextHeader('References', $references);
|
||||
}
|
||||
|
||||
private function getMailData(): array
|
||||
{
|
||||
return [
|
||||
'emailContent' => $this->getEmailContent(),
|
||||
'fields' => $this->formattedData,
|
||||
'form' => $this->event->form,
|
||||
'integrationData' => $this->integrationData,
|
||||
'noBranding' => $this->event->form->no_branding,
|
||||
'submission_id' => $this->getEncodedSubmissionId(),
|
||||
];
|
||||
}
|
||||
|
||||
private function getEmailContent(): string
|
||||
{
|
||||
$parser = new MentionParser($this->integrationData->email_content ?? '', $this->formattedData);
|
||||
return $parser->parse();
|
||||
}
|
||||
|
||||
private function getEncodedSubmissionId(): ?string
|
||||
{
|
||||
$submissionId = $this->event->data['submission_id'] ?? null;
|
||||
return $submissionId ? Hashids::encode($submissionId) : null;
|
||||
}
|
||||
|
||||
private function getRespondentEmail(): ?string
|
||||
{
|
||||
$emailFields = ['email', 'e-mail', 'mail'];
|
||||
|
||||
foreach ($this->formattedData as $field => $value) {
|
||||
if (in_array(strtolower($field), $emailFields) && $this->validateEmail($value)) {
|
||||
return $value;
|
||||
}
|
||||
}
|
||||
|
||||
// If no email field found, search for any field containing a valid email
|
||||
foreach ($this->formattedData as $value) {
|
||||
if ($this->validateEmail($value)) {
|
||||
return $value;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public static function validateEmail($email): bool
|
||||
{
|
||||
return (bool)filter_var($email, FILTER_VALIDATE_EMAIL);
|
||||
}
|
||||
}
|
||||
@@ -1,113 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Notifications\Forms;
|
||||
|
||||
use App\Events\Forms\FormSubmitted;
|
||||
use App\Service\Forms\FormSubmissionFormatter;
|
||||
use Illuminate\Bus\Queueable;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\Notifications\Messages\MailMessage;
|
||||
use Illuminate\Notifications\Notification;
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
class FormSubmissionNotification extends Notification implements ShouldQueue
|
||||
{
|
||||
use Queueable;
|
||||
|
||||
public FormSubmitted $event;
|
||||
private $mailer;
|
||||
|
||||
/**
|
||||
* Create a new notification instance.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __construct(FormSubmitted $event, private $integrationData, string $mailer)
|
||||
{
|
||||
$this->event = $event;
|
||||
$this->mailer = $mailer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the notification's delivery channels.
|
||||
*
|
||||
* @param mixed $notifiable
|
||||
* @return array
|
||||
*/
|
||||
public function via($notifiable)
|
||||
{
|
||||
return ['mail'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the mail representation of the notification.
|
||||
*
|
||||
* @param mixed $notifiable
|
||||
* @return \Illuminate\Notifications\Messages\MailMessage
|
||||
*/
|
||||
public function toMail($notifiable)
|
||||
{
|
||||
$formatter = (new FormSubmissionFormatter($this->event->form, $this->event->data))
|
||||
->showHiddenFields()
|
||||
->createLinks()
|
||||
->outputStringsOnly()
|
||||
->useSignedUrlForFiles();
|
||||
|
||||
return (new MailMessage())
|
||||
->mailer($this->mailer)
|
||||
->replyTo($this->getReplyToEmail($notifiable->routes['mail']))
|
||||
->from($this->getFromEmail(), config('app.name'))
|
||||
->subject('New form submission for "' . $this->event->form->title . '"')
|
||||
->markdown('mail.form.submission-notification', [
|
||||
'fields' => $formatter->getFieldsWithValue(),
|
||||
'form' => $this->event->form,
|
||||
]);
|
||||
}
|
||||
|
||||
private function getFromEmail()
|
||||
{
|
||||
if (config('app.self_hosted')) {
|
||||
return config('mail.from.address');
|
||||
}
|
||||
$originalFromAddress = Str::of(config('mail.from.address'))->explode('@');
|
||||
|
||||
return $originalFromAddress->first() . '+' . time() . '@' . $originalFromAddress->last();
|
||||
}
|
||||
|
||||
private function getReplyToEmail($default)
|
||||
{
|
||||
$replyTo = $this->integrationData->notification_reply_to ?? null;
|
||||
if ($replyTo && $this->validateEmail($replyTo)) {
|
||||
return $replyTo;
|
||||
}
|
||||
|
||||
return $this->getRespondentEmail() ?? $default;
|
||||
}
|
||||
|
||||
private function getRespondentEmail()
|
||||
{
|
||||
// Make sure we only have one email field in the form
|
||||
$emailFields = collect($this->event->form->properties)->filter(function ($field) {
|
||||
$hidden = $field['hidden'] ?? false;
|
||||
|
||||
return !$hidden && $field['type'] == 'email';
|
||||
});
|
||||
if ($emailFields->count() != 1) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (isset($this->event->data[$emailFields->first()['id']])) {
|
||||
$email = $this->event->data[$emailFields->first()['id']];
|
||||
if ($this->validateEmail($email)) {
|
||||
return $email;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public static function validateEmail($email): bool
|
||||
{
|
||||
return (bool) filter_var($email, FILTER_VALIDATE_EMAIL);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user