Updates for mailablej

This commit is contained in:
David Bomba 2023-02-14 21:04:07 +11:00
parent 6384e27e4b
commit 222cbc7703
8 changed files with 685 additions and 117 deletions

View File

@ -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()

View File

@ -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
}
}
`

View File

@ -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;
}

View File

@ -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]);
}
}

View File

@ -0,0 +1,107 @@
<?php
/**
* Invoice Ninja (https://invoiceninja.com).
*
* @link https://github.com/invoiceninja/invoiceninja source repository
*
* @copyright Copyright (c) 2023. Invoice Ninja LLC (https://invoiceninja.com)
*
* @license https://www.elastic.co/licensing/elastic-license
*/
namespace App\Services\Email;
use App\Services\Email\MailObject;
use Illuminate\Mail\Attachment;
use Illuminate\Mail\Mailable;
use Illuminate\Mail\Mailables\Content;
use Illuminate\Mail\Mailables\Envelope;
use Illuminate\Mail\Mailables\Headers;
class MailMailable extends Mailable
{
/**
* Create a new message instance.
*
* @return void
*/
public function __construct(public MailObject $mail_object){}
/**
* Get the message envelope.
*
* @return \Illuminate\Mail\Mailables\Envelope
*/
public function envelope()
{
return new Envelope(
subject: $this->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,
);
}
}

View File

@ -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;
}

View File

@ -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,

View File

@ -0,0 +1,34 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::table('company_user', function (Blueprint $table) {
$table->mediumText('react_settings')->nullable();
\Illuminate\Support\Facades\Artisan::call('ninja:design-update');
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
}
};