diff --git a/app/Models/CompanyUser.php b/app/Models/CompanyUser.php index cc1cbe796800..24e4935a705a 100644 --- a/app/Models/CompanyUser.php +++ b/app/Models/CompanyUser.php @@ -43,6 +43,7 @@ class CompanyUser extends Pivot 'permissions', 'notifications', 'settings', + 'react_settings', 'is_admin', 'is_owner', 'is_locked', @@ -71,12 +72,12 @@ class CompanyUser extends Pivot public function user_pivot() { - return $this->hasOne(User::class)->withPivot('permissions', 'settings', 'is_admin', 'is_owner', 'is_locked', 'slack_webhook_url', 'migrating'); + return $this->hasOne(User::class)->withPivot('permissions', 'settings', 'react_settings', 'is_admin', 'is_owner', 'is_locked', 'slack_webhook_url', 'migrating'); } public function company_pivot() { - return $this->hasOne(Company::class)->withPivot('permissions', 'settings', 'is_admin', 'is_owner', 'is_locked', 'slack_webhook_url', 'migrating'); + return $this->hasOne(Company::class)->withPivot('permissions', 'settings', 'react_settings', 'is_admin', 'is_owner', 'is_locked', 'slack_webhook_url', 'migrating'); } public function user() diff --git a/app/Services/Email/BaseMailer.php b/app/Services/Email/BaseMailer.php index 38b41c261bed..1fd066d5a8a5 100644 --- a/app/Services/Email/BaseMailer.php +++ b/app/Services/Email/BaseMailer.php @@ -11,15 +11,19 @@ namespace App\Services\Email; -use App\Libraries\MultiDB; -use App\Models\Company; -use App\Services\Email\MailBuild; use App\Utils\Ninja; +use App\Models\Company; use Illuminate\Bus\Queueable; +use App\Services\Email\MailBuild; +use Illuminate\Support\Facades\Mail; +use Illuminate\Support\Facades\Cache; +use Illuminate\Queue\SerializesModels; +use Turbo124\Beacon\Facades\LightLogs; +use Illuminate\Contracts\Mail\Mailable; +use Illuminate\Queue\InteractsWithQueue; +use App\DataMapper\Analytics\EmailSuccess; use Illuminate\Contracts\Queue\ShouldQueue; use Illuminate\Foundation\Bus\Dispatchable; -use Illuminate\Queue\InteractsWithQueue; -use Illuminate\Queue\SerializesModels; class BaseMailer implements ShouldQueue { @@ -29,27 +33,38 @@ class BaseMailer implements ShouldQueue public int $tries = 4; - public ?string $client_postmark_secret = false; + public ?string $client_postmark_secret = null; public ?string $client_mailgun_secret = null; public ?string $client_mailgun_domain = null; - public boolean $override = false; + public bool $override = false; public $deleteWhenMissingModels = true; - public function __construct() + public Mail $mail; + + private string $mailer = 'default'; + + public function __construct(public mixed $invitation, private ?string $db, public MailObject $mail_object) { + + $this->invitation = $invitation; + + $this->company = $invitation->company; + + $this->db = $db; + + $this->mail_object = $mail_object; + + $this->override = $mail_object->override; + } - public function handle(MailBuild $builder): void - { - } - public function companyCheck() + public function companyCheck(): void { - /* Handle bad state */ if(!$this->company) $this->fail(); @@ -66,17 +81,109 @@ class BaseMailer implements ShouldQueue public function configureMailer(): self { + $this->setMailDriver(); + $this->mail = Mail::mailer($this->mailer); + return $this; } + + /** + * Sets the mail driver to use and applies any specific configuration + * the the mailable + */ + private function setMailDriver(): self + { + + switch ($this->mail_object->settings->email_sending_method) { + case 'default': + $this->mailer = config('mail.default'); + break; + // case 'gmail': + // $this->mailer = 'gmail'; + // $this->setGmailMailer(); + // return $this; + // case 'office365': + // $this->mailer = 'office365'; + // $this->setOfficeMailer(); + // return $this; + // case 'client_postmark': + // $this->mailer = 'postmark'; + // $this->setPostmarkMailer(); + // return $this; + // case 'client_mailgun': + // $this->mailer = 'mailgun'; + // $this->setMailgunMailer(); + // return $this; + + default: + break; + } + + if(Ninja::isSelfHost()) + $this->setSelfHostMultiMailer(); + + return $this; + + } + + /** + * Allows configuration of multiple mailers + * per company for use by self hosted users + */ + private function setSelfHostMultiMailer(): void + { + + if (env($this->email_service->company->id . '_MAIL_HOST')) + { + + config([ + 'mail.mailers.smtp' => [ + 'transport' => 'smtp', + 'host' => env($this->email_service->company->id . '_MAIL_HOST'), + 'port' => env($this->email_service->company->id . '_MAIL_PORT'), + 'username' => env($this->email_service->company->id . '_MAIL_USERNAME'), + 'password' => env($this->email_service->company->id . '_MAIL_PASSWORD'), + ], + ]); + + if(env($this->email_service->company->id . '_MAIL_FROM_ADDRESS')) + { + $this->email_mailable + ->from(env($this->email_service->company->id . '_MAIL_FROM_ADDRESS', env('MAIL_FROM_ADDRESS')), env($this->email_service->company->id . '_MAIL_FROM_NAME', env('MAIL_FROM_NAME'))); + } + + } + + } + + + /** + * Ensure we discard any data that is not required + * + * @return void + */ + private function cleanUpMailers(): void + { + $this->client_postmark_secret = false; + + $this->client_mailgun_secret = false; + + $this->client_mailgun_domain = false; + + //always dump the drivers to prevent reuse + app('mail.manager')->forgetMailers(); + } + + public function trySending() { try { $mailer - ->to($this->nmo->to_user->email) - ->send($this->nmo->mailable); + ->to($this->mail_object->to_user->email) + ->send($this->mail_object->mailable); /* Count the amount of emails sent across all the users accounts */ Cache::increment($this->company->account->key); @@ -154,4 +261,3 @@ class BaseMailer implements ShouldQueue } } -` \ No newline at end of file diff --git a/app/Services/Email/MailBuild.php b/app/Services/Email/MailBuild.php index 0d58130bf941..be5f68bc1ad3 100644 --- a/app/Services/Email/MailBuild.php +++ b/app/Services/Email/MailBuild.php @@ -11,53 +11,90 @@ namespace App\Services\Email; +use App\Utils\Ninja; use App\Models\Client; use App\Models\Vendor; -use App\Utils\Ninja; -use Illuminate\Contracts\Mail\Mailable; -use Illuminate\Mail\Mailable as MailMailable; +use App\Models\Account; +use App\Utils\HtmlEngine; use Illuminate\Support\Facades\App; +use App\Services\Email\MailMailable; +use Illuminate\Mail\Mailables\Address; +use Illuminate\Contracts\Mail\Mailable; +use App\DataMapper\EmailTemplateDefaults; +use League\CommonMark\CommonMarkConverter; -/** - * Class assumption is that we will be emailing an entity that has an associated Invitation - */ class MailBuild { - + /** - * The settings object for this email - * @var CompanySettings $settings + * settings + * + * @var mixed */ protected $settings; - - /** - * The HTML / Template to use for this email - * @var string $template - */ + + /** @var mixed $template */ private string $template; - - /** - * The locale to use for - * translations for this email - */ + + /** @var mixed $locale */ private string $locale; - + + /** @var mixed $client */ private ?Client $client; - + + /** @var mixed $vendor */ private ?Vendor $vendor; - - public function __construct(public MailEntity $mail_entity) - { - - } - - public function run(): Mailable + + /** + * __construct + * + * @param mixed $mail_entity + * @return void + */ + public function __construct(public MailEntity $mail_entity){} + + /** + * Builds the mailable + * + * @return self + */ + public function run(): self { //resolve settings, if client existing - use merged - else default to company $this->settings = $this->mail_entity->company->settings; - $this->resolveEntities(); + $this->mail_entity->mail_object->settings = $this->settings; + + $this->resolveEntities() + ->setLocale() + ->setFrom() + ->setTo() + ->setTemplate() + ->setSubject() + ->setBody() + ->setReplyTo() + ->setBcc() + ->setAttachments() + ->setMetaData() + ->setVariables(); + + return $this; + } + + /** + * Returns the mailable to the mailer + * + * @return Mailable + */ + public function getMailable(): Mailable + { + return new MailMailable($this->mail_entity->mail_object); //todo current depends on EmailObject } + /** + * Resolve any class entities + * + * @return self + */ private function resolveEntities(): self { @@ -65,14 +102,13 @@ class MailBuild $this->vendor = $this->mail_entity->mail_object->vendor_id ? Vendor::find($this->mail_entity->mail_object->vendor_id) : null; - $this->locale = $this->mail_entity->company->locale(); - return $this; } - /** * Sets the meta data for the Email object + * + * @return self */ private function setMetaData(): self { @@ -85,20 +121,28 @@ class MailBuild $this->mail_entity->mail_object->whitelabel = $this->mail_entity->company->account->isPaid() ? true : false; + $this->mail_entity->mail_object->company = $this->mail_entity->company; + return $this; } + /** * Sets the locale + * + * @return self */ private function setLocale(): self { - if($this->mail_entity->mail_object->client_id) - $this->locale = $this->mail_entity->mail_object->client->locale(); - elseif($this->mail_entity->mail_object->vendor) - $this->locale = $this->mail_entity->mail_object->vendor->locale(); + if($this->client){ + $this->locale = $this->client->locale(); + $this->settings = $this->client->getMergedSettings(); + $this->mail_entity->mail_object->settings = $this->settings; + } + elseif($this->vendor) + $this->locale = $this->vendor->locale(); else $this->locale = $this->mail_entity->company->locale(); @@ -112,12 +156,14 @@ class MailBuild /** * Sets the template + * + * @return self */ private function setTemplate(): self { - $this->template = $this->mail_entity->mail_object->settings->email_style; + $this->template = $this->settings->email_style; - match($this->mail_entity->mail_object->settings->email_style){ + match($this->settings->email_style){ 'light' => $this->template = 'email.template.client', 'dark' => $this->template = 'email.template.client', 'custom' => $this->template = 'email.template.custom', @@ -128,14 +174,28 @@ class MailBuild return $this; } - + + /** + * setTo + * + * @return self + */ + private function setTo(): self + { + $this->mail_entity->mail_object->to = [new Address($this->mail_entity->invitation->contact->email, $this->mail_entity->invitation->contact->present()->name())]; + + return $this; + } + /** * Sets the FROM address + * + * @return self */ private function setFrom(): self { - if(Ninja::isHosted() && $this->mail_entity->mail_object->settings->email_sending_method == 'default'){ + if(Ninja::isHosted() && $this->settings->email_sending_method == 'default'){ $this->mail_entity->mail_object->from = new Address(config('mail.from.address'), $this->mail_entity->company->owner()->name()); return $this; } @@ -148,9 +208,30 @@ class MailBuild return $this; } + + /** + * Sets the subject of the email + * + * @return self + */ + private function setSubject(): self + { - /** + if ($this->mail_entity->mail_object->subject) //where the user updates the subject from the UI + return $this; + elseif(is_string($this->mail_entity->mail_object->email_template_subject) && strlen($this->settings->{$this->mail_entity->mail_object->email_template_subject}) > 3) + $this->mail_entity->mail_object->subject = $this->settings->{$this->mail_entity->mail_object->email_template_subject}; + else + $this->mail_entity->mail_object->subject = EmailTemplateDefaults::getDefaultTemplate($this->mail_entity->mail_object->email_template_subject, $this->locale); + + return $this; + + } + + /** * Sets the body of the email + * + * @return self */ private function setBody(): self { @@ -158,47 +239,64 @@ class MailBuild if($this->mail_entity->mail_object->body){ $this->mail_entity->mail_object->body = $this->mail_entity->mail_object->body; } - elseif(strlen($this->mail_entity->mail_object->settings->{$this->mail_entity->mail_object->email_template_body}) > 3){ - $this->mail_entity->mail_object->body = $this->mail_entity->mail_object->settings->{$this->mail_entity->mail_object->email_template_body}; + elseif(is_string($this->mail_entity->mail_object->email_template_body) && strlen($this->settings->{$this->mail_entity->mail_object->email_template_body}) > 3){ + $this->mail_entity->mail_object->body = $this->settings->{$this->mail_entity->mail_object->email_template_body}; } else{ $this->mail_entity->mail_object->body = EmailTemplateDefaults::getDefaultTemplate($this->mail_entity->mail_object->email_template_body, $this->locale); } if($this->template == 'email.template.custom'){ - $this->mail_entity->mail_object->body = (str_replace('$body', $this->mail_entity->mail_object->body, $this->mail_entity->mail_object->settings->email_style_custom)); + $this->mail_entity->mail_object->body = (str_replace('$body', $this->mail_entity->mail_object->body, $this->settings->email_style_custom)); } return $this; } - /** - * Sets the subject of the email - */ - private function setSubject(): self + /** + * Sets the attachments for the email + * + * Note that we base64 encode these, as they + * sometimes may not survive serialization. + * + * We decode these in the Mailable later + * + * @return self + */ + private function setAttachments(): self { + $attachments = []; - if ($this->mail_entity->mail_object->subject) //where the user updates the subject from the UI - return $this; - elseif(strlen($this->mail_entity->mail_object->settings->{$this->mail_entity->mail_object->email_template_subject}) > 3) - $this->mail_entity->mail_object->subject = $this->mail_entity->mail_object->settings->{$this->mail_entity->mail_object->email_template_subject}; - else - $this->mail_entity->mail_object->subject = EmailTemplateDefaults::getDefaultTemplate($this->mail_entity->mail_object->email_template_subject, $this->locale); + if ($this->settings->document_email_attachment && $this->mail_entity->company->account->hasFeature(Account::FEATURE_DOCUMENTS)) { + + foreach ($this->mail_entity->company->documents as $document) { + + $attachments[] = ['file' => base64_encode($document->getFile()), 'name' => $document->name]; + + } + + } + + $this->mail_entity->mail_object->attachments = array_merge($this->mail_entity->mail_object->attachments, $attachments); return $this; } + + /** * Sets the reply to of the email + * + * @return self */ private function setReplyTo(): self { - $reply_to_email = str_contains($this->mail_entity->mail_object->settings->reply_to_email, "@") ? $this->mail_entity->mail_object->settings->reply_to_email : $this->mail_entity->company->owner()->email; + $reply_to_email = str_contains($this->settings->reply_to_email, "@") ? $this->settings->reply_to_email : $this->mail_entity->company->owner()->email; - $reply_to_name = strlen($this->mail_entity->mail_object->settings->reply_to_name) > 3 ? $this->mail_entity->mail_object->settings->reply_to_name : $this->mail_entity->company->owner()->present()->name(); + $reply_to_name = strlen($this->settings->reply_to_name) > 3 ? $this->settings->reply_to_name : $this->mail_entity->company->owner()->present()->name(); $this->mail_entity->mail_object->reply_to = array_merge($this->mail_entity->mail_object->reply_to, [new Address($reply_to_email, $reply_to_name)]); @@ -208,13 +306,22 @@ class MailBuild /** * Replaces the template placeholders * with variable values. + * + * @return self */ public function setVariables(): self { - $this->mail_entity->mail_object->body = strtr($this->mail_entity->mail_object->body, $this->mail_entity->mail_object->variables); - $this->mail_entity->mail_object->subject = strtr($this->mail_entity->mail_object->subject, $this->mail_entity->mail_object->variables); + if($this->mail_entity->mail_object->variables){ + $this->mail_entity->mail_object->subject = strtr($this->mail_entity->mail_object->subject, $this->mail_entity->mail_object->variables); + $this->mail_entity->mail_object->body = strtr($this->mail_entity->mail_object->body, $this->mail_entity->mail_object->variables); + } + + $variables = (new HtmlEngine($this->mail_entity->invitation))->makeValues(); + + $this->mail_entity->mail_object->subject = strtr($this->mail_entity->mail_object->subject, $variables); + $this->mail_entity->mail_object->body = strtr($this->mail_entity->mail_object->body, $variables); if($this->template != 'custom') $this->mail_entity->mail_object->body = $this->parseMarkdownToHtml($this->mail_entity->mail_object->body); @@ -224,18 +331,20 @@ class MailBuild /** * Sets the BCC of the email + * + * @return self */ private function setBcc(): self { $bccs = []; $bcc_array = []; - if (strlen($this->mail_entity->mail_object->settings->bcc_email) > 1) { + if (strlen($this->settings->bcc_email) > 1) { if (Ninja::isHosted() && $this->mail_entity->company->account->isPaid()) { - $bccs = array_slice(explode(',', str_replace(' ', '', $this->mail_entity->mail_object->settings->bcc_email)), 0, 2); + $bccs = array_slice(explode(',', str_replace(' ', '', $this->settings->bcc_email)), 0, 2); } elseif(Ninja::isSelfHost()) { - $bccs = (explode(',', str_replace(' ', '', $this->mail_entity->mail_object->settings->bcc_email))); + $bccs = (explode(',', str_replace(' ', '', $this->settings->bcc_email))); } } @@ -260,41 +369,18 @@ class MailBuild ]; } - /** - * Sets the attachments for the email - * - * Note that we base64 encode these, as they - * sometimes may not survive serialization. - * - * We decode these in the Mailable later - */ - private function setAttachments(): self - { - $attachments = []; - - if ($this->mail_entity->mail_object->settings->document_email_attachment && $this->mail_entity->company->account->hasFeature(Account::FEATURE_DOCUMENTS)) { - - foreach ($this->mail_entity->company->documents as $document) { - - $attachments[] = ['file' => base64_encode($document->getFile()), 'name' => $document->name]; - - } - - } - - $this->mail_entity->mail_object->attachments = array_merge($this->mail_entity->mail_object->attachments, $attachments); - - return $this; - - } /** * Sets the headers for the email + * + * @return self */ private function setHeaders(): self { if($this->mail_entity->mail_object->invitation_key) $this->mail_entity->mail_object->headers = array_merge($this->mail_entity->mail_object->headers, ['x-invitation-key' => $this->mail_entity->mail_object->invitation_key]); + elseif($this->mail_entity->invitation) + $this->mail_entity->mail_object->headers = array_merge($this->mail_entity->mail_object->headers, ['x-invitation-key' => $this->mail_entity->invitation->key]); return $this; } diff --git a/app/Services/Email/MailEntity.php b/app/Services/Email/MailEntity.php index 5994e2597cc7..4b56f70ba719 100644 --- a/app/Services/Email/MailEntity.php +++ b/app/Services/Email/MailEntity.php @@ -11,22 +11,52 @@ namespace App\Services\Email; -use App\Libraries\MultiDB; +use App\Utils\Ninja; use App\Models\Company; -use App\Services\Email\MailBuild; +use App\Libraries\MultiDB; use Illuminate\Bus\Queueable; +use Illuminate\Mail\Mailable; +use App\Services\Email\MailBuild; +use Illuminate\Support\Facades\Mail; +use Illuminate\Support\Facades\Cache; +use Illuminate\Queue\SerializesModels; +use Turbo124\Beacon\Facades\LightLogs; +use Illuminate\Queue\InteractsWithQueue; +use App\DataMapper\Analytics\EmailSuccess; use Illuminate\Contracts\Queue\ShouldQueue; use Illuminate\Foundation\Bus\Dispatchable; -use Illuminate\Queue\InteractsWithQueue; -use Illuminate\Queue\SerializesModels; -class MailEntity extends BaseMailer implements ShouldQueue +class MailEntity implements ShouldQueue { use Dispatchable, InteractsWithQueue, Queueable, SerializesModels; public Company $company; - public function __construct(protected ?mixed $invitation, private ?string $db, public MailObject $mail_object) + public int $tries = 4; + + public ?string $client_postmark_secret = null; + + public ?string $client_mailgun_secret = null; + + public ?string $client_mailgun_domain = null; + + public bool $override = false; + + public $deleteWhenMissingModels = true; + + private string $mailer = ''; + + public $invitation; + + public Mail $mail; + + private ?string $db; + + public MailObject $mail_object; + + public Mailable $mailable; + + public function __construct($invitation, $db, $mail_object) { $this->invitation = $invitation; @@ -41,23 +71,224 @@ class MailEntity extends BaseMailer implements ShouldQueue } - public function handle(MailBuild $builder): void + public function handle(): void { + $builder = new MailBuild($this); + MultiDB::setDb($this->db); $this->companyCheck(); //construct mailable $builder->run($this); + + $this->mailable = $builder->getMailable(); + + $this->setMailDriver() + ->trySending(); + //spam checks //what do we pass into a generaic builder? //construct mailer - $mailer = $this->configureMailer() - ->trySending(); - } + public function companyCheck(): void + { + /* Handle bad state */ + if(!$this->company) + $this->fail(); + + /* Handle deactivated company */ + if($this->company->is_disabled && !$this->override) + $this->fail(); + + /* To handle spam users we drop all emails from flagged accounts */ + if(Ninja::isHosted() && $this->company->account && $this->company->account->is_flagged) + $this->fail(); + + } + + public function configureMailer(): self + { + $this->setMailDriver(); + + $this->mail = Mail::mailer($this->mailer); + + return $this; + } + + + /** + * Sets the mail driver to use and applies any specific configuration + * the the mailable + */ + private function setMailDriver(): self + { + + switch ($this->mail_object->settings->email_sending_method) { + case 'default': + $this->mailer = config('mail.default'); + break; + // case 'gmail': + // $this->mailer = 'gmail'; + // $this->setGmailMailer(); + // return $this; + // case 'office365': + // $this->mailer = 'office365'; + // $this->setOfficeMailer(); + // return $this; + // case 'client_postmark': + // $this->mailer = 'postmark'; + // $this->setPostmarkMailer(); + // return $this; + // case 'client_mailgun': + // $this->mailer = 'mailgun'; + // $this->setMailgunMailer(); + // return $this; + + default: + break; + } + + if(Ninja::isSelfHost()) + $this->setSelfHostMultiMailer(); + + return $this; + + } + + /** + * Allows configuration of multiple mailers + * per company for use by self hosted users + */ + private function setSelfHostMultiMailer(): void + { + + if (env($this->company->id . '_MAIL_HOST')) + { + + config([ + 'mail.mailers.smtp' => [ + 'transport' => 'smtp', + 'host' => env($this->company->id . '_MAIL_HOST'), + 'port' => env($this->company->id . '_MAIL_PORT'), + 'username' => env($this->company->id . '_MAIL_USERNAME'), + 'password' => env($this->company->id . '_MAIL_PASSWORD'), + ], + ]); + + if(env($this->company->id . '_MAIL_FROM_ADDRESS')) + { + $this->mailable + ->from(env($this->company->id . '_MAIL_FROM_ADDRESS', env('MAIL_FROM_ADDRESS')), env($this->company->id . '_MAIL_FROM_NAME', env('MAIL_FROM_NAME'))); + } + + } + + } + + + /** + * Ensure we discard any data that is not required + * + * @return void + */ + private function cleanUpMailers(): void + { + $this->client_postmark_secret = false; + + $this->client_mailgun_secret = false; + + $this->client_mailgun_domain = false; + + //always dump the drivers to prevent reuse + app('mail.manager')->forgetMailers(); + } + + + public function trySending() + { + try { + + $mail = Mail::mailer($this->mailer); + $mail->send($this->mailable); + + /* Count the amount of emails sent across all the users accounts */ + Cache::increment($this->company->account->key); + + LightLogs::create(new EmailSuccess($this->company->company_key)) + ->send(); + + } + catch(\Symfony\Component\Mime\Exception\RfcComplianceException $e) { + nlog("Mailer failed with a Logic Exception {$e->getMessage()}"); + $this->fail(); + $this->cleanUpMailers(); + $this->logMailError($e->getMessage(), $this->company->clients()->first()); + return; + } + catch(\Symfony\Component\Mime\Exception\LogicException $e){ + nlog("Mailer failed with a Logic Exception {$e->getMessage()}"); + $this->fail(); + $this->cleanUpMailers(); + $this->logMailError($e->getMessage(), $this->company->clients()->first()); + return; + } + catch (\Exception | \Google\Service\Exception $e) { + + nlog("Mailer failed with {$e->getMessage()}"); + $message = $e->getMessage(); + + /** + * Post mark buries the proper message in a a guzzle response + * this merges a text string with a json object + * need to harvest the ->Message property using the following + */ + if(stripos($e->getMessage(), 'code 406') || stripos($e->getMessage(), 'code 300') || stripos($e->getMessage(), 'code 413')) + { + + $message = "Either Attachment too large, or recipient has been suppressed."; + + $this->fail(); + $this->logMailError($e->getMessage(), $this->company->clients()->first()); + $this->cleanUpMailers(); + + return; + + } + + //only report once, not on all tries + if($this->attempts() == $this->tries) + { + + /* If the is an entity attached to the message send a failure mailer */ + if($this->nmo->entity) + $this->entityEmailFailed($message); + + /* Don't send postmark failures to Sentry */ + if(Ninja::isHosted() && (!$e instanceof ClientException)) + app('sentry')->captureException($e); + + } + + /* Releasing immediately does not add in the backoff */ + $this->release($this->backoff()[$this->attempts()-1]); + + } + } + + public function backoff() + { + return [5, 10, 30, 240]; + } + + public function failed($exception = null) + { + + config(['queue.failed.driver' => null]); + + } } diff --git a/app/Services/Email/MailMailable.php b/app/Services/Email/MailMailable.php new file mode 100644 index 000000000000..d88a6a62ded9 --- /dev/null +++ b/app/Services/Email/MailMailable.php @@ -0,0 +1,107 @@ +mail_object->subject, + tags: [$this->mail_object->company_key], + replyTo: $this->mail_object->reply_to, + from: $this->mail_object->from, + to: $this->mail_object->to, + bcc: $this->mail_object->bcc + ); + } + + /** + * Get the message content definition. + * + * @return \Illuminate\Mail\Mailables\Content + */ + public function content() + { + return new Content( + view: $this->mail_object->html_template, + text: $this->mail_object->text_template, + with: [ + 'text_body' => strip_tags($this->mail_object->body), //@todo this is a bit hacky here. + 'body' => $this->mail_object->body, + 'settings' => $this->mail_object->settings, + 'whitelabel' => $this->mail_object->whitelabel, + 'logo' => $this->mail_object->logo, + 'signature' => $this->mail_object->signature, + 'company' => $this->mail_object->company, + 'greeting' => '' + ] + ); + } + + /** + * Get the attachments for the message. + * + * @return array + */ + public function attachments() + { + + $attachments = []; + + foreach($this->mail_object->attachments as $file) + { + $attachments[] = Attachment::fromData(fn () => base64_decode($file['file']), $file['name']); + } + + return $attachments; + + } + + /** + * Get the message headers. + * + * @return \Illuminate\Mail\Mailables\Headers + */ + public function headers() + { + + return new Headers( + messageId: null, + references: [], + text: $this->mail_object->headers, + ); + + } + +} diff --git a/app/Services/Email/MailObject.php b/app/Services/Email/MailObject.php index f9467866d60c..8cb9f4abc723 100644 --- a/app/Services/Email/MailObject.php +++ b/app/Services/Email/MailObject.php @@ -11,6 +11,7 @@ namespace App\Services\Email; +use App\Models\Company; use Illuminate\Mail\Mailables\Address; /** @@ -77,11 +78,12 @@ class MailObject public array $variables = []; - public ?string $reminder_template = null; + public ?string $template = null; public ?string $template_data = null; public bool $override = false; + public ?Company $company = null; } \ No newline at end of file diff --git a/app/Transformers/CompanyUserTransformer.php b/app/Transformers/CompanyUserTransformer.php index e71eed9ad542..6a0643d11b9c 100644 --- a/app/Transformers/CompanyUserTransformer.php +++ b/app/Transformers/CompanyUserTransformer.php @@ -44,6 +44,7 @@ class CompanyUserTransformer extends EntityTransformer 'permissions' => $company_user->permissions ?: '', 'notifications' => $company_user->notifications ? (object) $company_user->notifications : $blank_obj, 'settings' => $company_user->settings ? (object) $company_user->settings : $blank_obj, + 'react_settings' => $company_user->react_settings ? (object) $company_user->react_settings : $blank_obj, 'is_owner' => (bool) $company_user->is_owner, 'is_admin' => (bool) $company_user->is_admin, 'is_locked' => (bool) $company_user->is_locked, diff --git a/database/migrations/2023_02_14_064135_create_react_settings_column_company_user_table.php b/database/migrations/2023_02_14_064135_create_react_settings_column_company_user_table.php new file mode 100644 index 000000000000..281b7d8e4c9d --- /dev/null +++ b/database/migrations/2023_02_14_064135_create_react_settings_column_company_user_table.php @@ -0,0 +1,34 @@ +mediumText('react_settings')->nullable(); + + \Illuminate\Support\Facades\Artisan::call('ninja:design-update'); + + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + + } +};