mirror of
https://github.com/invoiceninja/invoiceninja.git
synced 2024-11-10 13:12:50 +01:00
Fixes and documentation for Email Defaults
This commit is contained in:
parent
b582f489c4
commit
5dacc04295
@ -21,8 +21,6 @@ use Illuminate\Http\Response;
|
|||||||
use App\Utils\Traits\MakesHash;
|
use App\Utils\Traits\MakesHash;
|
||||||
use App\Jobs\Entity\EmailEntity;
|
use App\Jobs\Entity\EmailEntity;
|
||||||
use App\Models\RecurringInvoice;
|
use App\Models\RecurringInvoice;
|
||||||
use App\Services\Email\MailEntity;
|
|
||||||
use App\Services\Email\MailObject;
|
|
||||||
use App\Services\Email\EmailObject;
|
use App\Services\Email\EmailObject;
|
||||||
use App\Events\Quote\QuoteWasEmailed;
|
use App\Events\Quote\QuoteWasEmailed;
|
||||||
use App\Transformers\QuoteTransformer;
|
use App\Transformers\QuoteTransformer;
|
||||||
@ -121,7 +119,6 @@ class EmailController extends BaseController
|
|||||||
$entity_obj = $entity::withTrashed()->with('invitations')->find($request->input('entity_id'));
|
$entity_obj = $entity::withTrashed()->with('invitations')->find($request->input('entity_id'));
|
||||||
$subject = $request->has('subject') ? $request->input('subject') : '';
|
$subject = $request->has('subject') ? $request->input('subject') : '';
|
||||||
$body = $request->has('body') ? $request->input('body') : '';
|
$body = $request->has('body') ? $request->input('body') : '';
|
||||||
$entity_string = strtolower(class_basename($entity_obj));
|
|
||||||
$template = str_replace('email_template_', '', $request->input('template'));
|
$template = str_replace('email_template_', '', $request->input('template'));
|
||||||
|
|
||||||
$data = [
|
$data = [
|
||||||
@ -133,10 +130,10 @@ class EmailController extends BaseController
|
|||||||
$mo->subject = strlen($subject) > 3 ? $subject : null;
|
$mo->subject = strlen($subject) > 3 ? $subject : null;
|
||||||
$mo->body = strlen($body) > 3 ? $body : null;
|
$mo->body = strlen($body) > 3 ? $body : null;
|
||||||
$mo->entity_id = $request->input('entity_id');
|
$mo->entity_id = $request->input('entity_id');
|
||||||
$mo->template = $template;
|
$mo->template = $template; //full template name in use
|
||||||
$mo->entity_class = $this->resolveClass($entity);
|
$mo->entity_class = $this->resolveClass($entity);
|
||||||
$mo->email_template_body = "email_template_{$template}";
|
$mo->email_template_body = $template;
|
||||||
$mo->email_template_subject = "email_subject_{$template}";
|
$mo->email_template_subject = str_replace("template", "subject", $template);
|
||||||
|
|
||||||
if (Ninja::isHosted() && !$entity_obj->company->account->account_sms_verified) {
|
if (Ninja::isHosted() && !$entity_obj->company->account->account_sms_verified) {
|
||||||
return response(['message' => 'Please verify your account to send emails.'], 400);
|
return response(['message' => 'Please verify your account to send emails.'], 400);
|
||||||
@ -146,7 +143,7 @@ class EmailController extends BaseController
|
|||||||
return $this->sendPurchaseOrder($entity_obj, $data, $template);
|
return $this->sendPurchaseOrder($entity_obj, $data, $template);
|
||||||
}
|
}
|
||||||
|
|
||||||
$entity_obj->invitations->each(function ($invitation) use ($data, $entity_string, $entity_obj, $template, $mo) {
|
$entity_obj->invitations->each(function ($invitation) use ($data, $entity_obj, $template, $mo) {
|
||||||
if (! $invitation->contact->trashed() && $invitation->contact->email) {
|
if (! $invitation->contact->trashed() && $invitation->contact->email) {
|
||||||
$entity_obj->service()->markSent()->save();
|
$entity_obj->service()->markSent()->save();
|
||||||
|
|
||||||
@ -155,7 +152,7 @@ class EmailController extends BaseController
|
|||||||
$mo->invitation_id = $invitation->id;
|
$mo->invitation_id = $invitation->id;
|
||||||
|
|
||||||
Email::dispatch($mo, $invitation->company);
|
Email::dispatch($mo, $invitation->company);
|
||||||
// MailEntity::dispatch($invitation, $invitation->company->db, $mo);
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -214,10 +211,14 @@ class EmailController extends BaseController
|
|||||||
|
|
||||||
private function resolveClass(string $entity): string
|
private function resolveClass(string $entity): string
|
||||||
{
|
{
|
||||||
|
|
||||||
match($entity){
|
match($entity){
|
||||||
'invoice' => $class = Invoice::class,
|
'invoice' => $class = Invoice::class,
|
||||||
|
'App\Models\Invoice' => $class = Invoice::class,
|
||||||
'credit' => $class = Credit::class,
|
'credit' => $class = Credit::class,
|
||||||
|
'App\Models\Credit' => $class = Credit::class,
|
||||||
'quote' => $class = Quote::class,
|
'quote' => $class = Quote::class,
|
||||||
|
'App\Models\Quote' => $class = Quote::class,
|
||||||
'purchase_order' => $class = PurchaseOrder::class,
|
'purchase_order' => $class = PurchaseOrder::class,
|
||||||
'purchaseOrder' => $class = PurchaseOrder::class,
|
'purchaseOrder' => $class = PurchaseOrder::class,
|
||||||
'App\Models\PurchaseOrder' => $class = PurchaseOrder::class,
|
'App\Models\PurchaseOrder' => $class = PurchaseOrder::class,
|
||||||
@ -225,5 +226,6 @@ class EmailController extends BaseController
|
|||||||
};
|
};
|
||||||
|
|
||||||
return $class;
|
return $class;
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -134,7 +134,6 @@ class EmailDefaults
|
|||||||
*/
|
*/
|
||||||
private function setTo(): self
|
private function setTo(): self
|
||||||
{
|
{
|
||||||
|
|
||||||
if ($this->email->email_object->to) {
|
if ($this->email->email_object->to) {
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
@ -150,10 +149,13 @@ class EmailDefaults
|
|||||||
private function setBody(): self
|
private function setBody(): self
|
||||||
{
|
{
|
||||||
if ($this->email->email_object->body) {
|
if ($this->email->email_object->body) {
|
||||||
$this->email->email_object->body = $this->email->email_object->body;
|
// A Custom Message has been set in the email screen.
|
||||||
|
return $this;
|
||||||
} elseif (strlen($this->email->email_object->settings?->{$this->email->email_object->email_template_body}) > 3) {
|
} elseif (strlen($this->email->email_object->settings?->{$this->email->email_object->email_template_body}) > 3) {
|
||||||
|
// A body has been saved in the settings.
|
||||||
$this->email->email_object->body = $this->email->email_object->settings?->{$this->email->email_object->email_template_body};
|
$this->email->email_object->body = $this->email->email_object->settings?->{$this->email->email_object->email_template_body};
|
||||||
} else {
|
} else {
|
||||||
|
// Default template to be used
|
||||||
$this->email->email_object->body = EmailTemplateDefaults::getDefaultTemplate($this->email->email_object->email_template_body, $this->locale);
|
$this->email->email_object->body = EmailTemplateDefaults::getDefaultTemplate($this->email->email_object->email_template_body, $this->locale);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -207,7 +209,7 @@ class EmailDefaults
|
|||||||
if ($this->template != 'custom') {
|
if ($this->template != 'custom') {
|
||||||
$this->email->email_object->body = $this->parseMarkdownToHtml($this->email->email_object->body);
|
$this->email->email_object->body = $this->parseMarkdownToHtml($this->email->email_object->body);
|
||||||
}
|
}
|
||||||
nlog($this->email->email_object->subject);
|
nlog($this->email->email_object->subject);
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,570 +0,0 @@
|
|||||||
<?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\DataMapper\EmailTemplateDefaults;
|
|
||||||
use App\Jobs\Entity\CreateRawPdf;
|
|
||||||
use App\Jobs\Vendor\CreatePurchaseOrderPdf;
|
|
||||||
use App\Models\Account;
|
|
||||||
use App\Models\Client;
|
|
||||||
use App\Models\ClientContact;
|
|
||||||
use App\Models\Credit;
|
|
||||||
use App\Models\Expense;
|
|
||||||
use App\Models\Invoice;
|
|
||||||
use App\Models\PurchaseOrder;
|
|
||||||
use App\Models\Quote;
|
|
||||||
use App\Models\Task;
|
|
||||||
use App\Models\Vendor;
|
|
||||||
use App\Models\VendorContact;
|
|
||||||
use App\Utils\HtmlEngine;
|
|
||||||
use App\Utils\Ninja;
|
|
||||||
use App\Utils\Traits\MakesHash;
|
|
||||||
use App\Utils\VendorHtmlEngine;
|
|
||||||
use Illuminate\Contracts\Mail\Mailable;
|
|
||||||
use Illuminate\Mail\Mailables\Address;
|
|
||||||
use Illuminate\Support\Facades\App;
|
|
||||||
use Illuminate\Support\Facades\URL;
|
|
||||||
use League\CommonMark\CommonMarkConverter;
|
|
||||||
|
|
||||||
class MailBuild
|
|
||||||
{
|
|
||||||
use MakesHash;
|
|
||||||
|
|
||||||
/** @var mixed $settings */
|
|
||||||
protected $settings;
|
|
||||||
|
|
||||||
/** @var string $template */
|
|
||||||
private string $template;
|
|
||||||
|
|
||||||
/** @var string $locale */
|
|
||||||
private string $locale;
|
|
||||||
|
|
||||||
/** @var ?Client $client */
|
|
||||||
private ?Client $client;
|
|
||||||
|
|
||||||
/** @var ?Vendor $vendor */
|
|
||||||
private ?Vendor $vendor;
|
|
||||||
|
|
||||||
/** @var mixed $html_engine */
|
|
||||||
private mixed $html_engine;
|
|
||||||
|
|
||||||
/** @var array $variables */
|
|
||||||
private array $variables = [];
|
|
||||||
|
|
||||||
/** @var int $max_attachment_size */
|
|
||||||
public int $max_attachment_size = 3000000;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* __construct
|
|
||||||
*
|
|
||||||
* @param MailEntity $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->resolveEntities()
|
|
||||||
->setLocale()
|
|
||||||
->setMetaData()
|
|
||||||
->setFrom()
|
|
||||||
->setTo()
|
|
||||||
->setTemplate()
|
|
||||||
->setSubject()
|
|
||||||
->setBody()
|
|
||||||
->setReplyTo()
|
|
||||||
->setBcc()
|
|
||||||
->setAttachments()
|
|
||||||
->setVariables();
|
|
||||||
|
|
||||||
return $this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the mailable to the mailer
|
|
||||||
*
|
|
||||||
* @return Mailable
|
|
||||||
*/
|
|
||||||
public function getMailable(): Mailable
|
|
||||||
{
|
|
||||||
return new MailMailable($this->mail_entity->mail_object);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Resolve any class entities
|
|
||||||
*
|
|
||||||
* @return self
|
|
||||||
*/
|
|
||||||
private function resolveEntities(): self
|
|
||||||
{
|
|
||||||
$client_contact = $this->mail_entity?->invitation?->client_contact_id ? ClientContact::withTrashed()->find($this->mail_entity->invitation->client_contact_id) : null;
|
|
||||||
|
|
||||||
$this->client = $client_contact?->client;
|
|
||||||
|
|
||||||
$vendor_contact = $this->mail_entity?->invitation?->vendor_contact_id ? VendorContact::withTrashed()->find($this->mail_entity->invitation->vendor_contact_id) : null;
|
|
||||||
|
|
||||||
$this->vendor = $vendor_contact?->vendor;
|
|
||||||
|
|
||||||
if ($this->mail_entity?->invitation) {
|
|
||||||
if ($this->mail_entity->invitation?->invoice) {
|
|
||||||
$this->mail_entity->mail_object->entity_string = 'invoice';
|
|
||||||
$this->mail_entity->mail_object->entity_class = Invoice::class;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($this->mail_entity->invitation?->quote) {
|
|
||||||
$this->mail_entity->mail_object->entity_string = 'quote';
|
|
||||||
$this->mail_entity->mail_object->entity_class = Quote::class;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($this->mail_entity->invitation?->credit) {
|
|
||||||
$this->mail_entity->mail_object->entity_string = 'credit';
|
|
||||||
$this->mail_entity->mail_object->entity_class = Credit::class;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($this->mail_entity->invitation?->puchase_order) {
|
|
||||||
$this->mail_entity->mail_object->entity_string = 'purchase_order';
|
|
||||||
$this->mail_entity->mail_object->entity_class = PurchaseOrder::class;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return $this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the locale
|
|
||||||
* Sets the settings object depending on context
|
|
||||||
* Sets the HTML variables depending on context
|
|
||||||
*
|
|
||||||
* @return self
|
|
||||||
*/
|
|
||||||
private function setLocale(): self
|
|
||||||
{
|
|
||||||
if ($this->client) {
|
|
||||||
$this->locale = $this->client->locale();
|
|
||||||
$this->settings = $this->client->getMergedSettings();
|
|
||||||
|
|
||||||
if ($this->mail_entity->invitation) {
|
|
||||||
$this->variables = (new HtmlEngine($this->mail_entity->invitation))->makeValues();
|
|
||||||
}
|
|
||||||
} elseif ($this->vendor) {
|
|
||||||
$this->locale = $this->vendor->locale();
|
|
||||||
$this->settings = $this->mail_entity->company->settings;
|
|
||||||
|
|
||||||
if ($this->mail_entity->invitation) {
|
|
||||||
$this->variables = (new VendorHtmlEngine($this->mail_entity->invitation))->makeValues();
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
$this->locale = $this->mail_entity->company->locale();
|
|
||||||
$this->settings = $this->mail_entity->company->settings;
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->mail_entity->mail_object->settings = $this->settings;
|
|
||||||
|
|
||||||
App::setLocale($this->locale);
|
|
||||||
App::forgetInstance('translator');
|
|
||||||
$t = app('translator');
|
|
||||||
$t->replace(Ninja::transformTranslations($this->settings));
|
|
||||||
|
|
||||||
return $this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the meta data for the Email object
|
|
||||||
*
|
|
||||||
* @return self
|
|
||||||
*/
|
|
||||||
private function setMetaData(): self
|
|
||||||
{
|
|
||||||
$this->mail_entity->mail_object->company_key = $this->mail_entity->company->company_key;
|
|
||||||
|
|
||||||
$this->mail_entity->mail_object->logo = $this->mail_entity->company->present()->logo();
|
|
||||||
|
|
||||||
$this->mail_entity->mail_object->signature = $this->mail_entity->mail_object->signature ?: $this->settings->email_signature;
|
|
||||||
|
|
||||||
$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 template
|
|
||||||
*
|
|
||||||
* @return self
|
|
||||||
*/
|
|
||||||
private function setTemplate(): self
|
|
||||||
{
|
|
||||||
$this->template = $this->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',
|
|
||||||
default => $this->template = 'email.template.client',
|
|
||||||
};
|
|
||||||
|
|
||||||
$this->mail_entity->mail_object->html_template = $this->template;
|
|
||||||
|
|
||||||
return $this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* setTo
|
|
||||||
*
|
|
||||||
* @return self
|
|
||||||
*/
|
|
||||||
private function setTo(): self
|
|
||||||
{
|
|
||||||
$this->mail_entity->mail_object->to = array_merge(
|
|
||||||
$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->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;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($this->mail_entity->mail_object->from) {
|
|
||||||
return $this;
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->mail_entity->mail_object->from = new Address($this->mail_entity->company->owner()->email, $this->mail_entity->company->owner()->name());
|
|
||||||
|
|
||||||
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) && strlen($this->settings->{$this->resolveBaseEntityTemplate()}) > 3) {
|
|
||||||
$this->mail_entity->mail_object->subject = $this->settings->{$this->resolveBaseEntityTemplate()};
|
|
||||||
} else {
|
|
||||||
$this->mail_entity->mail_object->subject = EmailTemplateDefaults::getDefaultTemplate($this->resolveBaseEntityTemplate(), $this->locale);
|
|
||||||
}
|
|
||||||
|
|
||||||
return $this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the body of the email
|
|
||||||
*
|
|
||||||
* @return self
|
|
||||||
*/
|
|
||||||
private function setBody(): self
|
|
||||||
{
|
|
||||||
if ($this->mail_entity->mail_object->body) {
|
|
||||||
$this->mail_entity->mail_object->body = $this->mail_entity->mail_object->body;
|
|
||||||
} elseif (is_string($this->mail_entity->mail_object->email_template) && strlen($this->settings->{$this->resolveBaseEntityTemplate('body')}) > 3) {
|
|
||||||
$this->mail_entity->mail_object->body = $this->settings->{$this->resolveBaseEntityTemplate('body')};
|
|
||||||
} else {
|
|
||||||
$this->mail_entity->mail_object->body = EmailTemplateDefaults::getDefaultTemplate($this->resolveBaseEntityTemplate('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->settings->email_style_custom));
|
|
||||||
}
|
|
||||||
|
|
||||||
return $this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Where no template is explicitly passed, we need to infer by the entity type -
|
|
||||||
* which is hopefully resolvable.
|
|
||||||
*
|
|
||||||
* @param string $type
|
|
||||||
* @return string
|
|
||||||
*/
|
|
||||||
private function resolveBaseEntityTemplate(string $type = 'subject'): string
|
|
||||||
{
|
|
||||||
if ($this->mail_entity->mail_object->email_template) {
|
|
||||||
match ($type) {
|
|
||||||
'subject' => $template = "email_subject_{$this->mail_entity->mail_object->email_template}",
|
|
||||||
'body' => $template = "email_template_{$this->mail_entity->mail_object->email_template}",
|
|
||||||
default => $template = "email_template_invoice",
|
|
||||||
};
|
|
||||||
|
|
||||||
return $template;
|
|
||||||
}
|
|
||||||
|
|
||||||
//handle statements being emailed
|
|
||||||
//handle custom templates these types won't have a resolvable entity_string
|
|
||||||
if (!$this->mail_entity->mail_object->entity_string) {
|
|
||||||
return 'email_template_invoice';
|
|
||||||
}
|
|
||||||
|
|
||||||
match ($type) {
|
|
||||||
'subject' => $template = "email_subject_{$this->mail_entity->mail_object->entity_string}",
|
|
||||||
'body' => $template = "email_template_{$this->mail_entity->mail_object->entity_string}",
|
|
||||||
default => $template = "email_template_invoice",
|
|
||||||
};
|
|
||||||
|
|
||||||
return $template;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 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
|
|
||||||
{
|
|
||||||
$this->setContextAttachments();
|
|
||||||
|
|
||||||
if ($this->settings->document_email_attachment && $this->mail_entity->company->account->hasFeature(Account::FEATURE_DOCUMENTS)) {
|
|
||||||
$this->attachDocuments($this->mail_entity->company->documents);
|
|
||||||
}
|
|
||||||
|
|
||||||
return $this;
|
|
||||||
}
|
|
||||||
|
|
||||||
private function attachDocuments($documents): self
|
|
||||||
{
|
|
||||||
foreach ($documents as $document) {
|
|
||||||
if ($document->size > $this->max_attachment_size) {
|
|
||||||
$this->mail_entity->mail_object->attachment_links = array_merge($this->mail_entity->mail_object->attachment_links, [["<a class='doc_links' href='" . URL::signedRoute('documents.public_download', ['document_hash' => $document->hash]) ."'>". $document->name ."</a>"]]);
|
|
||||||
} else {
|
|
||||||
$this->mail_entity->mail_object->attachments = array_merge($this->mail_entity->mail_object->attachments, [['file' => base64_encode($document->getFile()), 'name' => $document->name]]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return $this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Depending on context we may need to resolve the
|
|
||||||
* attachment dependencies.
|
|
||||||
*
|
|
||||||
* ie. Resolve the entity.
|
|
||||||
* ie. Resolve if we should attach the Entity PDF
|
|
||||||
* ie. Create the Entity PDF
|
|
||||||
* ie. Inject the PDF
|
|
||||||
*
|
|
||||||
* @return self
|
|
||||||
*/
|
|
||||||
private function setContextAttachments(): self
|
|
||||||
{
|
|
||||||
if (!$this->settings->pdf_email_attachment || !$this->mail_entity->company->account->hasFeature(Account::FEATURE_PDF_ATTACHMENT)) {
|
|
||||||
return $this;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($this->mail_entity->invitation?->purchase_order) {
|
|
||||||
$pdf = (new CreatePurchaseOrderPdf($this->mail_entity->invitation))->rawPdf();
|
|
||||||
|
|
||||||
$this->mail_entity->mail_object->attachments = array_merge($this->mail_entity->mail_object->attachments, [['file' => base64_encode($pdf), 'name' => $this->mail_entity->invitation->purchase_order->numberFormatter().'.pdf']]);
|
|
||||||
|
|
||||||
if ($this->vendor->getSetting('document_email_attachment') !== false && $this->mail_entity->company->account->hasFeature(Account::FEATURE_DOCUMENTS)) {
|
|
||||||
$this->attachDocuments($this->mail_entity->invitation->purchase_order->documents);
|
|
||||||
}
|
|
||||||
|
|
||||||
return $this;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!$this->mail_entity->mail_object->entity_string) {
|
|
||||||
return $this;
|
|
||||||
}
|
|
||||||
|
|
||||||
$pdf = ((new CreateRawPdf($this->mail_entity->invitation, $this->mail_entity->invitation->company->db))->handle());
|
|
||||||
|
|
||||||
$this->mail_entity->mail_object->attachments = array_merge($this->mail_entity->mail_object->attachments, [['file' => base64_encode($pdf), 'name' => $this->mail_entity->invitation->{$this->mail_entity->mail_object->entity_string}->numberFormatter().'.pdf']]);
|
|
||||||
|
|
||||||
if ($this->client->getSetting('document_email_attachment') !== false && $this->mail_entity->company->account->hasFeature(Account::FEATURE_DOCUMENTS)) {
|
|
||||||
$this->attachDocuments($this->mail_entity->invitation->{$this->mail_entity->mail_object->entity_string}->documents);
|
|
||||||
}
|
|
||||||
|
|
||||||
return $this;
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
if ($this->settings->ubl_email_attachment && $this->mail_entity->mail_object->entity_string == 'invoice') {
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($this->mail_entity->mail_object->entity_string == 'invoice') {
|
|
||||||
$line_items = $this->mail_entity->invitation->invoice->line_items;
|
|
||||||
|
|
||||||
foreach ($line_items as $item) {
|
|
||||||
$expense_ids = [];
|
|
||||||
|
|
||||||
if (property_exists($item, 'expense_id')) {
|
|
||||||
$expense_ids[] = $item->expense_id;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (count($expense_ids) > 0) {
|
|
||||||
$expenses = Expense::whereIn('id', $this->transformKeys($expense_ids))
|
|
||||||
->where('invoice_documents', 1)
|
|
||||||
->cursor()
|
|
||||||
->each(function ($expense) {
|
|
||||||
$this->attachDocuments($expense->documents);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
$task_ids = [];
|
|
||||||
|
|
||||||
if (property_exists($item, 'task_id')) {
|
|
||||||
$task_ids[] = $item->task_id;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (count($task_ids) > 0 && $this->mail_entity->company->invoice_task_documents) {
|
|
||||||
$tasks = Task::whereIn('id', $this->transformKeys($task_ids))
|
|
||||||
->cursor()
|
|
||||||
->each(function ($task) {
|
|
||||||
$this->attachDocuments($task->documents);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
return $this;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the reply to of the email
|
|
||||||
*
|
|
||||||
* @return self
|
|
||||||
*/
|
|
||||||
private function setReplyTo(): self
|
|
||||||
{
|
|
||||||
$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->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)]);
|
|
||||||
|
|
||||||
return $this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Replaces the template placeholders
|
|
||||||
* with variable values.
|
|
||||||
*
|
|
||||||
* @return self
|
|
||||||
*/
|
|
||||||
public function setVariables(): self
|
|
||||||
{
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->mail_entity->mail_object->subject = strtr($this->mail_entity->mail_object->subject, $this->variables);
|
|
||||||
$this->mail_entity->mail_object->body = strtr($this->mail_entity->mail_object->body, $this->variables);
|
|
||||||
|
|
||||||
if ($this->template != 'custom') {
|
|
||||||
$this->mail_entity->mail_object->body = $this->parseMarkdownToHtml($this->mail_entity->mail_object->body);
|
|
||||||
}
|
|
||||||
|
|
||||||
return $this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the BCC of the email
|
|
||||||
*
|
|
||||||
* @return self
|
|
||||||
*/
|
|
||||||
private function setBcc(): self
|
|
||||||
{
|
|
||||||
$bccs = [];
|
|
||||||
$bcc_array = [];
|
|
||||||
|
|
||||||
if (strlen($this->settings->bcc_email) > 1) {
|
|
||||||
if (Ninja::isHosted() && $this->mail_entity->company->account->isPaid()) {
|
|
||||||
$bccs = array_slice(explode(',', str_replace(' ', '', $this->settings->bcc_email)), 0, 2);
|
|
||||||
} elseif (Ninja::isSelfHost()) {
|
|
||||||
$bccs = (explode(',', str_replace(' ', '', $this->settings->bcc_email)));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach ($bccs as $bcc) {
|
|
||||||
$bcc_array[] = new Address($bcc);
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->mail_entity->mail_object->bcc = array_merge($this->mail_entity->mail_object->bcc, $bcc_array);
|
|
||||||
|
|
||||||
return $this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the CC of the email
|
|
||||||
* @todo at some point....
|
|
||||||
*/
|
|
||||||
private function buildCc()
|
|
||||||
{
|
|
||||||
return [
|
|
||||||
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 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;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Converts any markdown to HTML in the email
|
|
||||||
*
|
|
||||||
* @param string $markdown The body to convert
|
|
||||||
* @return string The parsed markdown response
|
|
||||||
*/
|
|
||||||
private function parseMarkdownToHtml(string $markdown): ?string
|
|
||||||
{
|
|
||||||
$converter = new CommonMarkConverter([
|
|
||||||
'allow_unsafe_links' => false,
|
|
||||||
]);
|
|
||||||
|
|
||||||
return $converter->convert($markdown);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,589 +0,0 @@
|
|||||||
<?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\DataMapper\Analytics\EmailSuccess;
|
|
||||||
use App\Libraries\Google\Google;
|
|
||||||
use App\Libraries\MultiDB;
|
|
||||||
use App\Models\Company;
|
|
||||||
use App\Models\User;
|
|
||||||
use App\Utils\Ninja;
|
|
||||||
use App\Utils\Traits\MakesHash;
|
|
||||||
use GuzzleHttp\Exception\ClientException;
|
|
||||||
use Illuminate\Bus\Queueable;
|
|
||||||
use Illuminate\Contracts\Mail\Mailer;
|
|
||||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
|
||||||
use Illuminate\Foundation\Bus\Dispatchable;
|
|
||||||
use Illuminate\Mail\Mailable;
|
|
||||||
use Illuminate\Queue\InteractsWithQueue;
|
|
||||||
use Illuminate\Queue\SerializesModels;
|
|
||||||
use Illuminate\Support\Facades\Cache;
|
|
||||||
use Illuminate\Support\Facades\Mail;
|
|
||||||
use Turbo124\Beacon\Facades\LightLogs;
|
|
||||||
|
|
||||||
class MailEntity implements ShouldQueue
|
|
||||||
{
|
|
||||||
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels, MakesHash;
|
|
||||||
|
|
||||||
public Company $company;
|
|
||||||
|
|
||||||
public Mailable $mailable;
|
|
||||||
|
|
||||||
public Mailer $mail;
|
|
||||||
|
|
||||||
public ?string $client_postmark_secret = null;
|
|
||||||
|
|
||||||
public ?string $client_mailgun_secret = null;
|
|
||||||
|
|
||||||
public ?string $client_mailgun_domain = null;
|
|
||||||
|
|
||||||
public bool $override = false;
|
|
||||||
|
|
||||||
private string $mailer = '';
|
|
||||||
|
|
||||||
public int $tries = 4;
|
|
||||||
|
|
||||||
public $deleteWhenMissingModels = true;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* __construct
|
|
||||||
*
|
|
||||||
* @param mixed $invitation
|
|
||||||
* @param mixed $db
|
|
||||||
* @param mixed $mail_object
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
public function __construct(public mixed $invitation, private ?string $db, public MailObject $mail_object)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Handle the job
|
|
||||||
*
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
public function handle(): void
|
|
||||||
{
|
|
||||||
MultiDB::setDb($this->db);
|
|
||||||
|
|
||||||
/* Where there are no invitations, we need to harvest the company and also use the correct context to build the mailable*/
|
|
||||||
$this->company = $this->invitation->company;
|
|
||||||
|
|
||||||
$this->override = $this->mail_object->override;
|
|
||||||
|
|
||||||
$builder = new MailBuild($this);
|
|
||||||
|
|
||||||
/* Construct Mailable */
|
|
||||||
$builder->run($this);
|
|
||||||
|
|
||||||
$this->mailable = $builder->getMailable();
|
|
||||||
|
|
||||||
/* Email quality checks */
|
|
||||||
if ($this->preFlightChecksFail()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Try sending email */
|
|
||||||
$this->setMailDriver()
|
|
||||||
->configureMailer()
|
|
||||||
->trySending();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* configureMailer
|
|
||||||
*
|
|
||||||
* @return self
|
|
||||||
*/
|
|
||||||
public function configureMailer(): self
|
|
||||||
{
|
|
||||||
$this->mail = Mail::mailer($this->mailer);
|
|
||||||
|
|
||||||
if ($this->client_postmark_secret) {
|
|
||||||
$this->mail->postmark_config($this->client_postmark_secret);
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($this->client_mailgun_secret) {
|
|
||||||
$this->mail->mailgun_config($this->client_mailgun_secret, $this->client_mailgun_domain);
|
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
|
||||||
|
|
||||||
app('mail.manager')->forgetMailers();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Attempts to send the email
|
|
||||||
*
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
public function trySending(): void
|
|
||||||
{
|
|
||||||
try {
|
|
||||||
$this->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->mail_object->entity_id) {
|
|
||||||
// $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]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* On the hosted platform we scan all outbound email for
|
|
||||||
* spam. This sequence processes the filters we use on all
|
|
||||||
* emails.
|
|
||||||
*/
|
|
||||||
public function preFlightChecksFail(): bool
|
|
||||||
{
|
|
||||||
/* Handle bad state */
|
|
||||||
if (!$this->company) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Handle deactivated company */
|
|
||||||
if ($this->company->is_disabled && !$this->override) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* To handle spam users we drop all emails from flagged accounts */
|
|
||||||
if (Ninja::isHosted() && $this->company->account && $this->company->account->is_flagged) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* On the hosted platform we set default contacts a @example.com email address - we shouldn't send emails to these types of addresses */
|
|
||||||
if ($this->hasInValidEmails()) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* GMail users are uncapped */
|
|
||||||
if (in_array($this->mail_object->settings->email_sending_method, ['gmail', 'office365', 'client_postmark', 'client_mailgun'])) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* On the hosted platform, if the user is over the email quotas, we do not send the email. */
|
|
||||||
if (Ninja::isHosted() && $this->company->account && $this->company->account->emailQuotaExceeded()) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* If the account is verified, we allow emails to flow */
|
|
||||||
if (Ninja::isHosted() && $this->company->account && $this->company->account->is_verified_account) {
|
|
||||||
//11-01-2022
|
|
||||||
|
|
||||||
/* Continue to analyse verified accounts in case they later start sending poor quality emails*/
|
|
||||||
// if(class_exists(\Modules\Admin\Jobs\Account\EmailQuality::class))
|
|
||||||
// (new \Modules\Admin\Jobs\Account\EmailQuality($this->mail_object, $this->company))->run();
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/* On the hosted platform if the user has not verified their account we fail here - but still check what they are trying to send! */
|
|
||||||
if ($this->company->account && !$this->company->account->account_sms_verified) {
|
|
||||||
if (class_exists(\Modules\Admin\Jobs\Account\EmailFilter::class)) {
|
|
||||||
(new \Modules\Admin\Jobs\Account\EmailFilter($this->mail_object, $this->company))->run();
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* On the hosted platform we actively scan all outbound emails to ensure outbound email quality remains high */
|
|
||||||
if (class_exists(\Modules\Admin\Jobs\Account\EmailFilter::class)) {
|
|
||||||
(new \Modules\Admin\Jobs\Account\EmailFilter($this->mail_object, $this->company))->run();
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Checks if emails are have some illegal / required characters.
|
|
||||||
*
|
|
||||||
* @return bool
|
|
||||||
*/
|
|
||||||
private function hasInValidEmails(): bool
|
|
||||||
{
|
|
||||||
if (Ninja::isSelfHost()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach ($this->mail_object->to as $address_object) {
|
|
||||||
if (strpos($address_object->address, '@example.com') !== false) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!str_contains($address_object->address, "@")) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($address_object->address == " ") {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Check to ensure no cross account
|
|
||||||
* emails can be sent.
|
|
||||||
*
|
|
||||||
* @param User $user
|
|
||||||
*/
|
|
||||||
private function checkValidSendingUser($user)
|
|
||||||
{
|
|
||||||
/* Always ensure the user is set on the correct account */
|
|
||||||
if ($user->account_id != $this->company->account_id) {
|
|
||||||
$this->mail_object->settings->email_sending_method = 'default';
|
|
||||||
return $this->setMailDriver();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Resolves the sending user
|
|
||||||
* when configuring the Mailer
|
|
||||||
* on behalf of the client
|
|
||||||
*
|
|
||||||
* @return User $user
|
|
||||||
*/
|
|
||||||
private function resolveSendingUser(): ?User
|
|
||||||
{
|
|
||||||
$sending_user = $this->mail_object->settings->gmail_sending_user_id;
|
|
||||||
|
|
||||||
if ($sending_user == "0") {
|
|
||||||
$user = $this->company->owner();
|
|
||||||
} else {
|
|
||||||
$user = User::find($this->decodePrimaryKey($sending_user));
|
|
||||||
}
|
|
||||||
|
|
||||||
return $user;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Configures Mailgun using client supplied secret
|
|
||||||
* as the Mailer
|
|
||||||
*/
|
|
||||||
private function setMailgunMailer()
|
|
||||||
{
|
|
||||||
if (strlen($this->mail_object->settings->mailgun_secret) > 2 && strlen($this->mail_object->settings->mailgun_domain) > 2) {
|
|
||||||
$this->client_mailgun_secret = $this->mail_object->settings->mailgun_secret;
|
|
||||||
$this->client_mailgun_domain = $this->mail_object->settings->mailgun_domain;
|
|
||||||
} else {
|
|
||||||
$this->mail_object->settings->email_sending_method = 'default';
|
|
||||||
return $this->setMailDriver();
|
|
||||||
}
|
|
||||||
|
|
||||||
$user = $this->resolveSendingUser();
|
|
||||||
|
|
||||||
$sending_email = (isset($this->mail_object->settings->custom_sending_email) && stripos($this->mail_object->settings->custom_sending_email, "@")) ? $this->mail_object->settings->custom_sending_email : $user->email;
|
|
||||||
$sending_user = (isset($this->mail_object->settings->email_from_name) && strlen($this->mail_object->settings->email_from_name) > 2) ? $this->mail_object->settings->email_from_name : $user->name();
|
|
||||||
|
|
||||||
$this->mailable
|
|
||||||
->from($sending_email, $sending_user);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Configures Postmark using client supplied secret
|
|
||||||
* as the Mailer
|
|
||||||
*/
|
|
||||||
private function setPostmarkMailer()
|
|
||||||
{
|
|
||||||
if (strlen($this->mail_object->settings->postmark_secret) > 2) {
|
|
||||||
$this->client_postmark_secret = $this->mail_object->settings->postmark_secret;
|
|
||||||
} else {
|
|
||||||
$this->mail_object->settings->email_sending_method = 'default';
|
|
||||||
return $this->setMailDriver();
|
|
||||||
}
|
|
||||||
|
|
||||||
$user = $this->resolveSendingUser();
|
|
||||||
|
|
||||||
$sending_email = (isset($this->mail_object->settings->custom_sending_email) && stripos($this->mail_object->settings->custom_sending_email, "@")) ? $this->mail_object->settings->custom_sending_email : $user->email;
|
|
||||||
$sending_user = (isset($this->mail_object->settings->email_from_name) && strlen($this->mail_object->settings->email_from_name) > 2) ? $this->mail_object->settings->email_from_name : $user->name();
|
|
||||||
|
|
||||||
$this->mailable
|
|
||||||
->from($sending_email, $sending_user);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Configures Microsoft via Oauth
|
|
||||||
* as the Mailer
|
|
||||||
*/
|
|
||||||
private function setOfficeMailer()
|
|
||||||
{
|
|
||||||
$user = $this->resolveSendingUser();
|
|
||||||
|
|
||||||
$this->checkValidSendingUser($user);
|
|
||||||
|
|
||||||
nlog("Sending via {$user->name()}");
|
|
||||||
|
|
||||||
$token = $this->refreshOfficeToken($user);
|
|
||||||
|
|
||||||
if ($token) {
|
|
||||||
$user->oauth_user_token = $token;
|
|
||||||
$user->save();
|
|
||||||
} else {
|
|
||||||
$this->mail_object->settings->email_sending_method = 'default';
|
|
||||||
return $this->setMailDriver();
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->mailable
|
|
||||||
->from($user->email, $user->name())
|
|
||||||
->withSymfonyMessage(function ($message) use ($token) {
|
|
||||||
$message->getHeaders()->addTextHeader('gmailtoken', $token);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Configures GMail via Oauth
|
|
||||||
* as the Mailer
|
|
||||||
*/
|
|
||||||
private function setGmailMailer()
|
|
||||||
{
|
|
||||||
$user = $this->resolveSendingUser();
|
|
||||||
|
|
||||||
$this->checkValidSendingUser($user);
|
|
||||||
|
|
||||||
nlog("Sending via {$user->name()}");
|
|
||||||
|
|
||||||
$google = (new Google())->init();
|
|
||||||
|
|
||||||
try {
|
|
||||||
if ($google->getClient()->isAccessTokenExpired()) {
|
|
||||||
$google->refreshToken($user);
|
|
||||||
$user = $user->fresh();
|
|
||||||
}
|
|
||||||
|
|
||||||
$google->getClient()->setAccessToken(json_encode($user->oauth_user_token));
|
|
||||||
} catch(\Exception $e) {
|
|
||||||
// $this->logMailError('Gmail Token Invalid', $this->company->clients()->first());
|
|
||||||
$this->mail_object->settings->email_sending_method = 'default';
|
|
||||||
return $this->setMailDriver();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* If the user doesn't have a valid token, notify them
|
|
||||||
*/
|
|
||||||
|
|
||||||
if (!$user->oauth_user_token) {
|
|
||||||
$this->company->account->gmailCredentialNotification();
|
|
||||||
$this->mail_object->settings->email_sending_method = 'default';
|
|
||||||
return $this->setMailDriver();
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Now that our token is refreshed and valid we can boot the
|
|
||||||
* mail driver at runtime and also set the token which will persist
|
|
||||||
* just for this request.
|
|
||||||
*/
|
|
||||||
|
|
||||||
$token = $user->oauth_user_token->access_token;
|
|
||||||
|
|
||||||
if (!$token) {
|
|
||||||
$this->company->account->gmailCredentialNotification();
|
|
||||||
$this->mail_object->settings->email_sending_method = 'default';
|
|
||||||
return $this->setMailDriver();
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->mailable
|
|
||||||
->from($user->email, $user->name())
|
|
||||||
->withSymfonyMessage(function ($message) use ($token) {
|
|
||||||
$message->getHeaders()->addTextHeader('gmailtoken', $token);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Attempts to refresh the Microsoft refreshToken
|
|
||||||
*
|
|
||||||
* @param App\Models\User
|
|
||||||
* @return string | boool
|
|
||||||
*/
|
|
||||||
private function refreshOfficeToken($user)
|
|
||||||
{
|
|
||||||
$expiry = $user->oauth_user_token_expiry ?: now()->subDay();
|
|
||||||
|
|
||||||
if ($expiry->lt(now())) {
|
|
||||||
$guzzle = new \GuzzleHttp\Client();
|
|
||||||
$url = 'https://login.microsoftonline.com/common/oauth2/v2.0/token';
|
|
||||||
|
|
||||||
$token = json_decode($guzzle->post($url, [
|
|
||||||
'form_params' => [
|
|
||||||
'client_id' => config('ninja.o365.client_id') ,
|
|
||||||
'client_secret' => config('ninja.o365.client_secret') ,
|
|
||||||
'scope' => 'email Mail.Send offline_access profile User.Read openid',
|
|
||||||
'grant_type' => 'refresh_token',
|
|
||||||
'refresh_token' => $user->oauth_user_refresh_token
|
|
||||||
],
|
|
||||||
])->getBody()->getContents());
|
|
||||||
|
|
||||||
if ($token) {
|
|
||||||
$user->oauth_user_refresh_token = property_exists($token, 'refresh_token') ? $token->refresh_token : $user->oauth_user_refresh_token;
|
|
||||||
$user->oauth_user_token = $token->access_token;
|
|
||||||
$user->oauth_user_token_expiry = now()->addSeconds($token->expires_in);
|
|
||||||
$user->save();
|
|
||||||
|
|
||||||
return $token->access_token;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return $user->oauth_user_token;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Backoff time
|
|
||||||
*
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
public function backoff()
|
|
||||||
{
|
|
||||||
return [5, 10, 30, 240];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Failed handler
|
|
||||||
*
|
|
||||||
* @param mixed $exception
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
public function failed($exception = null)
|
|
||||||
{
|
|
||||||
config(['queue.failed.driver' => null]);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,100 +0,0 @@
|
|||||||
<?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 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' => str_replace("<br>", "\n", strip_tags($this->mail_object->body, "<br>")), //@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,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,89 +0,0 @@
|
|||||||
<?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\Models\Company;
|
|
||||||
use Illuminate\Mail\Mailables\Address;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* MailObject.
|
|
||||||
*/
|
|
||||||
class MailObject
|
|
||||||
{
|
|
||||||
public ?string $db = null;
|
|
||||||
|
|
||||||
public array $to = [];
|
|
||||||
|
|
||||||
public ?Address $from = null;
|
|
||||||
|
|
||||||
public array $reply_to = [];
|
|
||||||
|
|
||||||
public array $cc = [];
|
|
||||||
|
|
||||||
public array $bcc = [];
|
|
||||||
|
|
||||||
public ?string $subject = null;
|
|
||||||
|
|
||||||
public ?string $body = null;
|
|
||||||
|
|
||||||
public array $attachments = [];
|
|
||||||
|
|
||||||
public array $attachment_links = [];
|
|
||||||
|
|
||||||
public string $company_key;
|
|
||||||
|
|
||||||
public ?object $settings = null;
|
|
||||||
|
|
||||||
public bool $whitelabel = false;
|
|
||||||
|
|
||||||
public ?string $logo = null;
|
|
||||||
|
|
||||||
public ?string $signature = null;
|
|
||||||
|
|
||||||
public ?string $greeting = null;
|
|
||||||
|
|
||||||
public ?int $client_id = null;
|
|
||||||
|
|
||||||
public ?int $vendor_id = null;
|
|
||||||
|
|
||||||
public ?int $user_id = null;
|
|
||||||
|
|
||||||
public ?int $client_contact_id = null;
|
|
||||||
|
|
||||||
public ?int $vendor_contact_id = null;
|
|
||||||
|
|
||||||
public ?string $email_template = null; //this defines the template in short notation WITH the email_template prefix
|
|
||||||
|
|
||||||
public ?string $html_template = null;
|
|
||||||
|
|
||||||
public ?string $text_template = 'email.template.text';
|
|
||||||
|
|
||||||
public array $headers = [];
|
|
||||||
|
|
||||||
public ?string $invitation_key = null;
|
|
||||||
|
|
||||||
public ?int $entity_id = null;
|
|
||||||
|
|
||||||
public ?string $entity_class = null;
|
|
||||||
|
|
||||||
public ?string $entity_string = null;
|
|
||||||
|
|
||||||
public array $variables = [];
|
|
||||||
|
|
||||||
public ?string $template = null;
|
|
||||||
|
|
||||||
public ?string $template_data = null;
|
|
||||||
|
|
||||||
public bool $override = false;
|
|
||||||
|
|
||||||
public ?Company $company = null;
|
|
||||||
}
|
|
355
composer.lock
generated
355
composer.lock
generated
@ -4,7 +4,7 @@
|
|||||||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||||
"This file is @generated automatically"
|
"This file is @generated automatically"
|
||||||
],
|
],
|
||||||
"content-hash": "440f7b49dc043f645d8ab14c097589ac",
|
"content-hash": "5aa22a61531e67324395d207a7c94e27",
|
||||||
"packages": [
|
"packages": [
|
||||||
{
|
{
|
||||||
"name": "afosto/yaac",
|
"name": "afosto/yaac",
|
||||||
@ -1508,28 +1508,27 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "doctrine/lexer",
|
"name": "doctrine/lexer",
|
||||||
"version": "2.1.0",
|
"version": "3.0.0",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/doctrine/lexer.git",
|
"url": "https://github.com/doctrine/lexer.git",
|
||||||
"reference": "39ab8fcf5a51ce4b85ca97c7a7d033eb12831124"
|
"reference": "84a527db05647743d50373e0ec53a152f2cde568"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/doctrine/lexer/zipball/39ab8fcf5a51ce4b85ca97c7a7d033eb12831124",
|
"url": "https://api.github.com/repos/doctrine/lexer/zipball/84a527db05647743d50373e0ec53a152f2cde568",
|
||||||
"reference": "39ab8fcf5a51ce4b85ca97c7a7d033eb12831124",
|
"reference": "84a527db05647743d50373e0ec53a152f2cde568",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
"doctrine/deprecations": "^1.0",
|
"php": "^8.1"
|
||||||
"php": "^7.1 || ^8.0"
|
|
||||||
},
|
},
|
||||||
"require-dev": {
|
"require-dev": {
|
||||||
"doctrine/coding-standard": "^9 || ^10",
|
"doctrine/coding-standard": "^10",
|
||||||
"phpstan/phpstan": "^1.3",
|
"phpstan/phpstan": "^1.9",
|
||||||
"phpunit/phpunit": "^7.5 || ^8.5 || ^9.5",
|
"phpunit/phpunit": "^9.5",
|
||||||
"psalm/plugin-phpunit": "^0.18.3",
|
"psalm/plugin-phpunit": "^0.18.3",
|
||||||
"vimeo/psalm": "^4.11 || ^5.0"
|
"vimeo/psalm": "^5.0"
|
||||||
},
|
},
|
||||||
"type": "library",
|
"type": "library",
|
||||||
"autoload": {
|
"autoload": {
|
||||||
@ -1566,7 +1565,7 @@
|
|||||||
],
|
],
|
||||||
"support": {
|
"support": {
|
||||||
"issues": "https://github.com/doctrine/lexer/issues",
|
"issues": "https://github.com/doctrine/lexer/issues",
|
||||||
"source": "https://github.com/doctrine/lexer/tree/2.1.0"
|
"source": "https://github.com/doctrine/lexer/tree/3.0.0"
|
||||||
},
|
},
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
@ -1582,7 +1581,7 @@
|
|||||||
"type": "tidelift"
|
"type": "tidelift"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"time": "2022-12-14T08:49:07+00:00"
|
"time": "2022-12-15T16:57:16+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "dragonmantank/cron-expression",
|
"name": "dragonmantank/cron-expression",
|
||||||
@ -13599,85 +13598,6 @@
|
|||||||
],
|
],
|
||||||
"time": "2022-02-25T21:32:43+00:00"
|
"time": "2022-02-25T21:32:43+00:00"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"name": "darkaonline/l5-swagger",
|
|
||||||
"version": "8.1.0",
|
|
||||||
"source": {
|
|
||||||
"type": "git",
|
|
||||||
"url": "https://github.com/DarkaOnLine/L5-Swagger.git",
|
|
||||||
"reference": "aab46bf494ba52dcdd7d259ce178ad33d0327d04"
|
|
||||||
},
|
|
||||||
"dist": {
|
|
||||||
"type": "zip",
|
|
||||||
"url": "https://api.github.com/repos/DarkaOnLine/L5-Swagger/zipball/aab46bf494ba52dcdd7d259ce178ad33d0327d04",
|
|
||||||
"reference": "aab46bf494ba52dcdd7d259ce178ad33d0327d04",
|
|
||||||
"shasum": ""
|
|
||||||
},
|
|
||||||
"require": {
|
|
||||||
"ext-json": "*",
|
|
||||||
"laravel/framework": ">=8.40.0 || ^7.0",
|
|
||||||
"php": "^7.2 || ^8.0",
|
|
||||||
"swagger-api/swagger-ui": "^3.0",
|
|
||||||
"symfony/yaml": "^5.0",
|
|
||||||
"zircote/swagger-php": "3.*"
|
|
||||||
},
|
|
||||||
"require-dev": {
|
|
||||||
"mockery/mockery": "1.*",
|
|
||||||
"orchestra/testbench": "6.* || 5.*",
|
|
||||||
"php-coveralls/php-coveralls": "^2.0",
|
|
||||||
"phpunit/phpunit": "9.*"
|
|
||||||
},
|
|
||||||
"type": "library",
|
|
||||||
"extra": {
|
|
||||||
"laravel": {
|
|
||||||
"providers": [
|
|
||||||
"L5Swagger\\L5SwaggerServiceProvider"
|
|
||||||
],
|
|
||||||
"aliases": {
|
|
||||||
"L5Swagger": "L5Swagger\\L5SwaggerFacade"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"autoload": {
|
|
||||||
"files": [
|
|
||||||
"src/helpers.php"
|
|
||||||
],
|
|
||||||
"psr-4": {
|
|
||||||
"L5Swagger\\": "src"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"notification-url": "https://packagist.org/downloads/",
|
|
||||||
"license": [
|
|
||||||
"MIT"
|
|
||||||
],
|
|
||||||
"authors": [
|
|
||||||
{
|
|
||||||
"name": "Darius Matulionis",
|
|
||||||
"email": "darius@matulionis.lt"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"description": "OpenApi or Swagger integration to Laravel",
|
|
||||||
"keywords": [
|
|
||||||
"api",
|
|
||||||
"documentation",
|
|
||||||
"laravel",
|
|
||||||
"openapi",
|
|
||||||
"specification",
|
|
||||||
"swagger",
|
|
||||||
"ui"
|
|
||||||
],
|
|
||||||
"support": {
|
|
||||||
"issues": "https://github.com/DarkaOnLine/L5-Swagger/issues",
|
|
||||||
"source": "https://github.com/DarkaOnLine/L5-Swagger/tree/8.1.0"
|
|
||||||
},
|
|
||||||
"funding": [
|
|
||||||
{
|
|
||||||
"url": "https://github.com/DarkaOnLine",
|
|
||||||
"type": "github"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"time": "2022-01-07T09:08:44+00:00"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"name": "dnoegel/php-xdg-base-dir",
|
"name": "dnoegel/php-xdg-base-dir",
|
||||||
"version": "v0.1.1",
|
"version": "v0.1.1",
|
||||||
@ -13717,30 +13637,30 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "doctrine/annotations",
|
"name": "doctrine/annotations",
|
||||||
"version": "1.14.3",
|
"version": "2.0.1",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/doctrine/annotations.git",
|
"url": "https://github.com/doctrine/annotations.git",
|
||||||
"reference": "fb0d71a7393298a7b232cbf4c8b1f73f3ec3d5af"
|
"reference": "e157ef3f3124bbf6fe7ce0ffd109e8a8ef284e7f"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/doctrine/annotations/zipball/fb0d71a7393298a7b232cbf4c8b1f73f3ec3d5af",
|
"url": "https://api.github.com/repos/doctrine/annotations/zipball/e157ef3f3124bbf6fe7ce0ffd109e8a8ef284e7f",
|
||||||
"reference": "fb0d71a7393298a7b232cbf4c8b1f73f3ec3d5af",
|
"reference": "e157ef3f3124bbf6fe7ce0ffd109e8a8ef284e7f",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
"doctrine/lexer": "^1 || ^2",
|
"doctrine/lexer": "^2 || ^3",
|
||||||
"ext-tokenizer": "*",
|
"ext-tokenizer": "*",
|
||||||
"php": "^7.1 || ^8.0",
|
"php": "^7.2 || ^8.0",
|
||||||
"psr/cache": "^1 || ^2 || ^3"
|
"psr/cache": "^1 || ^2 || ^3"
|
||||||
},
|
},
|
||||||
"require-dev": {
|
"require-dev": {
|
||||||
"doctrine/cache": "^1.11 || ^2.0",
|
"doctrine/cache": "^2.0",
|
||||||
"doctrine/coding-standard": "^9 || ^10",
|
"doctrine/coding-standard": "^10",
|
||||||
"phpstan/phpstan": "~1.4.10 || ^1.8.0",
|
"phpstan/phpstan": "^1.8.0",
|
||||||
"phpunit/phpunit": "^7.5 || ^8.5 || ^9.5",
|
"phpunit/phpunit": "^7.5 || ^8.5 || ^9.5",
|
||||||
"symfony/cache": "^4.4 || ^5.4 || ^6",
|
"symfony/cache": "^5.4 || ^6",
|
||||||
"vimeo/psalm": "^4.10"
|
"vimeo/psalm": "^4.10"
|
||||||
},
|
},
|
||||||
"suggest": {
|
"suggest": {
|
||||||
@ -13787,9 +13707,9 @@
|
|||||||
],
|
],
|
||||||
"support": {
|
"support": {
|
||||||
"issues": "https://github.com/doctrine/annotations/issues",
|
"issues": "https://github.com/doctrine/annotations/issues",
|
||||||
"source": "https://github.com/doctrine/annotations/tree/1.14.3"
|
"source": "https://github.com/doctrine/annotations/tree/2.0.1"
|
||||||
},
|
},
|
||||||
"time": "2023-02-01T09:20:38+00:00"
|
"time": "2023-02-02T22:02:53+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "doctrine/instantiator",
|
"name": "doctrine/instantiator",
|
||||||
@ -14096,27 +14016,27 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "friendsofphp/php-cs-fixer",
|
"name": "friendsofphp/php-cs-fixer",
|
||||||
"version": "v3.14.2",
|
"version": "v3.14.4",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer.git",
|
"url": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer.git",
|
||||||
"reference": "14f0541651841b63640e7aafad041ad55dc7aa88"
|
"reference": "1b3d9dba63d93b8a202c31e824748218781eae6b"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/PHP-CS-Fixer/PHP-CS-Fixer/zipball/14f0541651841b63640e7aafad041ad55dc7aa88",
|
"url": "https://api.github.com/repos/PHP-CS-Fixer/PHP-CS-Fixer/zipball/1b3d9dba63d93b8a202c31e824748218781eae6b",
|
||||||
"reference": "14f0541651841b63640e7aafad041ad55dc7aa88",
|
"reference": "1b3d9dba63d93b8a202c31e824748218781eae6b",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
"composer/semver": "^3.3",
|
"composer/semver": "^3.3",
|
||||||
"composer/xdebug-handler": "^3.0.3",
|
"composer/xdebug-handler": "^3.0.3",
|
||||||
"doctrine/annotations": "^1.14.2 || ^2",
|
"doctrine/annotations": "^2",
|
||||||
"doctrine/lexer": "^2",
|
"doctrine/lexer": "^2 || ^3",
|
||||||
"ext-json": "*",
|
"ext-json": "*",
|
||||||
"ext-tokenizer": "*",
|
"ext-tokenizer": "*",
|
||||||
"php": "^7.4 || ^8.0",
|
"php": "^7.4 || ^8.0",
|
||||||
"sebastian/diff": "^4.0",
|
"sebastian/diff": "^4.0 || ^5.0",
|
||||||
"symfony/console": "^5.4 || ^6.0",
|
"symfony/console": "^5.4 || ^6.0",
|
||||||
"symfony/event-dispatcher": "^5.4 || ^6.0",
|
"symfony/event-dispatcher": "^5.4 || ^6.0",
|
||||||
"symfony/filesystem": "^5.4 || ^6.0",
|
"symfony/filesystem": "^5.4 || ^6.0",
|
||||||
@ -14174,7 +14094,7 @@
|
|||||||
"description": "A tool to automatically fix PHP code style",
|
"description": "A tool to automatically fix PHP code style",
|
||||||
"support": {
|
"support": {
|
||||||
"issues": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/issues",
|
"issues": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/issues",
|
||||||
"source": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/tree/v3.14.2"
|
"source": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/tree/v3.14.4"
|
||||||
},
|
},
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
@ -14182,7 +14102,7 @@
|
|||||||
"type": "github"
|
"type": "github"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"time": "2023-01-29T23:47:01+00:00"
|
"time": "2023-02-09T21:49:13+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "hamcrest/hamcrest-php",
|
"name": "hamcrest/hamcrest-php",
|
||||||
@ -16836,67 +16756,6 @@
|
|||||||
],
|
],
|
||||||
"time": "2023-01-03T19:28:04+00:00"
|
"time": "2023-01-03T19:28:04+00:00"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"name": "swagger-api/swagger-ui",
|
|
||||||
"version": "v3.52.5",
|
|
||||||
"source": {
|
|
||||||
"type": "git",
|
|
||||||
"url": "https://github.com/swagger-api/swagger-ui.git",
|
|
||||||
"reference": "f1ad60dc92e7edb0898583e16c3e66fe3e9eada2"
|
|
||||||
},
|
|
||||||
"dist": {
|
|
||||||
"type": "zip",
|
|
||||||
"url": "https://api.github.com/repos/swagger-api/swagger-ui/zipball/f1ad60dc92e7edb0898583e16c3e66fe3e9eada2",
|
|
||||||
"reference": "f1ad60dc92e7edb0898583e16c3e66fe3e9eada2",
|
|
||||||
"shasum": ""
|
|
||||||
},
|
|
||||||
"type": "library",
|
|
||||||
"notification-url": "https://packagist.org/downloads/",
|
|
||||||
"license": [
|
|
||||||
"Apache-2.0"
|
|
||||||
],
|
|
||||||
"authors": [
|
|
||||||
{
|
|
||||||
"name": "Anna Bodnia",
|
|
||||||
"email": "anna.bodnia@gmail.com"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Buu Nguyen",
|
|
||||||
"email": "buunguyen@gmail.com"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Josh Ponelat",
|
|
||||||
"email": "jponelat@gmail.com"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Kyle Shockey",
|
|
||||||
"email": "kyleshockey1@gmail.com"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Robert Barnwell",
|
|
||||||
"email": "robert@robertismy.name"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Sahar Jafari",
|
|
||||||
"email": "shr.jafari@gmail.com"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"description": " Swagger UI is a collection of HTML, Javascript, and CSS assets that dynamically generate beautiful documentation from a Swagger-compliant API.",
|
|
||||||
"homepage": "http://swagger.io",
|
|
||||||
"keywords": [
|
|
||||||
"api",
|
|
||||||
"documentation",
|
|
||||||
"openapi",
|
|
||||||
"specification",
|
|
||||||
"swagger",
|
|
||||||
"ui"
|
|
||||||
],
|
|
||||||
"support": {
|
|
||||||
"issues": "https://github.com/swagger-api/swagger-ui/issues",
|
|
||||||
"source": "https://github.com/swagger-api/swagger-ui/tree/v3.52.5"
|
|
||||||
},
|
|
||||||
"time": "2021-10-14T14:25:14+00:00"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"name": "symfony/polyfill-php81",
|
"name": "symfony/polyfill-php81",
|
||||||
"version": "v1.27.0",
|
"version": "v1.27.0",
|
||||||
@ -17038,81 +16897,6 @@
|
|||||||
],
|
],
|
||||||
"time": "2023-02-14T08:44:56+00:00"
|
"time": "2023-02-14T08:44:56+00:00"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"name": "symfony/yaml",
|
|
||||||
"version": "v5.4.21",
|
|
||||||
"source": {
|
|
||||||
"type": "git",
|
|
||||||
"url": "https://github.com/symfony/yaml.git",
|
|
||||||
"reference": "3713e20d93e46e681e51605d213027e48dab3469"
|
|
||||||
},
|
|
||||||
"dist": {
|
|
||||||
"type": "zip",
|
|
||||||
"url": "https://api.github.com/repos/symfony/yaml/zipball/3713e20d93e46e681e51605d213027e48dab3469",
|
|
||||||
"reference": "3713e20d93e46e681e51605d213027e48dab3469",
|
|
||||||
"shasum": ""
|
|
||||||
},
|
|
||||||
"require": {
|
|
||||||
"php": ">=7.2.5",
|
|
||||||
"symfony/deprecation-contracts": "^2.1|^3",
|
|
||||||
"symfony/polyfill-ctype": "^1.8"
|
|
||||||
},
|
|
||||||
"conflict": {
|
|
||||||
"symfony/console": "<5.3"
|
|
||||||
},
|
|
||||||
"require-dev": {
|
|
||||||
"symfony/console": "^5.3|^6.0"
|
|
||||||
},
|
|
||||||
"suggest": {
|
|
||||||
"symfony/console": "For validating YAML files using the lint command"
|
|
||||||
},
|
|
||||||
"bin": [
|
|
||||||
"Resources/bin/yaml-lint"
|
|
||||||
],
|
|
||||||
"type": "library",
|
|
||||||
"autoload": {
|
|
||||||
"psr-4": {
|
|
||||||
"Symfony\\Component\\Yaml\\": ""
|
|
||||||
},
|
|
||||||
"exclude-from-classmap": [
|
|
||||||
"/Tests/"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"notification-url": "https://packagist.org/downloads/",
|
|
||||||
"license": [
|
|
||||||
"MIT"
|
|
||||||
],
|
|
||||||
"authors": [
|
|
||||||
{
|
|
||||||
"name": "Fabien Potencier",
|
|
||||||
"email": "fabien@symfony.com"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Symfony Community",
|
|
||||||
"homepage": "https://symfony.com/contributors"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"description": "Loads and dumps YAML files",
|
|
||||||
"homepage": "https://symfony.com",
|
|
||||||
"support": {
|
|
||||||
"source": "https://github.com/symfony/yaml/tree/v5.4.21"
|
|
||||||
},
|
|
||||||
"funding": [
|
|
||||||
{
|
|
||||||
"url": "https://symfony.com/sponsor",
|
|
||||||
"type": "custom"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"url": "https://github.com/fabpot",
|
|
||||||
"type": "github"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
|
|
||||||
"type": "tidelift"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"time": "2023-02-21T19:46:44+00:00"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"name": "theseer/tokenizer",
|
"name": "theseer/tokenizer",
|
||||||
"version": "1.2.1",
|
"version": "1.2.1",
|
||||||
@ -17321,79 +17105,6 @@
|
|||||||
},
|
},
|
||||||
"abandoned": "symfony/filesystem",
|
"abandoned": "symfony/filesystem",
|
||||||
"time": "2015-12-17T08:42:14+00:00"
|
"time": "2015-12-17T08:42:14+00:00"
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "zircote/swagger-php",
|
|
||||||
"version": "3.3.7",
|
|
||||||
"source": {
|
|
||||||
"type": "git",
|
|
||||||
"url": "https://github.com/zircote/swagger-php.git",
|
|
||||||
"reference": "e8c3bb316e385e93a0c7e8b2aa0681079244c381"
|
|
||||||
},
|
|
||||||
"dist": {
|
|
||||||
"type": "zip",
|
|
||||||
"url": "https://api.github.com/repos/zircote/swagger-php/zipball/e8c3bb316e385e93a0c7e8b2aa0681079244c381",
|
|
||||||
"reference": "e8c3bb316e385e93a0c7e8b2aa0681079244c381",
|
|
||||||
"shasum": ""
|
|
||||||
},
|
|
||||||
"require": {
|
|
||||||
"doctrine/annotations": "^1.7",
|
|
||||||
"ext-json": "*",
|
|
||||||
"php": ">=7.2",
|
|
||||||
"psr/log": "^1.1 || ^2.0 || ^3.0",
|
|
||||||
"symfony/finder": ">=2.2",
|
|
||||||
"symfony/yaml": ">=3.3"
|
|
||||||
},
|
|
||||||
"require-dev": {
|
|
||||||
"composer/package-versions-deprecated": "1.11.99.2",
|
|
||||||
"friendsofphp/php-cs-fixer": "^2.17 || ^3.0",
|
|
||||||
"phpunit/phpunit": ">=8.5.14"
|
|
||||||
},
|
|
||||||
"bin": [
|
|
||||||
"bin/openapi"
|
|
||||||
],
|
|
||||||
"type": "library",
|
|
||||||
"autoload": {
|
|
||||||
"files": [
|
|
||||||
"src/functions.php"
|
|
||||||
],
|
|
||||||
"psr-4": {
|
|
||||||
"OpenApi\\": "src"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"notification-url": "https://packagist.org/downloads/",
|
|
||||||
"license": [
|
|
||||||
"Apache-2.0"
|
|
||||||
],
|
|
||||||
"authors": [
|
|
||||||
{
|
|
||||||
"name": "Robert Allen",
|
|
||||||
"email": "zircote@gmail.com"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Bob Fanger",
|
|
||||||
"email": "bfanger@gmail.com",
|
|
||||||
"homepage": "https://bfanger.nl"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Martin Rademacher",
|
|
||||||
"email": "mano@radebatz.net",
|
|
||||||
"homepage": "https://radebatz.net"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"description": "swagger-php - Generate interactive documentation for your RESTful API using phpdoc annotations",
|
|
||||||
"homepage": "https://github.com/zircote/swagger-php/",
|
|
||||||
"keywords": [
|
|
||||||
"api",
|
|
||||||
"json",
|
|
||||||
"rest",
|
|
||||||
"service discovery"
|
|
||||||
],
|
|
||||||
"support": {
|
|
||||||
"issues": "https://github.com/zircote/swagger-php/issues",
|
|
||||||
"source": "https://github.com/zircote/swagger-php/tree/3.3.7"
|
|
||||||
},
|
|
||||||
"time": "2023-01-03T21:17:10+00:00"
|
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"aliases": [],
|
"aliases": [],
|
||||||
|
@ -39,7 +39,7 @@ return [
|
|||||||
| @see https://imdhemy.com/laravel-iap-docs/docs/credentials/google-play
|
| @see https://imdhemy.com/laravel-iap-docs/docs/credentials/google-play
|
||||||
|
|
|
|
||||||
*/
|
*/
|
||||||
'google_application_credentials' => env('GOOGLE_APPLICATION_CREDENTIALS',false),
|
'google_application_credentials' => env('GOOGLE_APPLICATION_CREDENTIALS', base_path('VERSION.txt')),
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|--------------------------------------------------------------------------
|
|--------------------------------------------------------------------------
|
||||||
|
@ -213,5 +213,4 @@ return [
|
|||||||
'config_name' => env("YODLEE_CONFIG_NAME", false),
|
'config_name' => env("YODLEE_CONFIG_NAME", false),
|
||||||
],
|
],
|
||||||
'licenses' => env('LICENSES',false),
|
'licenses' => env('LICENSES',false),
|
||||||
'google_service_account' => env('GOOGLE_APPLICATION_CREDENTIALS', false),
|
|
||||||
];
|
];
|
||||||
|
Loading…
Reference in New Issue
Block a user