From 4377a0571643e77080ffcf775151970bb7157ad0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Benjamin=20Beganovi=C4=87?= Date: Wed, 17 Jan 2024 17:41:54 +0100 Subject: [PATCH 01/18] wip --- .../EmailPreferencesController.php | 45 +++++++++++++++++++ app/Services/Email/Email.php | 8 ++++ app/Utils/HtmlEngine.php | 2 + .../views/email/template/plain.blade.php | 5 +++ .../generic/email_preferences.blade.php | 37 +++++++++++++++ routes/client.php | 5 +++ 6 files changed, 102 insertions(+) create mode 100644 app/Http/Controllers/ClientPortal/EmailPreferencesController.php create mode 100644 resources/views/portal/ninja2020/generic/email_preferences.blade.php diff --git a/app/Http/Controllers/ClientPortal/EmailPreferencesController.php b/app/Http/Controllers/ClientPortal/EmailPreferencesController.php new file mode 100644 index 000000000000..247b3bba7446 --- /dev/null +++ b/app/Http/Controllers/ClientPortal/EmailPreferencesController.php @@ -0,0 +1,45 @@ +hasValidSignature()) { + abort(404); + } + + $data['recieve_emails'] = $clientContact->is_locked ? false : true; + $data['logo'] = $clientContact->company->present()->logo(); + + return $this->render('generic.email_preferences', $data); + } + + public function update(ClientContact $clientContact, Request $request): \Illuminate\Http\RedirectResponse + { + if (!$request->hasValidSignature()) { + abort(404); + } + + $clientContact->is_locked = $request->has('recieve_emails') ? false : true; + $clientContact->save(); + + return back()->with('message', ctrans('texts.updated_settings')); + } +} + diff --git a/app/Services/Email/Email.php b/app/Services/Email/Email.php index cfe56bfb2f9e..d529eedf0b5a 100644 --- a/app/Services/Email/Email.php +++ b/app/Services/Email/Email.php @@ -99,6 +99,8 @@ class Email implements ShouldQueue { MultiDB::setDb($this->company->db); + info('here 1'); + $this->setOverride() ->initModels() ->setDefaults() @@ -241,6 +243,8 @@ class Email implements ShouldQueue { $this->mailable = new EmailMailable($this->email_object); + info('here 2'); + return $this; } @@ -264,6 +268,10 @@ class Email implements ShouldQueue $mailer->mailgun_config($this->client_mailgun_secret, $this->client_mailgun_domain, $this->client_mailgun_endpoint); } + info("here 4"); + info($this->mailable->view); + info(print_r($this->mailable)); + /* Attempt the send! */ try { nlog("Using mailer => ". $this->mailer. " ". now()->toDateTimeString()); diff --git a/app/Utils/HtmlEngine.php b/app/Utils/HtmlEngine.php index d4fe57993252..232d66995bf8 100644 --- a/app/Utils/HtmlEngine.php +++ b/app/Utils/HtmlEngine.php @@ -765,6 +765,8 @@ class HtmlEngine $data[$key] = $value['value']; } + info("here 3"); + return $data; } diff --git a/resources/views/email/template/plain.blade.php b/resources/views/email/template/plain.blade.php index 270144bf7931..7ee4034ea7b4 100644 --- a/resources/views/email/template/plain.blade.php +++ b/resources/views/email/template/plain.blade.php @@ -44,6 +44,11 @@

@endif @endisset + +

Hello world

+

@isset($portal_url) {{ $portal_url }} @endisset

+

After portal

+ @if(isset($unsubscribe_link))

{{ ctrans('texts.unsubscribe') }}

@endif \ No newline at end of file diff --git a/resources/views/portal/ninja2020/generic/email_preferences.blade.php b/resources/views/portal/ninja2020/generic/email_preferences.blade.php new file mode 100644 index 000000000000..2c32ee9db0d5 --- /dev/null +++ b/resources/views/portal/ninja2020/generic/email_preferences.blade.php @@ -0,0 +1,37 @@ +@extends('portal.ninja2020.layout.clean') @section('meta_title', +ctrans('texts.preferences')) @section('body') +
+
+
+ Invoice Ninja logo +

+ {{ ctrans('texts.email_settings') }} +

+ +
+ @csrf @method('put') + + + +
+ +
+
+
+
+
+@stop diff --git a/routes/client.php b/routes/client.php index 870b3514abdb..95956d4e5df4 100644 --- a/routes/client.php +++ b/routes/client.php @@ -1,5 +1,6 @@ middleware('throttle:404'); + + +Route::get('client/email_preferences/{clientContact}', [EmailPreferencesController::class, 'index'])->name('client.email_preferences'); +Route::put('client/email_preferences/{clientContact}', [EmailPreferencesController::class, 'update']); From 5fba841ce68d0fe81dfdea6ae28135ebd54efce1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Benjamin=20Beganovi=C4=87?= Date: Thu, 18 Jan 2024 16:52:19 +0100 Subject: [PATCH 02/18] Add email notification for client unsubscribe --- .../ClientPortal/EmailPreferencesController.php | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/app/Http/Controllers/ClientPortal/EmailPreferencesController.php b/app/Http/Controllers/ClientPortal/EmailPreferencesController.php index 247b3bba7446..e8400553ef5b 100644 --- a/app/Http/Controllers/ClientPortal/EmailPreferencesController.php +++ b/app/Http/Controllers/ClientPortal/EmailPreferencesController.php @@ -13,6 +13,10 @@ namespace App\Http\Controllers\ClientPortal; use App\Http\Controllers\Controller; +use App\Jobs\Mail\NinjaMailer; +use App\Jobs\Mail\NinjaMailerJob; +use App\Jobs\Mail\NinjaMailerObject; +use App\Mail\Admin\ClientUnsubscribedObject; use App\Models\ClientContact; use Illuminate\Http\Request; @@ -39,6 +43,16 @@ class EmailPreferencesController extends Controller $clientContact->is_locked = $request->has('recieve_emails') ? false : true; $clientContact->save(); + if ($clientContact->is_locked) { + $nmo = new NinjaMailerObject(); + $nmo->mailable = new NinjaMailer((new ClientUnsubscribedObject($clientContact, $clientContact->company))->build()); + $nmo->company = $clientContact->company; + $nmo->to_user = $clientContact->company->owner(); + $nmo->settings = $clientContact->company->settings; + + (new NinjaMailerJob($nmo))->handle(); + } + return back()->with('message', ctrans('texts.updated_settings')); } } From b0a45f49d2eb6ddb7464cf6e9ba064e287959074 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Benjamin=20Beganovi=C4=87?= Date: Thu, 18 Jan 2024 16:52:25 +0100 Subject: [PATCH 03/18] Add URL dependency for email preferences --- app/Mail/TemplateEmail.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/Mail/TemplateEmail.php b/app/Mail/TemplateEmail.php index 1530ce9a4f8a..2abd4d52e71b 100644 --- a/app/Mail/TemplateEmail.php +++ b/app/Mail/TemplateEmail.php @@ -18,6 +18,7 @@ use App\Services\PdfMaker\Designs\Utilities\DesignHelpers; use App\Utils\HtmlEngine; use App\Utils\Ninja; use Illuminate\Mail\Mailable; +use Illuminate\Support\Facades\URL; class TemplateEmail extends Mailable { @@ -138,6 +139,7 @@ class TemplateEmail extends Mailable 'whitelabel' => $this->client->user->account->isPaid() ? true : false, 'logo' => $this->company->present()->logo($settings), 'links' => $this->build_email->getAttachmentLinks(), + 'email_preferences' => URL::signedRoute('client.email_preferences', ['invitation_key' => $this->invitation->key, 'clientContact' => $this->contact->hashed_id]), ]); foreach ($this->build_email->getAttachments() as $file) { From c4ab87ade5585dae70550abf8b9d0733602f5b8d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Benjamin=20Beganovi=C4=87?= Date: Thu, 18 Jan 2024 16:52:31 +0100 Subject: [PATCH 04/18] Add ClientUnsubscribedObject class for admin email --- app/Mail/Admin/ClientUnsubscribedObject.php | 55 +++++++++++++++++++++ 1 file changed, 55 insertions(+) create mode 100644 app/Mail/Admin/ClientUnsubscribedObject.php diff --git a/app/Mail/Admin/ClientUnsubscribedObject.php b/app/Mail/Admin/ClientUnsubscribedObject.php new file mode 100644 index 000000000000..e884840acb82 --- /dev/null +++ b/app/Mail/Admin/ClientUnsubscribedObject.php @@ -0,0 +1,55 @@ +company->getLocale()); + /* Set customized translations _NOW_ */ + $t->replace(Ninja::transformTranslations($this->company->settings)); + + $data = [ + 'title' => ctrans('texts.client_unsubscribed'), + 'message' => ctrans('texts.client_unsubscribed_help', ['client' => $this->contact->present()->name()]), + 'url' => $this->contact->client->portalUrl(false), + 'button' => ctrans('texts.view_client'), + 'signature' => $this->company->settings->email_signature, + 'settings' => $this->company->settings, + 'logo' => $this->company->present()->logo(), + ]; + + $mail_obj = new \stdClass(); + $mail_obj->subject = ctrans('texts.client_unsubscribed'); + $mail_obj->data = $data; + $mail_obj->markdown = 'email.admin.generic'; + $mail_obj->tag = $this->company->company_key; + + return $mail_obj; + } +} From d5d73b4a13f23a38d657ce5ecfd241071d5e36c7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Benjamin=20Beganovi=C4=87?= Date: Thu, 18 Jan 2024 16:52:38 +0100 Subject: [PATCH 05/18] Remove debug info statements from Email class --- app/Services/Email/Email.php | 8 -------- 1 file changed, 8 deletions(-) diff --git a/app/Services/Email/Email.php b/app/Services/Email/Email.php index d529eedf0b5a..cfe56bfb2f9e 100644 --- a/app/Services/Email/Email.php +++ b/app/Services/Email/Email.php @@ -99,8 +99,6 @@ class Email implements ShouldQueue { MultiDB::setDb($this->company->db); - info('here 1'); - $this->setOverride() ->initModels() ->setDefaults() @@ -243,8 +241,6 @@ class Email implements ShouldQueue { $this->mailable = new EmailMailable($this->email_object); - info('here 2'); - return $this; } @@ -268,10 +264,6 @@ class Email implements ShouldQueue $mailer->mailgun_config($this->client_mailgun_secret, $this->client_mailgun_domain, $this->client_mailgun_endpoint); } - info("here 4"); - info($this->mailable->view); - info(print_r($this->mailable)); - /* Attempt the send! */ try { nlog("Using mailer => ". $this->mailer. " ". now()->toDateTimeString()); From e2cbe85cc0ce81bc6cd965deeb1074e2930427b6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Benjamin=20Beganovi=C4=87?= Date: Thu, 18 Jan 2024 16:52:43 +0100 Subject: [PATCH 06/18] Remove unnecessary debug statement in HtmlEngine.php --- app/Utils/HtmlEngine.php | 2 -- 1 file changed, 2 deletions(-) diff --git a/app/Utils/HtmlEngine.php b/app/Utils/HtmlEngine.php index 232d66995bf8..d4fe57993252 100644 --- a/app/Utils/HtmlEngine.php +++ b/app/Utils/HtmlEngine.php @@ -765,8 +765,6 @@ class HtmlEngine $data[$key] = $value['value']; } - info("here 3"); - return $data; } From 0b2e17b9d2cfd7fc922a890d0f8b4267b8fdedd2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Benjamin=20Beganovi=C4=87?= Date: Thu, 18 Jan 2024 16:52:48 +0100 Subject: [PATCH 07/18] Add client_unsubscribed message to texts.php --- lang/en/texts.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lang/en/texts.php b/lang/en/texts.php index b7df2cdddbbd..d8625e588a5c 100644 --- a/lang/en/texts.php +++ b/lang/en/texts.php @@ -5213,6 +5213,8 @@ $lang = array( 'nordigen_requisition_body' => 'Access to bank account feeds has expired as set in End User Agreement.

Please log into Invoice Ninja and re-authenticate with your banks to continue receiving transactions.', 'participant' => 'Participant', 'participant_name' => 'Participant name', + 'client_unsubscribed' => 'Client changed e-mail preferences', + 'client_unsubscribed_help' => 'Client :client changed preferences and unsubscribed from e-mails.', ); return $lang; From c68b1c07c77368fea17aeab6caed7fefd38d07da Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Benjamin=20Beganovi=C4=87?= Date: Thu, 18 Jan 2024 16:52:53 +0100 Subject: [PATCH 08/18] Add email preferences link to client email template --- resources/views/email/template/client.blade.php | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/resources/views/email/template/client.blade.php b/resources/views/email/template/client.blade.php index 4d9462b2b370..7a4177cdc2e9 100644 --- a/resources/views/email/template/client.blade.php +++ b/resources/views/email/template/client.blade.php @@ -163,7 +163,7 @@ {{ $slot ?? '' }} {!! $body ?? '' !!} - + + + @isset($email_preferences) + + + + + + @endisset From 9fee6ed6da93fa3e59b5a4a6da637ac0c88c099f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Benjamin=20Beganovi=C4=87?= Date: Thu, 18 Jan 2024 16:52:57 +0100 Subject: [PATCH 09/18] Add email preferences link to dark email template --- resources/views/email/template/dark.blade.php | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/resources/views/email/template/dark.blade.php b/resources/views/email/template/dark.blade.php index c96087dd12f5..faaf2b30acce 100644 --- a/resources/views/email/template/dark.blade.php +++ b/resources/views/email/template/dark.blade.php @@ -14,4 +14,12 @@

@endif +@isset($email_preferences) +

+ + {{ ctrans('texts.email_preferences') }} + +

+@endif + @endcomponent From ca15f284cd81bc6d1fa561363523ebc609469ec2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Benjamin=20Beganovi=C4=87?= Date: Thu, 18 Jan 2024 16:53:01 +0100 Subject: [PATCH 10/18] Add email preferences link to light email template --- resources/views/email/template/light.blade.php | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/resources/views/email/template/light.blade.php b/resources/views/email/template/light.blade.php index 43b495cf4452..9a82f9c9e21a 100644 --- a/resources/views/email/template/light.blade.php +++ b/resources/views/email/template/light.blade.php @@ -14,4 +14,12 @@

@endif +@isset($email_preferences) +

+ + {{ ctrans('texts.email_preferences') }} + +

+@endif + @endcomponent From 9f671a294785b54acbcf5d0aed8dfa213aa8242d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Benjamin=20Beganovi=C4=87?= Date: Thu, 18 Jan 2024 16:53:04 +0100 Subject: [PATCH 11/18] Add email preferences link and update unsubscribe link --- resources/views/email/template/plain.blade.php | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/resources/views/email/template/plain.blade.php b/resources/views/email/template/plain.blade.php index 7ee4034ea7b4..1ae5fe70b167 100644 --- a/resources/views/email/template/plain.blade.php +++ b/resources/views/email/template/plain.blade.php @@ -45,9 +45,12 @@ @endif @endisset -

Hello world

-

@isset($portal_url) {{ $portal_url }} @endisset

-

After portal

+@if(isset($email_preferences)) +

+ {{ ctrans('texts.email_preferences') }} +

+@endif + @if(isset($unsubscribe_link))

{{ ctrans('texts.unsubscribe') }}

From 4328413a15420d37cf67018475262b3d56d50c52 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Benjamin=20Beganovi=C4=87?= Date: Thu, 18 Jan 2024 16:53:08 +0100 Subject: [PATCH 12/18] Fix route middleware for email preferences --- routes/client.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/routes/client.php b/routes/client.php index 95956d4e5df4..eff978f740b0 100644 --- a/routes/client.php +++ b/routes/client.php @@ -161,6 +161,6 @@ Route::fallback(function () { })->middleware('throttle:404'); - -Route::get('client/email_preferences/{clientContact}', [EmailPreferencesController::class, 'index'])->name('client.email_preferences'); -Route::put('client/email_preferences/{clientContact}', [EmailPreferencesController::class, 'update']); +// Fix me: Move into invite_db middleware group. +Route::get('client/email_preferences/{clientContact}/{invitation_key}', [EmailPreferencesController::class, 'index'])->name('client.email_preferences'); +Route::put('client/email_preferences/{clientContact}/{invitation_key}', [EmailPreferencesController::class, 'update']); From f6402f87a559a7f193cc9dfb71593186cd1db729 Mon Sep 17 00:00:00 2001 From: David Bomba Date: Fri, 19 Jan 2024 13:45:24 +1100 Subject: [PATCH 13/18] Improvements for unsubscribe links --- .../EmailPreferencesController.php | 36 +++++++++---------- app/Mail/TemplateEmail.php | 2 +- app/Models/CreditInvitation.php | 5 +++ app/Models/InvoiceInvitation.php | 5 +++ app/Models/PurchaseOrderInvitation.php | 5 +++ app/Models/QuoteInvitation.php | 5 +++ app/Models/RecurringInvoiceInvitation.php | 6 ++++ lang/en/texts.php | 1 + .../views/email/template/client.blade.php | 2 +- .../generic/email_preferences.blade.php | 14 ++++---- routes/client.php | 5 +-- 11 files changed, 57 insertions(+), 29 deletions(-) diff --git a/app/Http/Controllers/ClientPortal/EmailPreferencesController.php b/app/Http/Controllers/ClientPortal/EmailPreferencesController.php index e8400553ef5b..5ed1311b0e3c 100644 --- a/app/Http/Controllers/ClientPortal/EmailPreferencesController.php +++ b/app/Http/Controllers/ClientPortal/EmailPreferencesController.php @@ -19,38 +19,38 @@ use App\Jobs\Mail\NinjaMailerObject; use App\Mail\Admin\ClientUnsubscribedObject; use App\Models\ClientContact; use Illuminate\Http\Request; +use Illuminate\Support\Str; class EmailPreferencesController extends Controller { - public function index(ClientContact $clientContact, Request $request): \Illuminate\View\View + public function index(string $entity, string $invitation_key, Request $request): \Illuminate\View\View { - if (!$request->hasValidSignature()) { - abort(404); - } + $class = "\\App\\Models\\".ucfirst(Str::camel($entity)).'Invitation'; + $invitation = $class::where('key', $invitation_key)->firstOrFail(); - $data['recieve_emails'] = $clientContact->is_locked ? false : true; - $data['logo'] = $clientContact->company->present()->logo(); + $data['receive_emails'] = $invitation->contact->is_locked ? false : true; + $data['company'] = $invitation->company; return $this->render('generic.email_preferences', $data); } - public function update(ClientContact $clientContact, Request $request): \Illuminate\Http\RedirectResponse + public function update(string $entity, string $invitation_key, Request $request): \Illuminate\Http\RedirectResponse { - if (!$request->hasValidSignature()) { - abort(404); - } + + $class = "\\App\\Models\\" . ucfirst(Str::camel($entity)) . 'Invitation'; + $invitation = $class::withTrashed()->where('key', $invitation_key)->firstOrFail(); - $clientContact->is_locked = $request->has('recieve_emails') ? false : true; - $clientContact->save(); + $invitation->contact->is_locked = $request->has('receive_emails') ? false : true; + $invitation->contact->push(); - if ($clientContact->is_locked) { + if ($invitation->contact->is_locked) { $nmo = new NinjaMailerObject(); - $nmo->mailable = new NinjaMailer((new ClientUnsubscribedObject($clientContact, $clientContact->company))->build()); - $nmo->company = $clientContact->company; - $nmo->to_user = $clientContact->company->owner(); - $nmo->settings = $clientContact->company->settings; + $nmo->mailable = new NinjaMailer((new ClientUnsubscribedObject($invitation->contact, $invitation->contact->company))->build()); + $nmo->company = $invitation->contact->company; + $nmo->to_user = $invitation->contact->company->owner(); + $nmo->settings = $invitation->contact->company->settings; - (new NinjaMailerJob($nmo))->handle(); + NinjaMailerJob::dispatch($nmo); } return back()->with('message', ctrans('texts.updated_settings')); diff --git a/app/Mail/TemplateEmail.php b/app/Mail/TemplateEmail.php index 2abd4d52e71b..f9f4b1562a21 100644 --- a/app/Mail/TemplateEmail.php +++ b/app/Mail/TemplateEmail.php @@ -139,7 +139,7 @@ class TemplateEmail extends Mailable 'whitelabel' => $this->client->user->account->isPaid() ? true : false, 'logo' => $this->company->present()->logo($settings), 'links' => $this->build_email->getAttachmentLinks(), - 'email_preferences' => URL::signedRoute('client.email_preferences', ['invitation_key' => $this->invitation->key, 'clientContact' => $this->contact->hashed_id]), + 'email_preferences' => URL::signedRoute('client.email_preferences', ['entity' => $this->invitation->getEntityString(), 'invitation_key' => $this->invitation->key]), ]); foreach ($this->build_email->getAttachments() as $file) { diff --git a/app/Models/CreditInvitation.php b/app/Models/CreditInvitation.php index 545585bcc5e3..83656270280f 100644 --- a/app/Models/CreditInvitation.php +++ b/app/Models/CreditInvitation.php @@ -97,6 +97,11 @@ class CreditInvitation extends BaseModel return self::class; } + public function getEntityString(): string + { + return 'credit'; + } + public function entityType() { return Credit::class; diff --git a/app/Models/InvoiceInvitation.php b/app/Models/InvoiceInvitation.php index 2f5c6c08a1df..58c191fc4a41 100644 --- a/app/Models/InvoiceInvitation.php +++ b/app/Models/InvoiceInvitation.php @@ -97,6 +97,11 @@ class InvoiceInvitation extends BaseModel return self::class; } + public function getEntityString(): string + { + return 'invoice'; + } + public function entityType() { return Invoice::class; diff --git a/app/Models/PurchaseOrderInvitation.php b/app/Models/PurchaseOrderInvitation.php index a1702ea6b926..7584256f72c9 100644 --- a/app/Models/PurchaseOrderInvitation.php +++ b/app/Models/PurchaseOrderInvitation.php @@ -97,6 +97,11 @@ class PurchaseOrderInvitation extends BaseModel return self::class; } + public function getEntityString(): string + { + return 'purchase_order'; + } + public function entityType() { return PurchaseOrder::class; diff --git a/app/Models/QuoteInvitation.php b/app/Models/QuoteInvitation.php index 65aef2e2556a..e1c3c726973c 100644 --- a/app/Models/QuoteInvitation.php +++ b/app/Models/QuoteInvitation.php @@ -78,6 +78,11 @@ class QuoteInvitation extends BaseModel return self::class; } + public function getEntityString(): string + { + return 'quote'; + } + public function entityType() { return Quote::class; diff --git a/app/Models/RecurringInvoiceInvitation.php b/app/Models/RecurringInvoiceInvitation.php index 34928a155c1d..a5fe7868db4e 100644 --- a/app/Models/RecurringInvoiceInvitation.php +++ b/app/Models/RecurringInvoiceInvitation.php @@ -91,6 +91,12 @@ class RecurringInvoiceInvitation extends BaseModel return self::class; } + + public function getEntityString(): string + { + return 'recurring_invoice'; + } + public function entityType() { return RecurringInvoice::class; diff --git a/lang/en/texts.php b/lang/en/texts.php index d8625e588a5c..4a0f3c5c20aa 100644 --- a/lang/en/texts.php +++ b/lang/en/texts.php @@ -5215,6 +5215,7 @@ $lang = array( 'participant_name' => 'Participant name', 'client_unsubscribed' => 'Client changed e-mail preferences', 'client_unsubscribed_help' => 'Client :client changed preferences and unsubscribed from e-mails.', + 'resubscribe' => 'Resubscribe', ); return $lang; diff --git a/resources/views/email/template/client.blade.php b/resources/views/email/template/client.blade.php index 7a4177cdc2e9..2986b35345fb 100644 --- a/resources/views/email/template/client.blade.php +++ b/resources/views/email/template/client.blade.php @@ -240,7 +240,7 @@ style="padding-top: 10px;padding-bottom: 10px; background-color: #242424; border: 1px solid #c2c2c2; border-top-color: #242424; border-bottom-color: #242424;">

- {{ ctrans('texts.email_preferences') }} + {{ ctrans('texts.unsubscribe') }}

diff --git a/resources/views/portal/ninja2020/generic/email_preferences.blade.php b/resources/views/portal/ninja2020/generic/email_preferences.blade.php index 2c32ee9db0d5..492c42e2fce5 100644 --- a/resources/views/portal/ninja2020/generic/email_preferences.blade.php +++ b/resources/views/portal/ninja2020/generic/email_preferences.blade.php @@ -4,9 +4,9 @@ ctrans('texts.preferences')) @section('body')
Invoice Ninja logo

{{ ctrans('texts.email_settings') }} @@ -15,13 +15,13 @@ ctrans('texts.preferences')) @section('body')
@csrf @method('put') -

From 27dd844f7928b22a0411ecc06db016b43474df97 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Benjamin=20Beganovi=C4=87?= Date: Fri, 19 Jan 2024 15:20:27 +0100 Subject: [PATCH 16/18] Add email preferences link to EmailMailable class --- app/Services/Email/EmailMailable.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/app/Services/Email/EmailMailable.php b/app/Services/Email/EmailMailable.php index 2599d7d30161..ada43bab730d 100644 --- a/app/Services/Email/EmailMailable.php +++ b/app/Services/Email/EmailMailable.php @@ -77,6 +77,9 @@ class EmailMailable extends Mailable 'company' => $this->email_object->company, 'greeting' => '', 'links' => array_merge($this->email_object->links, $links->toArray()), + 'email_preferences' => $this->email_object->invitation + ? URL::signedRoute('client.email_preferences', ['entity' => $this->email_object->invitation->getEntityString(), 'invitation_key' => $this->email_object->invitation->key]) + : false, ] ); } From 6052e4418615e2a965a1d0f10b8d33e8c9984c92 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Benjamin=20Beganovi=C4=87?= Date: Fri, 19 Jan 2024 15:20:31 +0100 Subject: [PATCH 17/18] Add subscribe and unsubscribe help messages --- lang/en/texts.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lang/en/texts.php b/lang/en/texts.php index 4a0f3c5c20aa..4fc584147e40 100644 --- a/lang/en/texts.php +++ b/lang/en/texts.php @@ -5216,6 +5216,9 @@ $lang = array( 'client_unsubscribed' => 'Client changed e-mail preferences', 'client_unsubscribed_help' => 'Client :client changed preferences and unsubscribed from e-mails.', 'resubscribe' => 'Resubscribe', + 'subscribe' => 'Subscribe', + 'subscribe_help' => 'You are currently subscribed and will continue to receive email communications.', + 'unsubscribe_help' => 'You are currently not subscribed, and therefore, will not receive emails at this time.', ); return $lang; From 48374cf009eec5a73e48bdd297aea4d346e30b52 Mon Sep 17 00:00:00 2001 From: David Bomba Date: Mon, 22 Jan 2024 10:52:30 +1100 Subject: [PATCH 18/18] Updates for unsubscribe --- .../EmailPreferencesController.php | 15 ++++---- app/Mail/Admin/ClientUnsubscribedObject.php | 9 +++-- app/Models/ClientContact.php | 35 +++++++++++++------ lang/en/texts.php | 4 +-- 4 files changed, 41 insertions(+), 22 deletions(-) diff --git a/app/Http/Controllers/ClientPortal/EmailPreferencesController.php b/app/Http/Controllers/ClientPortal/EmailPreferencesController.php index f7b1417aa516..6fd5063756aa 100644 --- a/app/Http/Controllers/ClientPortal/EmailPreferencesController.php +++ b/app/Http/Controllers/ClientPortal/EmailPreferencesController.php @@ -12,14 +12,15 @@ namespace App\Http\Controllers\ClientPortal; -use App\Http\Controllers\Controller; +use Illuminate\Support\Str; +use Illuminate\Http\Request; +use App\Models\ClientContact; use App\Jobs\Mail\NinjaMailer; use App\Jobs\Mail\NinjaMailerJob; +use App\Http\Controllers\Controller; use App\Jobs\Mail\NinjaMailerObject; +use Illuminate\Support\Facades\Cache; use App\Mail\Admin\ClientUnsubscribedObject; -use App\Models\ClientContact; -use Illuminate\Http\Request; -use Illuminate\Support\Str; class EmailPreferencesController extends Controller { @@ -42,14 +43,16 @@ class EmailPreferencesController extends Controller $invitation->contact->is_locked = $request->action === 'unsubscribe' ? true : false; $invitation->contact->push(); - if ($invitation->contact->is_locked) { + if ($invitation->contact->is_locked && !Cache::has("unsubscribe_notitfication_suppression:{$invitation_key}")) { $nmo = new NinjaMailerObject(); - $nmo->mailable = new NinjaMailer((new ClientUnsubscribedObject($invitation->contact, $invitation->contact->company))->build()); + $nmo->mailable = new NinjaMailer((new ClientUnsubscribedObject($invitation->contact, $invitation->contact->company, $invitation->contact->company->owner()->company_users()->first()->portalType() ?? true))->build()); $nmo->company = $invitation->contact->company; $nmo->to_user = $invitation->contact->company->owner(); $nmo->settings = $invitation->contact->company->settings; NinjaMailerJob::dispatch($nmo); + + Cache::put("unsubscribe_notitfication_suppression:{$invitation_key}", true, 3600); } return back()->with('message', ctrans('texts.updated_settings')); diff --git a/app/Mail/Admin/ClientUnsubscribedObject.php b/app/Mail/Admin/ClientUnsubscribedObject.php index e884840acb82..eb0190ab161f 100644 --- a/app/Mail/Admin/ClientUnsubscribedObject.php +++ b/app/Mail/Admin/ClientUnsubscribedObject.php @@ -20,7 +20,8 @@ class ClientUnsubscribedObject { public function __construct( public ClientContact $contact, - public Company $company + public Company $company, + private bool $use_react_link = false ) { } @@ -36,12 +37,13 @@ class ClientUnsubscribedObject $data = [ 'title' => ctrans('texts.client_unsubscribed'), - 'message' => ctrans('texts.client_unsubscribed_help', ['client' => $this->contact->present()->name()]), - 'url' => $this->contact->client->portalUrl(false), + 'content' => ctrans('texts.client_unsubscribed_help', ['client' => $this->contact->present()->name()]), + 'url' => $this->contact->getAdminLink($this->use_react_link), 'button' => ctrans('texts.view_client'), 'signature' => $this->company->settings->email_signature, 'settings' => $this->company->settings, 'logo' => $this->company->present()->logo(), + 'text_body' => "\n\n".ctrans('texts.client_unsubscribed_help', ['client' => $this->contact->present()->name()])."\n\n", ]; $mail_obj = new \stdClass(); @@ -49,6 +51,7 @@ class ClientUnsubscribedObject $mail_obj->data = $data; $mail_obj->markdown = 'email.admin.generic'; $mail_obj->tag = $this->company->company_key; + $mail_obj->text_view = 'email.template.text'; return $mail_obj; } diff --git a/app/Models/ClientContact.php b/app/Models/ClientContact.php index 0acdfb09cf42..2f5a948feae8 100644 --- a/app/Models/ClientContact.php +++ b/app/Models/ClientContact.php @@ -11,22 +11,23 @@ namespace App\Models; -use App\Jobs\Mail\NinjaMailer; -use App\Jobs\Mail\NinjaMailerJob; -use App\Jobs\Mail\NinjaMailerObject; -use App\Mail\ClientContact\ClientContactResetPasswordObject; -use App\Models\Presenters\ClientContactPresenter; use App\Utils\Ninja; +use Illuminate\Support\Str; +use App\Jobs\Mail\NinjaMailer; use App\Utils\Traits\AppSetup; use App\Utils\Traits\MakesHash; -use Illuminate\Contracts\Translation\HasLocalePreference; -use Illuminate\Database\Eloquent\Factories\HasFactory; -use Illuminate\Database\Eloquent\Model; -use Illuminate\Database\Eloquent\SoftDeletes; -use Illuminate\Foundation\Auth\User as Authenticatable; -use Illuminate\Notifications\Notifiable; +use App\Jobs\Mail\NinjaMailerJob; +use App\Jobs\Mail\NinjaMailerObject; use Illuminate\Support\Facades\Cache; +use Illuminate\Database\Eloquent\Model; +use Illuminate\Notifications\Notifiable; use Laracasts\Presenter\PresentableTrait; +use Illuminate\Database\Eloquent\SoftDeletes; +use App\Models\Presenters\ClientContactPresenter; +use Illuminate\Database\Eloquent\Factories\HasFactory; +use Illuminate\Foundation\Auth\User as Authenticatable; +use Illuminate\Contracts\Translation\HasLocalePreference; +use App\Mail\ClientContact\ClientContactResetPasswordObject; /** * Class ClientContact @@ -339,4 +340,16 @@ class ClientContact extends Authenticatable implements HasLocalePreference return ''; } } + + public function getAdminLink($use_react_link = false): string + { + return $use_react_link ? $this->getReactLink() : config('ninja.app_url'); + } + + private function getReactLink(): string + { + return config('ninja.react_url')."/#/clients/{$this->client->hashed_id}"; + } + + } diff --git a/lang/en/texts.php b/lang/en/texts.php index 4fc584147e40..ceb0e221993e 100644 --- a/lang/en/texts.php +++ b/lang/en/texts.php @@ -5213,8 +5213,8 @@ $lang = array( 'nordigen_requisition_body' => 'Access to bank account feeds has expired as set in End User Agreement.

Please log into Invoice Ninja and re-authenticate with your banks to continue receiving transactions.', 'participant' => 'Participant', 'participant_name' => 'Participant name', - 'client_unsubscribed' => 'Client changed e-mail preferences', - 'client_unsubscribed_help' => 'Client :client changed preferences and unsubscribed from e-mails.', + 'client_unsubscribed' => 'Client unsubscribed from emails.', + 'client_unsubscribed_help' => 'Client :client has unsubscribed from your e-mails. The client needs to consent to receive future emails from you.', 'resubscribe' => 'Resubscribe', 'subscribe' => 'Subscribe', 'subscribe_help' => 'You are currently subscribed and will continue to receive email communications.',