1
0
mirror of https://github.com/invoiceninja/invoiceninja.git synced 2024-11-16 16:13:20 +01:00
invoiceninja/app/Services/Email/EmailDefaults.php

398 lines
14 KiB
PHP
Raw Normal View History

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-10-26 04:57:44 +02:00
use App\DataMapper\EmailTemplateDefaults;
use App\Jobs\Entity\CreateRawPdf;
use App\Jobs\Invoice\CreateUbl;
2023-04-28 09:04:49 +02:00
use App\Models\Account;
2023-03-08 10:30:45 +01:00
use App\Models\Expense;
use App\Models\Invoice;
2023-10-26 04:57:44 +02:00
use App\Models\Task;
use App\Utils\Ninja;
2023-03-08 10:30:45 +01:00
use App\Utils\Traits\MakesHash;
2023-04-28 09:04:49 +02:00
use Illuminate\Mail\Mailables\Address;
2023-10-26 04:57:44 +02:00
use Illuminate\Support\Facades\App;
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
2023-03-08 13:16:08 +01:00
* @var \App\DataMapper\CompanySettings $settings
2023-01-15 11:16:10 +01:00
*/
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
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-04-04 08:58:01 +02: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-04-04 08:58:01 +02:00
$this->setLocale()
2023-01-15 04:44:23 +01:00
->setFrom()
2023-03-07 12:36:50 +01:00
->setTo()
->setCc()
2023-01-15 04:44:23 +01:00
->setTemplate()
->setBody()
->setSubject()
->setReplyTo()
->setBcc()
->setAttachments()
2023-03-24 00:10:35 +01:00
->setVariables()
->setHeaders();
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-03-12 06:37:00 +01:00
'plain' => $this->template = 'email.template.plain',
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') {
2023-03-13 08:30:05 +01:00
if ($this->email->company->account->isPaid() && property_exists($this->email->email_object->settings, 'email_from_name') && strlen($this->email->email_object->settings->email_from_name) > 1) {
$email_from_name = $this->email->email_object->settings->email_from_name;
} else {
$email_from_name = $this->email->company->present()->name();
}
$this->email->email_object->from = new Address(config('mail.from.address'), $email_from_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-09 21:58:15 +01:00
$this->email->email_object->from = new Address(config('mail.from.address'), config('mail.from.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-05-24 06:47:05 +02:00
if (strlen($this->email->email_object->body) > 3) {
// A Custom Message has been set in the email screen.
2023-03-07 13:17:03 +01:00
} elseif (strlen($this->email->email_object->settings?->{$this->email->email_object->email_template_body}) > 3) {
// 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 {
// 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-05-24 06:47:05 +02:00
2023-07-06 13:16:18 +02:00
$breaks = ["<br />","<br>","<br/>"];
$this->email->email_object->text_body = str_ireplace($breaks, "\r\n", $this->email->email_object->body);
$this->email->email_object->text_body = strip_tags($this->email->email_object->text_body);
$this->email->email_object->text_body = str_replace('$view_button', '$view_url', $this->email->email_object->text_body);
2023-05-24 06:47:05 +02:00
if ($this->template == 'email.template.custom') {
$this->email->email_object->body = (str_replace('$body', $this->email->email_object->body, str_replace(["\r","\n"], "", $this->email->email_object->settings->email_style_custom)));
}
2023-01-15 04:44:23 +01:00
return $this;
2023-05-24 06:47:05 +02:00
2023-01-15 04:44:23 +01:00
}
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-07-06 13:16:18 +02: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-07-06 13:16:18 +02:00
$this->email->email_object->text_body = strtr($this->email->email_object->text_body, $this->email->email_object->variables);
2023-04-04 08:58:01 +02: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-06-06 07:06:23 +02:00
//06-06-2023 ensure we do not parse markdown in custom templates
if ($this->template != 'custom' && $this->template != 'email.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, 5);
2023-10-26 04:57:44 +02:00
} else {
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-04-04 08:58:01 +02:00
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
*/
private function setCc(): self
2023-01-15 04:44:23 +01:00
{
return $this;
// return $this->email->email_object->cc;
// return [
// ];
2023-01-15 04:44:23 +01:00
}
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-12-04 12:14:20 +01:00
if (!$this->email->company->account->hasFeature(Account::FEATURE_PDF_ATTACHMENT) || $this->email->email_object->email_template_subject == 'email_subject_statement') {
2023-03-08 10:30:45 +01:00
return $this;
2023-03-08 13:16:08 +01:00
}
2023-03-08 10:30:45 +01:00
2023-03-08 13:16:08 +01:00
/** Purchase Order / Invoice / Credit / Quote PDF */
2023-11-26 08:41:42 +01:00
if ($this->email->email_object->settings->pdf_email_attachment) {
2023-10-26 03:25:56 +02:00
$pdf = ((new CreateRawPdf($this->email->email_object->invitation))->handle());
2023-03-08 10:59:39 +01:00
$this->email->email_object->attachments = array_merge($this->email->email_object->attachments, [['file' => base64_encode($pdf), 'name' => $this->email->email_object->entity->numberFormatter().'.pdf']]);
2023-03-12 06:41:43 +01:00
}
/** UBL xml file */
2023-03-12 06:52:56 +01:00
if ($this->email->email_object->settings->ubl_email_attachment && $this->email->email_object->entity instanceof Invoice) {
2023-03-12 06:41:43 +01:00
$ubl_string = (new CreateUbl($this->email->email_object->entity))->handle();
if ($ubl_string) {
$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 13:16:08 +01:00
}
2023-04-17 09:24:16 +02:00
/** E-Invoice xml file */
if ($this->email->email_object->settings->enable_e_invoice && $this->email->email_object->entity instanceof Invoice) {
2023-08-16 11:55:35 +02:00
$xml_string = $this->email->email_object->entity->service()->getEInvoice();
2023-04-28 09:04:49 +02:00
2023-10-26 04:57:44 +02:00
if($xml_string) {
2023-08-16 11:55:35 +02:00
$this->email->email_object->attachments = array_merge($this->email->email_object->attachments, [['file' => base64_encode($xml_string), 'name' => explode(".", $this->email->email_object->entity->getFileName('xml'))[0]."-e_invoice.xml"]]);
2023-10-26 04:57:44 +02:00
}
2023-04-04 08:58:01 +02:00
}
2023-03-08 10:59:39 +01:00
2023-03-18 08:24:56 +01:00
if (!$this->email->email_object->settings->document_email_attachment || !$this->email->company->account->hasFeature(Account::FEATURE_DOCUMENTS)) {
2023-03-12 06:41:43 +01:00
return $this;
2023-03-18 08:24:56 +01:00
}
2023-03-12 06:41:43 +01:00
2023-03-08 10:59:39 +01:00
/* Company Documents */
2023-09-11 02:30:02 +02:00
$this->email->email_object->documents = array_merge($this->email->email_object->documents, $this->email->company->documents()->where('is_public', true)->pluck('id')->toArray());
2023-03-08 07:56:34 +01:00
2023-03-08 13:16:08 +01:00
/** Entity Documents */
2023-03-08 10:30:45 +01:00
if ($this->email->email_object->entity?->documents) {
2023-09-11 02:30:02 +02:00
$this->email->email_object->documents = array_merge($this->email->email_object->documents, $this->email->email_object->entity->documents()->where('is_public', true)->pluck('id')->toArray());
2023-01-15 04:44:23 +01:00
}
2023-03-08 13:16:08 +01:00
/** Recurring Invoice Documents */
if ($this->email->email_object->entity instanceof Invoice && $this->email->email_object->entity->recurring_id != null) {
2023-09-11 02:30:02 +02:00
$this->email->email_object->documents = array_merge($this->email->email_object->documents, $this->email->email_object->entity->recurring_invoice->documents()->where('is_public', true)->pluck('id')->toArray());
2023-03-08 10:30:45 +01:00
}
2023-03-08 13:16:08 +01:00
/** Task / Expense Documents */
2023-03-08 10:30:45 +01:00
if ($this->email->email_object->entity instanceof Invoice) {
$expense_ids = [];
$task_ids = [];
2023-04-04 08:58:01 +02:00
2023-03-08 13:16:08 +01:00
foreach ($this->email->email_object->entity->line_items as $item) {
2023-03-08 10:30:45 +01:00
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) {
2023-08-06 09:03:12 +02:00
Expense::query()->whereIn('id', $this->transformKeys($expense_ids))
2023-03-08 10:30:45 +01:00
->where('invoice_documents', 1)
->cursor()
->each(function ($expense) {
2023-10-26 04:57:44 +02:00
$this->email->email_object->documents = array_merge($this->email->email_object->documents, $expense->documents()->where('is_public', true)->pluck('id')->toArray());
2023-03-08 10:30:45 +01:00
});
}
if (count($task_ids) > 0 && $this->email->company->invoice_task_documents) {
2023-08-06 09:03:12 +02:00
Task::query()->whereIn('id', $this->transformKeys($task_ids))
2023-03-08 10:30:45 +01:00
->cursor()
->each(function ($task) {
2023-10-26 04:57:44 +02:00
$this->email->email_object->documents = array_merge($this->email->email_object->documents, $task->documents()->where('is_public', true)->pluck('id')->toArray());
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 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) {
2023-03-24 00:10:35 +01:00
$this->email->email_object->headers = array_merge($this->email->email_object->headers, ['x-invitation' => $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
}