2023-01-15 04:44:23 +01:00
|
|
|
<?php
|
|
|
|
/**
|
|
|
|
* Invoice Ninja (https://invoiceninja.com).
|
|
|
|
*
|
|
|
|
* @link https://github.com/invoiceninja/invoiceninja source repository
|
|
|
|
*
|
2023-01-28 23:21:40 +01:00
|
|
|
* @copyright Copyright (c) 2023. Invoice Ninja LLC (https://invoiceninja.com)
|
2023-01-15 04:44:23 +01:00
|
|
|
*
|
|
|
|
* @license https://www.elastic.co/licensing/elastic-license
|
|
|
|
*/
|
|
|
|
|
|
|
|
namespace App\Services\Email;
|
|
|
|
|
2023-03-08 10:30:45 +01:00
|
|
|
use App\Models\Task;
|
2023-01-15 04:44:23 +01:00
|
|
|
use App\Utils\Ninja;
|
2023-03-08 10:59:39 +01:00
|
|
|
use App\Models\Quote;
|
|
|
|
use App\Models\Credit;
|
2023-03-08 10:30:45 +01:00
|
|
|
use App\Models\Account;
|
|
|
|
use App\Models\Expense;
|
|
|
|
use App\Models\Invoice;
|
2023-03-08 10:59:39 +01:00
|
|
|
use App\Models\PurchaseOrder;
|
2023-03-08 10:47:13 +01:00
|
|
|
use App\Jobs\Invoice\CreateUbl;
|
2023-03-08 10:30:45 +01:00
|
|
|
use App\Utils\Traits\MakesHash;
|
2023-03-08 10:47:13 +01:00
|
|
|
use Illuminate\Mail\Attachment;
|
2023-03-08 10:59:39 +01:00
|
|
|
use App\Jobs\Entity\CreateRawPdf;
|
2023-01-15 04:44:23 +01:00
|
|
|
use Illuminate\Support\Facades\App;
|
2023-03-08 10:30:45 +01:00
|
|
|
use Illuminate\Mail\Mailables\Address;
|
|
|
|
use App\DataMapper\EmailTemplateDefaults;
|
2023-01-15 04:44:23 +01:00
|
|
|
use League\CommonMark\CommonMarkConverter;
|
|
|
|
|
|
|
|
class EmailDefaults
|
|
|
|
{
|
2023-03-08 10:30:45 +01:00
|
|
|
use MakesHash;
|
2023-01-15 11:16:10 +01:00
|
|
|
/**
|
|
|
|
* The settings object for this email
|
|
|
|
* @var CompanySettings $settings
|
|
|
|
*/
|
2023-01-15 04:44:23 +01:00
|
|
|
protected $settings;
|
|
|
|
|
2023-01-15 11:16:10 +01:00
|
|
|
/**
|
|
|
|
* The HTML / Template to use for this email
|
|
|
|
* @var string $template
|
|
|
|
*/
|
2023-01-15 04:44:23 +01:00
|
|
|
private string $template;
|
|
|
|
|
2023-01-15 11:16:10 +01:00
|
|
|
/**
|
|
|
|
* The locale to use for
|
|
|
|
* translations for this email
|
|
|
|
*/
|
2023-01-15 04:44:23 +01:00
|
|
|
private string $locale;
|
|
|
|
|
2023-01-15 11:16:10 +01:00
|
|
|
/**
|
2023-03-06 09:07:25 +01:00
|
|
|
* @param Email $email job class
|
|
|
|
* @param EmailObject $email_object the email object class
|
2023-01-15 11:16:10 +01:00
|
|
|
*/
|
2023-03-06 09:07:25 +01:00
|
|
|
public function __construct(protected Email $email)
|
2023-02-16 02:36:09 +01:00
|
|
|
{
|
|
|
|
}
|
2023-01-15 04:44:23 +01:00
|
|
|
|
2023-01-15 11:16:10 +01:00
|
|
|
/**
|
2023-02-16 02:36:09 +01:00
|
|
|
* Entry point for generating
|
2023-01-15 11:16:10 +01:00
|
|
|
* the defaults for the email object
|
2023-02-16 02:36:09 +01:00
|
|
|
*
|
2023-01-15 11:16:10 +01:00
|
|
|
* @return EmailObject $email_object The email object
|
|
|
|
*/
|
2023-01-15 04:44:23 +01:00
|
|
|
public function run()
|
|
|
|
{
|
2023-03-06 09:07:25 +01:00
|
|
|
$this->settings = $this->email->email_object->settings;
|
2023-01-15 04:44:23 +01:00
|
|
|
|
2023-03-06 09:07:25 +01:00
|
|
|
$this->setLocale() //
|
2023-01-15 04:44:23 +01:00
|
|
|
->setFrom()
|
2023-03-07 12:36:50 +01:00
|
|
|
->setTo()
|
2023-01-15 04:44:23 +01:00
|
|
|
->setTemplate()
|
|
|
|
->setBody()
|
|
|
|
->setSubject()
|
|
|
|
->setReplyTo()
|
|
|
|
->setBcc()
|
|
|
|
->setAttachments()
|
|
|
|
->setVariables();
|
|
|
|
|
2023-03-06 09:07:25 +01:00
|
|
|
return $this->email->email_object;
|
2023-01-15 04:44:23 +01:00
|
|
|
}
|
|
|
|
|
2023-01-15 11:16:10 +01:00
|
|
|
/**
|
|
|
|
* Sets the locale
|
|
|
|
*/
|
2023-01-15 04:44:23 +01:00
|
|
|
private function setLocale(): self
|
|
|
|
{
|
2023-03-06 09:07:25 +01:00
|
|
|
if ($this->email->email_object->client) {
|
|
|
|
$this->locale = $this->email->email_object->client->locale();
|
|
|
|
} elseif ($this->email->email_object->vendor) {
|
|
|
|
$this->locale = $this->email->email_object->vendor->locale();
|
2023-02-16 02:36:09 +01:00
|
|
|
} else {
|
2023-03-06 09:07:25 +01:00
|
|
|
$this->locale = $this->email->company->locale();
|
2023-02-16 02:36:09 +01:00
|
|
|
}
|
2023-01-15 04:44:23 +01:00
|
|
|
|
|
|
|
App::setLocale($this->locale);
|
|
|
|
App::forgetInstance('translator');
|
|
|
|
$t = app('translator');
|
|
|
|
$t->replace(Ninja::transformTranslations($this->settings));
|
|
|
|
|
|
|
|
return $this;
|
|
|
|
}
|
|
|
|
|
2023-01-15 11:16:10 +01:00
|
|
|
/**
|
|
|
|
* Sets the template
|
|
|
|
*/
|
2023-01-15 04:44:23 +01:00
|
|
|
private function setTemplate(): self
|
|
|
|
{
|
2023-03-06 09:07:25 +01:00
|
|
|
$this->template = $this->email->email_object->settings->email_style;
|
2023-01-15 04:44:23 +01:00
|
|
|
|
2023-03-06 09:07:25 +01:00
|
|
|
match ($this->email->email_object->settings->email_style) {
|
2023-01-15 04:44:23 +01:00
|
|
|
'light' => $this->template = 'email.template.client',
|
|
|
|
'dark' => $this->template = 'email.template.client',
|
|
|
|
'custom' => $this->template = 'email.template.custom',
|
|
|
|
default => $this->template = 'email.template.client',
|
2023-02-16 02:36:09 +01:00
|
|
|
};
|
2023-01-15 04:44:23 +01:00
|
|
|
|
2023-03-06 09:07:25 +01:00
|
|
|
$this->email->email_object->html_template = $this->template;
|
2023-01-15 04:44:23 +01:00
|
|
|
|
|
|
|
return $this;
|
|
|
|
}
|
|
|
|
|
2023-01-15 11:16:10 +01:00
|
|
|
/**
|
|
|
|
* Sets the FROM address
|
|
|
|
*/
|
2023-01-15 04:44:23 +01:00
|
|
|
private function setFrom(): self
|
2023-02-16 02:36:09 +01:00
|
|
|
{
|
2023-03-06 09:07:25 +01:00
|
|
|
if (Ninja::isHosted() && $this->email->email_object->settings->email_sending_method == 'default') {
|
|
|
|
$this->email->email_object->from = new Address(config('mail.from.address'), $this->email->company->owner()->name());
|
2023-01-22 07:46:56 +01:00
|
|
|
return $this;
|
|
|
|
}
|
|
|
|
|
2023-03-06 09:07:25 +01:00
|
|
|
if ($this->email->email_object->from) {
|
2023-01-15 04:44:23 +01:00
|
|
|
return $this;
|
2023-02-16 02:36:09 +01:00
|
|
|
}
|
2023-01-15 04:44:23 +01:00
|
|
|
|
2023-03-06 09:07:25 +01:00
|
|
|
$this->email->email_object->from = new Address($this->email->company->owner()->email, $this->email->company->owner()->name());
|
2023-01-15 04:44:23 +01:00
|
|
|
|
|
|
|
return $this;
|
|
|
|
}
|
|
|
|
|
2023-03-07 12:36:50 +01:00
|
|
|
/**
|
|
|
|
* Sets the To address
|
|
|
|
*/
|
|
|
|
private function setTo(): self
|
|
|
|
{
|
|
|
|
if ($this->email->email_object->to) {
|
|
|
|
return $this;
|
|
|
|
}
|
|
|
|
|
|
|
|
$this->email->email_object->to = [new Address($this->email->email_object->contact->email, $this->email->email_object->contact->present()->name())];
|
|
|
|
|
|
|
|
return $this;
|
|
|
|
}
|
|
|
|
|
2023-02-16 02:36:09 +01:00
|
|
|
/**
|
2023-01-15 11:16:10 +01:00
|
|
|
* Sets the body of the email
|
|
|
|
*/
|
2023-01-15 04:44:23 +01:00
|
|
|
private function setBody(): self
|
2023-03-08 10:30:45 +01:00
|
|
|
{
|
|
|
|
|
2023-03-06 09:07:25 +01:00
|
|
|
if ($this->email->email_object->body) {
|
2023-03-08 07:20:40 +01:00
|
|
|
// A Custom Message has been set in the email screen.
|
|
|
|
return $this;
|
2023-03-07 13:17:03 +01:00
|
|
|
} elseif (strlen($this->email->email_object->settings?->{$this->email->email_object->email_template_body}) > 3) {
|
2023-03-08 07:20:40 +01:00
|
|
|
// A body has been saved in the settings.
|
2023-03-07 13:17:03 +01:00
|
|
|
$this->email->email_object->body = $this->email->email_object->settings?->{$this->email->email_object->email_template_body};
|
2023-02-16 02:36:09 +01:00
|
|
|
} else {
|
2023-03-08 07:20:40 +01:00
|
|
|
// Default template to be used
|
2023-03-06 09:07:25 +01:00
|
|
|
$this->email->email_object->body = EmailTemplateDefaults::getDefaultTemplate($this->email->email_object->email_template_body, $this->locale);
|
2023-01-15 04:44:23 +01:00
|
|
|
}
|
|
|
|
|
2023-02-16 02:36:09 +01:00
|
|
|
if ($this->template == 'email.template.custom') {
|
2023-03-06 09:07:25 +01:00
|
|
|
$this->email->email_object->body = (str_replace('$body', $this->email->email_object->body, $this->email->email_object->settings->email_style_custom));
|
2023-01-15 04:44:23 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
return $this;
|
|
|
|
}
|
|
|
|
|
2023-01-15 11:16:10 +01:00
|
|
|
/**
|
|
|
|
* Sets the subject of the email
|
|
|
|
*/
|
2023-01-15 04:44:23 +01:00
|
|
|
private function setSubject(): self
|
|
|
|
{
|
2023-03-06 09:07:25 +01:00
|
|
|
if ($this->email->email_object->subject) { //where the user updates the subject from the UI
|
2023-01-15 04:44:23 +01:00
|
|
|
return $this;
|
2023-03-08 08:33:42 +01:00
|
|
|
} elseif (strlen($this->email->email_object->settings?->{$this->email->email_object->email_template_subject}) > 3) {
|
2023-03-07 13:17:03 +01:00
|
|
|
$this->email->email_object->subject = $this->email->email_object->settings?->{$this->email->email_object->email_template_subject};
|
2023-02-16 02:36:09 +01:00
|
|
|
} else {
|
2023-03-06 09:07:25 +01:00
|
|
|
$this->email->email_object->subject = EmailTemplateDefaults::getDefaultTemplate($this->email->email_object->email_template_subject, $this->locale);
|
2023-02-16 02:36:09 +01:00
|
|
|
}
|
2023-01-15 04:44:23 +01:00
|
|
|
|
|
|
|
return $this;
|
|
|
|
}
|
|
|
|
|
2023-01-15 11:16:10 +01:00
|
|
|
/**
|
|
|
|
* Sets the reply to of the email
|
|
|
|
*/
|
|
|
|
private function setReplyTo(): self
|
2023-01-15 04:44:23 +01:00
|
|
|
{
|
2023-03-06 09:07:25 +01:00
|
|
|
$reply_to_email = str_contains($this->email->email_object->settings->reply_to_email, "@") ? $this->email->email_object->settings->reply_to_email : $this->email->company->owner()->email;
|
2023-01-15 04:44:23 +01:00
|
|
|
|
2023-03-06 09:07:25 +01:00
|
|
|
$reply_to_name = strlen($this->email->email_object->settings->reply_to_name) > 3 ? $this->email->email_object->settings->reply_to_name : $this->email->company->owner()->present()->name();
|
2023-01-15 11:16:10 +01:00
|
|
|
|
2023-03-06 09:07:25 +01:00
|
|
|
$this->email->email_object->reply_to = array_merge($this->email->email_object->reply_to, [new Address($reply_to_email, $reply_to_name)]);
|
2023-01-15 04:44:23 +01:00
|
|
|
|
|
|
|
return $this;
|
|
|
|
}
|
|
|
|
|
2023-01-15 11:16:10 +01:00
|
|
|
/**
|
2023-02-16 02:36:09 +01:00
|
|
|
* Replaces the template placeholders
|
2023-01-15 11:16:10 +01:00
|
|
|
* with variable values.
|
|
|
|
*/
|
|
|
|
public function setVariables(): self
|
2023-01-15 04:44:23 +01:00
|
|
|
{
|
2023-03-06 09:07:25 +01:00
|
|
|
$this->email->email_object->body = strtr($this->email->email_object->body, $this->email->email_object->variables);
|
2023-01-15 11:16:10 +01:00
|
|
|
|
2023-03-06 09:07:25 +01:00
|
|
|
$this->email->email_object->subject = strtr($this->email->email_object->subject, $this->email->email_object->variables);
|
2023-01-15 04:44:23 +01:00
|
|
|
|
2023-02-16 02:36:09 +01:00
|
|
|
if ($this->template != 'custom') {
|
2023-03-06 09:07:25 +01:00
|
|
|
$this->email->email_object->body = $this->parseMarkdownToHtml($this->email->email_object->body);
|
2023-02-16 02:36:09 +01:00
|
|
|
}
|
2023-03-08 10:30:45 +01:00
|
|
|
|
2023-01-15 04:44:23 +01:00
|
|
|
return $this;
|
|
|
|
}
|
|
|
|
|
2023-01-15 11:16:10 +01:00
|
|
|
/**
|
|
|
|
* Sets the BCC of the email
|
|
|
|
*/
|
2023-01-15 04:44:23 +01:00
|
|
|
private function setBcc(): self
|
|
|
|
{
|
|
|
|
$bccs = [];
|
|
|
|
$bcc_array = [];
|
|
|
|
|
2023-03-06 09:07:25 +01:00
|
|
|
if (strlen($this->email->email_object->settings->bcc_email) > 1) {
|
|
|
|
if (Ninja::isHosted() && $this->email->company->account->isPaid()) {
|
|
|
|
$bccs = array_slice(explode(',', str_replace(' ', '', $this->email->email_object->settings->bcc_email)), 0, 2);
|
2023-02-16 02:36:09 +01:00
|
|
|
} elseif (Ninja::isSelfHost()) {
|
2023-03-06 09:07:25 +01:00
|
|
|
$bccs = (explode(',', str_replace(' ', '', $this->email->email_object->settings->bcc_email)));
|
2023-01-15 04:44:23 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-02-16 02:36:09 +01:00
|
|
|
foreach ($bccs as $bcc) {
|
2023-01-15 04:44:23 +01:00
|
|
|
$bcc_array[] = new Address($bcc);
|
|
|
|
}
|
|
|
|
|
2023-03-06 09:07:25 +01:00
|
|
|
$this->email->email_object->bcc = array_merge($this->email->email_object->bcc, $bcc_array);
|
2023-01-15 04:44:23 +01:00
|
|
|
|
|
|
|
return $this;
|
|
|
|
}
|
|
|
|
|
2023-01-15 11:16:10 +01:00
|
|
|
/**
|
|
|
|
* Sets the CC of the email
|
|
|
|
* @todo at some point....
|
|
|
|
*/
|
2023-01-15 04:44:23 +01:00
|
|
|
private function buildCc()
|
|
|
|
{
|
|
|
|
return [
|
|
|
|
|
|
|
|
];
|
|
|
|
}
|
|
|
|
|
2023-02-16 02:36:09 +01:00
|
|
|
/**
|
2023-01-15 11:16:10 +01:00
|
|
|
* Sets the attachments for the email
|
|
|
|
*
|
2023-02-16 02:36:09 +01:00
|
|
|
* Note that we base64 encode these, as they
|
2023-01-15 11:16:10 +01:00
|
|
|
* sometimes may not survive serialization.
|
|
|
|
*
|
|
|
|
* We decode these in the Mailable later
|
|
|
|
*/
|
2023-01-15 04:44:23 +01:00
|
|
|
private function setAttachments(): self
|
|
|
|
{
|
2023-03-08 07:56:34 +01:00
|
|
|
$documents = [];
|
2023-01-15 04:44:23 +01:00
|
|
|
|
2023-03-08 10:59:39 +01:00
|
|
|
/* Return early if the user cannot attach documents */
|
2023-03-08 10:47:13 +01:00
|
|
|
if (!$this->email->email_object->settings->document_email_attachment || !$this->email->company->account->hasFeature(Account::FEATURE_DOCUMENTS))
|
2023-03-08 10:30:45 +01:00
|
|
|
return $this;
|
|
|
|
|
2023-03-08 10:59:39 +01:00
|
|
|
if($this->email->email_object->entity instanceof PurchaseOrder) {
|
|
|
|
|
|
|
|
}
|
|
|
|
else if($this->email->email_object->settings->pdf_email_attachment &&
|
|
|
|
($this->email->email_object->entity instanceof Invoice ||
|
|
|
|
$this->email->email_object->entity instanceof Quote ||
|
|
|
|
$this->email->email_object->entity instanceof Credit)) {
|
|
|
|
|
|
|
|
$pdf = ((new CreateRawPdf($this->email->email_object->invitation, $this->email->company->db))->handle());
|
|
|
|
|
|
|
|
$this->email->email_object->attachments = array_merge($this->email->email_object->attachments, [['file' => base64_encode($pdf), 'name' => $this->email->email_object->entity->numberFormatter().'.pdf']]);
|
|
|
|
|
|
|
|
}
|
|
|
|
/* Company Documents */
|
2023-03-08 10:30:45 +01:00
|
|
|
$this->email->email_object->documents = array_merge($this->email->email_object->documents, $this->email->company->documents->pluck('id')->toArray());
|
2023-03-08 07:56:34 +01:00
|
|
|
|
2023-03-08 10:30:45 +01:00
|
|
|
if ($this->email->email_object->entity?->documents) {
|
|
|
|
$this->email->email_object->documents = array_merge($this->email->email_object->documents, $this->email->email_object->entity->documents->pluck('id')->toArray());
|
2023-01-15 04:44:23 +01:00
|
|
|
}
|
|
|
|
|
2023-03-08 10:30:45 +01:00
|
|
|
if ($this->email->email_object->entity instanceof Invoice && $this->email->email_object->entity->recurring_id != null){
|
|
|
|
$this->email->email_object->documents = array_merge($this->email->email_object->documents, $this->email->email_object->entity->recurring_invoice->documents->pluck('id')->toArray());
|
|
|
|
}
|
|
|
|
|
|
|
|
if ($this->email->email_object->entity instanceof Invoice) {
|
|
|
|
|
|
|
|
$expense_ids = [];
|
|
|
|
$task_ids = [];
|
|
|
|
|
|
|
|
foreach ($this->email->email_object->entity->line_items as $item)
|
|
|
|
{
|
|
|
|
|
|
|
|
if (property_exists($item, 'expense_id')) {
|
|
|
|
$expense_ids[] = $item->expense_id;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (property_exists($item, 'task_id')) {
|
|
|
|
$task_ids[] = $item->task_id;
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
if (count($expense_ids) > 0) {
|
|
|
|
|
|
|
|
Expense::whereIn('id', $this->transformKeys($expense_ids))
|
|
|
|
->where('invoice_documents', 1)
|
|
|
|
->cursor()
|
|
|
|
->each(function ($expense) {
|
|
|
|
$this->email->email_object->documents = array_merge($this->email->email_object->documents, $expense->documents->pluck('id')->toArray());
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
if (count($task_ids) > 0 && $this->email->company->invoice_task_documents) {
|
|
|
|
Task::whereIn('id', $this->transformKeys($task_ids))
|
|
|
|
->cursor()
|
|
|
|
->each(function ($task) {
|
|
|
|
$this->email->email_object->documents = array_merge($this->email->email_object->documents, $task->documents->pluck('id')->toArray());
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
2023-01-15 04:44:23 +01:00
|
|
|
|
2023-03-08 10:47:13 +01:00
|
|
|
|
|
|
|
if ($this->email->email_object->entity instanceof Invoice && $this->email->email_object->settings->ubl_email_attachment) {
|
|
|
|
$ubl_string = (new CreateUbl($this->email->email_object->entity))->handle();
|
|
|
|
|
|
|
|
if ($ubl_string) {
|
2023-03-08 10:59:39 +01:00
|
|
|
$this->email->email_object->attachments = array_merge($this->email->email_object->attachments, [['file' => base64_encode($ubl_string), 'name' => $this->email->email_object->entity->getFileName('xml')]]);
|
2023-03-08 10:47:13 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-01-15 04:44:23 +01:00
|
|
|
return $this;
|
2023-03-08 10:30:45 +01:00
|
|
|
|
2023-01-15 04:44:23 +01:00
|
|
|
}
|
|
|
|
|
2023-01-15 11:16:10 +01:00
|
|
|
/**
|
|
|
|
* Sets the headers for the email
|
|
|
|
*/
|
2023-01-15 04:44:23 +01:00
|
|
|
private function setHeaders(): self
|
|
|
|
{
|
2023-03-06 09:07:25 +01:00
|
|
|
if ($this->email->email_object->invitation_key) {
|
|
|
|
$this->email->email_object->headers = array_merge($this->email->email_object->headers, ['x-invitation-key' => $this->email->email_object->invitation_key]);
|
2023-02-16 02:36:09 +01:00
|
|
|
}
|
2023-01-15 04:44:23 +01:00
|
|
|
|
|
|
|
return $this;
|
|
|
|
}
|
|
|
|
|
2023-01-15 11:16:10 +01:00
|
|
|
/**
|
|
|
|
* Converts any markdown to HTML in the email
|
2023-02-16 02:36:09 +01:00
|
|
|
*
|
2023-01-15 11:16:10 +01:00
|
|
|
* @param string $markdown The body to convert
|
|
|
|
* @return string The parsed markdown response
|
|
|
|
*/
|
|
|
|
private function parseMarkdownToHtml(string $markdown): ?string
|
2023-01-15 04:44:23 +01:00
|
|
|
{
|
|
|
|
$converter = new CommonMarkConverter([
|
|
|
|
'allow_unsafe_links' => false,
|
|
|
|
]);
|
|
|
|
|
|
|
|
return $converter->convert($markdown);
|
|
|
|
}
|
2023-02-16 02:36:09 +01:00
|
|
|
}
|