mirror of
https://github.com/invoiceninja/invoiceninja.git
synced 2025-05-30 22:54:32 -04:00
commit
76d159ee02
@ -0,0 +1,61 @@
|
||||
<?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\Http\Controllers\ClientPortal;
|
||||
|
||||
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;
|
||||
|
||||
class EmailPreferencesController extends Controller
|
||||
{
|
||||
public function index(string $entity, string $invitation_key, Request $request): \Illuminate\View\View
|
||||
{
|
||||
$class = "\\App\\Models\\".ucfirst(Str::camel($entity)).'Invitation';
|
||||
$invitation = $class::where('key', $invitation_key)->firstOrFail();
|
||||
|
||||
$data['receive_emails'] = $invitation->contact->is_locked ? false : true;
|
||||
$data['company'] = $invitation->company;
|
||||
|
||||
return $this->render('generic.email_preferences', $data);
|
||||
}
|
||||
|
||||
public function update(string $entity, string $invitation_key, Request $request): \Illuminate\Http\RedirectResponse
|
||||
{
|
||||
$class = "\\App\\Models\\" . ucfirst(Str::camel($entity)) . 'Invitation';
|
||||
$invitation = $class::withTrashed()->where('key', $invitation_key)->firstOrFail();
|
||||
|
||||
$invitation->contact->is_locked = $request->action === 'unsubscribe' ? true : false;
|
||||
$invitation->contact->push();
|
||||
|
||||
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, $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'));
|
||||
}
|
||||
}
|
||||
|
58
app/Mail/Admin/ClientUnsubscribedObject.php
Normal file
58
app/Mail/Admin/ClientUnsubscribedObject.php
Normal file
@ -0,0 +1,58 @@
|
||||
<?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\Mail\Admin;
|
||||
|
||||
use App\Models\ClientContact;
|
||||
use App\Models\Company;
|
||||
use App\Utils\Ninja;
|
||||
use Illuminate\Support\Facades\App;
|
||||
|
||||
class ClientUnsubscribedObject
|
||||
{
|
||||
public function __construct(
|
||||
public ClientContact $contact,
|
||||
public Company $company,
|
||||
private bool $use_react_link = false
|
||||
) {
|
||||
}
|
||||
|
||||
public function build()
|
||||
{
|
||||
App::forgetInstance('translator');
|
||||
/* Init a new copy of the translator*/
|
||||
$t = app('translator');
|
||||
/* Set the locale*/
|
||||
App::setLocale($this->company->getLocale());
|
||||
/* Set customized translations _NOW_ */
|
||||
$t->replace(Ninja::transformTranslations($this->company->settings));
|
||||
|
||||
$data = [
|
||||
'title' => ctrans('texts.client_unsubscribed'),
|
||||
'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();
|
||||
$mail_obj->subject = ctrans('texts.client_unsubscribed');
|
||||
$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;
|
||||
}
|
||||
}
|
@ -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', ['entity' => $this->invitation->getEntityString(), 'invitation_key' => $this->invitation->key]),
|
||||
]);
|
||||
|
||||
foreach ($this->build_email->getAttachments() as $file) {
|
||||
|
@ -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}";
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
@ -97,6 +97,11 @@ class CreditInvitation extends BaseModel
|
||||
return self::class;
|
||||
}
|
||||
|
||||
public function getEntityString(): string
|
||||
{
|
||||
return 'credit';
|
||||
}
|
||||
|
||||
public function entityType()
|
||||
{
|
||||
return Credit::class;
|
||||
|
@ -97,6 +97,11 @@ class InvoiceInvitation extends BaseModel
|
||||
return self::class;
|
||||
}
|
||||
|
||||
public function getEntityString(): string
|
||||
{
|
||||
return 'invoice';
|
||||
}
|
||||
|
||||
public function entityType()
|
||||
{
|
||||
return Invoice::class;
|
||||
|
@ -97,6 +97,11 @@ class PurchaseOrderInvitation extends BaseModel
|
||||
return self::class;
|
||||
}
|
||||
|
||||
public function getEntityString(): string
|
||||
{
|
||||
return 'purchase_order';
|
||||
}
|
||||
|
||||
public function entityType()
|
||||
{
|
||||
return PurchaseOrder::class;
|
||||
|
@ -78,6 +78,11 @@ class QuoteInvitation extends BaseModel
|
||||
return self::class;
|
||||
}
|
||||
|
||||
public function getEntityString(): string
|
||||
{
|
||||
return 'quote';
|
||||
}
|
||||
|
||||
public function entityType()
|
||||
{
|
||||
return Quote::class;
|
||||
|
@ -91,6 +91,12 @@ class RecurringInvoiceInvitation extends BaseModel
|
||||
return self::class;
|
||||
}
|
||||
|
||||
|
||||
public function getEntityString(): string
|
||||
{
|
||||
return 'recurring_invoice';
|
||||
}
|
||||
|
||||
public function entityType()
|
||||
{
|
||||
return RecurringInvoice::class;
|
||||
|
@ -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,
|
||||
]
|
||||
);
|
||||
}
|
||||
|
@ -5213,6 +5213,12 @@ $lang = array(
|
||||
'nordigen_requisition_body' => 'Access to bank account feeds has expired as set in End User Agreement. <br><br>Please log into Invoice Ninja and re-authenticate with your banks to continue receiving transactions.',
|
||||
'participant' => 'Participant',
|
||||
'participant_name' => 'Participant name',
|
||||
'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.',
|
||||
'unsubscribe_help' => 'You are currently not subscribed, and therefore, will not receive emails at this time.',
|
||||
);
|
||||
|
||||
return $lang;
|
||||
|
@ -163,7 +163,7 @@
|
||||
|
||||
{{ $slot ?? '' }}
|
||||
{!! $body ?? '' !!}
|
||||
|
||||
|
||||
<div>
|
||||
<a href="#"
|
||||
style="display: inline-block;background-color: {{ $primary_color }}; color: #ffffff; text-transform: uppercase;letter-spacing: 2px; text-decoration: none; font-size: 13px; font-weight: 600;">
|
||||
@ -232,6 +232,21 @@
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
@isset($email_preferences)
|
||||
<tr>
|
||||
<td bgcolor="#242424" cellpadding="20">
|
||||
<div class="dark-bg-base"
|
||||
style="padding-top: 10px;padding-bottom: 10px; background-color: #242424; border: 1px solid #c2c2c2; border-top-color: #242424; border-bottom-color: #242424;">
|
||||
<a href="{{ $email_preferences }}">
|
||||
<p style="text-align: center; color: #ffffff; font-size: 10px; font-family: Verdana, Geneva, Tahoma, sans-serif;">
|
||||
{{ ctrans('texts.unsubscribe') }}
|
||||
</p>
|
||||
</a>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
@endisset
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
|
@ -14,4 +14,12 @@
|
||||
</p>
|
||||
@endif
|
||||
|
||||
@isset($email_preferences)
|
||||
<p>
|
||||
<a href="{{ $email_preferences }}">
|
||||
{{ ctrans('texts.email_preferences') }}
|
||||
</a>
|
||||
</p>
|
||||
@endif
|
||||
|
||||
@endcomponent
|
||||
|
@ -14,4 +14,12 @@
|
||||
</p>
|
||||
@endif
|
||||
|
||||
@isset($email_preferences)
|
||||
<p>
|
||||
<a href="{{ $email_preferences }}">
|
||||
{{ ctrans('texts.email_preferences') }}
|
||||
</a>
|
||||
</p>
|
||||
@endif
|
||||
|
||||
@endcomponent
|
||||
|
@ -44,6 +44,14 @@
|
||||
</p>
|
||||
@endif
|
||||
@endisset
|
||||
|
||||
@if(isset($email_preferences))
|
||||
<p>
|
||||
<a href="{{ $email_preferences}} ">{{ ctrans('texts.email_preferences') }}</a>
|
||||
</p>
|
||||
@endif
|
||||
|
||||
|
||||
@if(isset($unsubscribe_link))
|
||||
<p><a href="{{$unsubscribe_link}}">{{ ctrans('texts.unsubscribe') }}</a></p>
|
||||
@endif
|
@ -0,0 +1,43 @@
|
||||
@extends('portal.ninja2020.layout.clean') @section('meta_title',
|
||||
ctrans('texts.preferences')) @section('body')
|
||||
<div class="flex h-screen">
|
||||
<div class="m-auto md:w-1/3 lg:w-1/5">
|
||||
<div class="flex flex-col items-center">
|
||||
<img
|
||||
src="{{ $company->present()->logo() }}"
|
||||
class="border-gray-100 h-18 pb-4"
|
||||
alt="{{ $company->present()->name() }}"
|
||||
/>
|
||||
<h1 class="text-center text-2xl mt-10">
|
||||
{{ ctrans('texts.email_preferences') }}
|
||||
</h1>
|
||||
|
||||
<form class="my-4 flex flex-col items-center text-center" method="post">
|
||||
@csrf @method('put')
|
||||
|
||||
@if($receive_emails)
|
||||
<p>{{ ctrans('texts.subscribe_help') }}</p>
|
||||
|
||||
<button
|
||||
name="action"
|
||||
value="unsubscribe"
|
||||
class="button button-secondary mt-4"
|
||||
>
|
||||
{{ ctrans('texts.unsubscribe') }}
|
||||
</button>
|
||||
@else
|
||||
<p>{{ ctrans('texts.unsubscribe_help') }}</p>
|
||||
|
||||
<button
|
||||
name="action"
|
||||
value="subscribe"
|
||||
class="button button-secondary mt-4"
|
||||
>
|
||||
{{ ctrans('texts.subscribe') }}
|
||||
</button>
|
||||
@endif
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@stop
|
@ -1,5 +1,6 @@
|
||||
<?php
|
||||
|
||||
use App\Http\Controllers\ClientPortal\EmailPreferencesController;
|
||||
use App\Http\Controllers\Auth\ContactForgotPasswordController;
|
||||
use App\Http\Controllers\Auth\ContactLoginController;
|
||||
use App\Http\Controllers\Auth\ContactRegisterController;
|
||||
@ -132,6 +133,9 @@ Route::group(['middleware' => ['invite_db'], 'prefix' => 'client', 'as' => 'clie
|
||||
Route::get('{entity}/{invitation_key}/download', [App\Http\Controllers\ClientPortal\InvitationController::class, 'routerForDownload'])->middleware('token_auth');
|
||||
Route::get('pay/{invitation_key}', [App\Http\Controllers\ClientPortal\InvitationController::class, 'payInvoice'])->name('pay.invoice');
|
||||
|
||||
Route::get('email_preferences/{entity}/{invitation_key}', [EmailPreferencesController::class, 'index'])->name('email_preferences')->middleware('signed');
|
||||
Route::put('email_preferences/{entity}/{invitation_key}', [EmailPreferencesController::class, 'update']);
|
||||
|
||||
Route::get('unsubscribe/{entity}/{invitation_key}', [App\Http\Controllers\ClientPortal\InvitationController::class, 'unsubscribe'])->name('unsubscribe');
|
||||
});
|
||||
|
||||
@ -159,3 +163,5 @@ Route::fallback(function () {
|
||||
abort(404);
|
||||
|
||||
})->middleware('throttle:404');
|
||||
|
||||
// Fix me: Move into invite_db middleware group.
|
||||
|
Loading…
x
Reference in New Issue
Block a user