From 550cb4272264761098bf2a8de85048194ad14a35 Mon Sep 17 00:00:00 2001 From: David Bomba Date: Wed, 11 Dec 2019 07:25:54 +1100 Subject: [PATCH] Dynamically apply locale (#3140) * Minor fixes for OpenAPI docs for clients * Add fields to company transformer * Padding email templates, system level and custom * Minor fixes for email template subject * Working on Email Templates * Clean up User model, remove redundant permissions methods * Implement Locale for API * Implement Locale middleware for client routes --- app/Console/Commands/SendTestEmails.php | 74 ++++ app/DataMapper/EmailTemplateDefaults.php | 23 +- app/Helpers/TranslationHelper.php | 4 +- app/Http/Kernel.php | 13 +- app/Http/Middleware/Locale.php | 36 ++ app/Http/Middleware/StartupCheck.php | 11 +- app/Mail/TemplateEmail.php | 49 +++ app/Models/Client.php | 11 + app/Models/User.php | 42 +-- config/ninja.php | 1 + resources/views/email/template/dark.blade.php | 10 + .../views/email/template/light.blade.php | 10 + .../views/email/template/master.blade.php | 339 ++++++++++++++++++ .../views/email/template/plain.blade.php | 4 + routes/api.php | 6 +- routes/client.php | 12 +- 16 files changed, 572 insertions(+), 73 deletions(-) create mode 100644 app/Console/Commands/SendTestEmails.php create mode 100644 app/Http/Middleware/Locale.php create mode 100644 app/Mail/TemplateEmail.php create mode 100644 resources/views/email/template/master.blade.php diff --git a/app/Console/Commands/SendTestEmails.php b/app/Console/Commands/SendTestEmails.php new file mode 100644 index 000000000000..a57f33902338 --- /dev/null +++ b/app/Console/Commands/SendTestEmails.php @@ -0,0 +1,74 @@ +sendTemplateEmails('plain'); + $this->sendTemplateEmails('light'); + $this->sendTemplateEmails('dark'); + } + + private function sendTemplateEmails($template) + { + $message = [ + 'title' => 'Invoice XJ-3838', + 'body' => '
"Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium doloremque laudantium, totam rem aperiam, eaque ipsa quae ab illo inventore veritatis et quasi architecto beatae vitae dicta sunt explicabo. Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed quia consequuntur magni dolores eos qui ratione voluptatem sequi nesciunt. Neque porro quisquam est, qui dolorem ipsum quia dolor sit amet, consectetur, adipisci velit, sed quia non numquam eius modi tempora incidunt ut labore et dolore magnam aliquam quaerat voluptatem. Ut enim ad minima veniam, quis nostrum exercitationem ullam corporis suscipit laboriosam, nisi ut aliquid ex ea commodi consequatur? Quis autem vel eum iure reprehenderit qui in ea voluptate velit esse quam nihil molestiae consequatur, vel illum qui dolorem eum fugiat quo voluptas nulla pariatur?"
', + 'subject' => 'The Test Subject', + 'footer' => 'Lovely Footer Texts', + ]; + + $user = User::whereEmail('user@example.com')->first(); + + if(!$user){ + $user = factory(\App\Models\User::class)->create([ + 'confirmation_code' => '123', + ]); + } + + $cc_emails = [config('ninja.testvars.test_email')]; + $bcc_emails = [config('ninja.testvars.test_email')]; + + Mail::to(config('ninja.testvars.test_email')) + ->cc($cc_emails) + ->bcc($bcc_emails) + //->replyTo(also_available_if_needed) + ->send(new TemplateEmail($message, $template, $user)); + } +} diff --git a/app/DataMapper/EmailTemplateDefaults.php b/app/DataMapper/EmailTemplateDefaults.php index a9ce7dac54aa..ca7970b81f26 100644 --- a/app/DataMapper/EmailTemplateDefaults.php +++ b/app/DataMapper/EmailTemplateDefaults.php @@ -17,7 +17,8 @@ class EmailTemplateDefaults { public static function emailInvoiceSubject() { - return Parsedown::instance()->line(self::transformText('invoice_subject')); + return ctrans('invoice_subject', ['number'=>'$number', 'account'=>'$company']); + //return Parsedown::instance()->line(self::transformText('invoice_subject')); } public static function emailInvoiceTemplate() @@ -27,7 +28,9 @@ class EmailTemplateDefaults public static function emailQuoteSubject() { - return Parsedown::instance()->line(self::transformText('quote_subject')); + return ctrans('quote_subject', ['number'=>'$number', 'account'=>'$company']); + + //return Parsedown::instance()->line(self::transformText('quote_subject')); } public static function emailQuoteTemplate() @@ -37,7 +40,8 @@ class EmailTemplateDefaults public static function emailPaymentSubject() { - return Parsedown::instance()->line(self::transformText('payment_subject')); + return ctrans('texts.payment_subject'); + //return Parsedown::instance()->line(self::transformText('payment_subject')); } public static function emailPaymentTemplate() @@ -47,7 +51,9 @@ class EmailTemplateDefaults public static function emailReminder1Subject() { - return Parsedown::instance()->line(self::transformText('reminder_subject')); + return ctrans('reminder_subject', ['invoice'=>'$number', 'account'=>'$company']); + + // return Parsedown::instance()->line(self::transformText('reminder_subject')); } public static function emailReminder1Template() @@ -57,7 +63,8 @@ class EmailTemplateDefaults public static function emailReminder2Subject() { - return Parsedown::instance()->line(self::transformText('reminder_subject')); + return ctrans('reminder_subject', ['invoice'=>'$number', 'account'=>'$company']); +// return Parsedown::instance()->line(self::transformText('reminder_subject')); } public static function emailReminder2Template() @@ -67,7 +74,8 @@ class EmailTemplateDefaults public static function emailReminder3Subject() { - return Parsedown::instance()->line(self::transformText('reminder_subject')); + return ctrans('reminder_subject', ['invoice'=>'$number', 'account'=>'$company']); +// return Parsedown::instance()->line(self::transformText('reminder_subject')); } public static function emailReminder3Template() @@ -77,7 +85,8 @@ class EmailTemplateDefaults public static function emailReminderEndlessSubject() { - return Parsedown::instance()->line(self::transformText('reminder_subject')); + return ctrans('reminder_subject', ['invoice'=>'$number', 'account'=>'$company']); +// return Parsedown::instance()->line(self::transformText('reminder_subject')); } public static function emailReminderEndlessTemplate() diff --git a/app/Helpers/TranslationHelper.php b/app/Helpers/TranslationHelper.php index 68f4067c7c71..281836d0d0b0 100644 --- a/app/Helpers/TranslationHelper.php +++ b/app/Helpers/TranslationHelper.php @@ -20,10 +20,10 @@ use Illuminate\Support\Facades\Cache; * @param string translation string key * @return string */ -function ctrans(string $string) : string +function ctrans(string $string, $replace = [], $locale = null) : string { //todo pass through the cached version of the custom strings here else return trans(); - return trans($string); + return trans($string, $replace, $locale); } \ No newline at end of file diff --git a/app/Http/Kernel.php b/app/Http/Kernel.php index 87dc5a09948b..451bf81706e7 100644 --- a/app/Http/Kernel.php +++ b/app/Http/Kernel.php @@ -72,15 +72,6 @@ class Kernel extends HttpKernel \App\Http\Middleware\StartupCheck::class, \App\Http\Middleware\QueryLogging::class, ], - 'api_db' => [ - \App\Http\Middleware\SetDb::class, - ], - 'web_db' => [ - \App\Http\Middleware\SetWebDb::class, - ], - 'url_db' => [ - \App\Http\Middleware\UrlSetDb::class, - ] ]; /** @@ -111,5 +102,9 @@ class Kernel extends HttpKernel 'password_protected' => \App\Http\Middleware\PasswordProtection::class, 'signed' => \Illuminate\Routing\Middleware\ValidateSignature::class, 'portal_enabled' => \App\Http\Middleware\ClientPortalEnabled::class, + 'url_db' => \App\Http\Middleware\UrlSetDb::class, + 'web_db' => \App\Http\Middleware\SetWebDb::class, + 'api_db' => \App\Http\Middleware\SetDb::class, + 'locale' => \App\Http\Middleware\Locale::class, ]; } diff --git a/app/Http/Middleware/Locale.php b/app/Http/Middleware/Locale.php new file mode 100644 index 000000000000..cac47a70c35b --- /dev/null +++ b/app/Http/Middleware/Locale.php @@ -0,0 +1,36 @@ +has('lang')) { + $locale = $request->input('lang'); + App::setLocale($locale); + } elseif (auth('contact')->user()) { + App::setLocale(auth('contact')->user()->client->locale()); + } elseif (auth()->user()) { + App::setLocale(auth()->user()->company()->getLocale()); + } + else + App::setLocale(config('ninja.i18n.locale')); + + return $next($request); + } +} diff --git a/app/Http/Middleware/StartupCheck.php b/app/Http/Middleware/StartupCheck.php index 08fbb281e0c9..51d01b4f0eef 100644 --- a/app/Http/Middleware/StartupCheck.php +++ b/app/Http/Middleware/StartupCheck.php @@ -11,13 +11,15 @@ namespace App\Http\Middleware; +use App\Models\Language; +use Closure; use Illuminate\Http\Request; +use Illuminate\Support\Facades\App; use Illuminate\Support\Facades\Cache; +use Illuminate\Support\Facades\Log; use Illuminate\Support\Facades\Request as Input; use Illuminate\Support\Facades\Schema; -use Illuminate\Support\Facades\Log; use Illuminate\Support\Facades\Session; -use Closure; /** @@ -64,10 +66,7 @@ class StartupCheck } } } - - // $end = microtime(true) - $start; - // Log::error("middleware cost = {$end} ms"); - + $response = $next($request); return $response; diff --git a/app/Mail/TemplateEmail.php b/app/Mail/TemplateEmail.php new file mode 100644 index 000000000000..a7f1badf69c9 --- /dev/null +++ b/app/Mail/TemplateEmail.php @@ -0,0 +1,49 @@ +message = $message; + $this->template = $template; + $this->user = $user; + } + + /** + * Build the message. + * + * @return $this + */ + public function build() + { + /*Alter Run Time Mailer configuration (driver etc etc) to regenerate the Mailer Singleton*/ + + //if using a system level template + $template_name = 'email.template.'.$this->template; + + return $this->from($this->user->email, $this->user->present()->name()) //todo this needs to be fixed to handle the hosted version + ->subject($this->message['subject']) + ->view($template_name, [ + 'body' => $this->message['body'], + 'footer' => $this->message['footer'], + 'title' => $this->message['title'], + ]); + + } +} \ No newline at end of file diff --git a/app/Models/Client.php b/app/Models/Client.php index 7c6dc8ac0fd1..b96eb36463e6 100644 --- a/app/Models/Client.php +++ b/app/Models/Client.php @@ -23,6 +23,7 @@ use App\Models\DatetimeFormat; use App\Models\Filterable; use App\Models\GatewayType; use App\Models\GroupSetting; +use App\Models\Language; use App\Models\Timezone; use App\Models\User; use App\Utils\Traits\CompanyGatewaySettings; @@ -174,6 +175,16 @@ class Client extends BaseModel return Timezone::find($this->getSetting('timezone_id')); } + public function language() + { + return Language::find($this->getSetting('language_id')); + } + + public function locale() + { + return $this->language()->locale; + } + public function date_format() { $date_formats = Cache::get('date_formats'); diff --git a/app/Models/User.php b/app/Models/User.php index 1e70dc56c87a..0408ff0e6b3e 100644 --- a/app/Models/User.php +++ b/app/Models/User.php @@ -90,7 +90,6 @@ class User extends Authenticatable implements MustVerifyEmail protected $casts = [ 'settings' => 'object', - 'permissions' => 'object', 'updated_at' => 'timestamp', 'created_at' => 'timestamp', 'deleted_at' => 'timestamp', @@ -197,19 +196,15 @@ class User extends Authenticatable implements MustVerifyEmail } /** - * Returns a object of user permissions + * Returns a comma separated list of user permissions * - * @return stdClass + * @return comma separated list */ public function permissions() { - $permissions = json_decode($this->company_user->permissions); - - if (! $permissions) - return []; + return $this->company_user->permissions; - return $permissions; } /** @@ -274,18 +269,6 @@ class User extends Authenticatable implements MustVerifyEmail } - /** - * Flattens a stdClass representation of the User Permissions - * into a Collection - * - * @return Collection - */ - public function permissionsFlat() :Collection - { - - return collect($this->permissions())->flatten(); - - } /** * Returns true if permissions exist in the map @@ -296,27 +279,8 @@ class User extends Authenticatable implements MustVerifyEmail public function hasPermission($permission) : bool { - return (stripos($this->company_user->permissions, $permission) !== false); - - // return $this->permissionsFlat()->contains($permission); - - } - - /** - * Returns a array of permission for the mobile application - * - * @return array - */ - public function permissionsMap() : array - { - - $keys = array_values((array) $this->permissions()); - $values = array_fill(0, count($keys), true); - - return array_combine($keys, $values); - } public function documents() diff --git a/config/ninja.php b/config/ninja.php index e749a77460cb..7be0b08649b9 100644 --- a/config/ninja.php +++ b/config/ninja.php @@ -59,6 +59,7 @@ return [ 'stripe' => env('STRIPE_KEYS',''), 'paypal' => env('PAYPAL_KEYS', ''), 'travis' => env('TRAVIS', false), + 'test_email' => env('TEST_EMAIL',''), ], 'contact' => [ 'email' => env('MAIL_FROM_ADDRESS'), diff --git a/resources/views/email/template/dark.blade.php b/resources/views/email/template/dark.blade.php index e69de29bb2d1..706ea191745e 100644 --- a/resources/views/email/template/dark.blade.php +++ b/resources/views/email/template/dark.blade.php @@ -0,0 +1,10 @@ +@extends('email.template.master') +@section('title') +{{ $title }} +@endsection +@section('content') +{!! $body !!} +@endsection +@section('footer') +{!! $footer !!} +@endsection \ No newline at end of file diff --git a/resources/views/email/template/light.blade.php b/resources/views/email/template/light.blade.php index e69de29bb2d1..706ea191745e 100644 --- a/resources/views/email/template/light.blade.php +++ b/resources/views/email/template/light.blade.php @@ -0,0 +1,10 @@ +@extends('email.template.master') +@section('title') +{{ $title }} +@endsection +@section('content') +{!! $body !!} +@endsection +@section('footer') +{!! $footer !!} +@endsection \ No newline at end of file diff --git a/resources/views/email/template/master.blade.php b/resources/views/email/template/master.blade.php new file mode 100644 index 000000000000..19a0e48c9a80 --- /dev/null +++ b/resources/views/email/template/master.blade.php @@ -0,0 +1,339 @@ + + + + + + @yield('title') + + + + This is preheader text. Some clients will show this text as a preview. + + + + + + + + + \ No newline at end of file diff --git a/resources/views/email/template/plain.blade.php b/resources/views/email/template/plain.blade.php index e69de29bb2d1..14cfbc408f39 100644 --- a/resources/views/email/template/plain.blade.php +++ b/resources/views/email/template/plain.blade.php @@ -0,0 +1,4 @@ +{{ $body }} +
+
+{{ $footer}} \ No newline at end of file diff --git a/routes/api.php b/routes/api.php index 1563b8e9e5bc..c9eb7e7f04f4 100644 --- a/routes/api.php +++ b/routes/api.php @@ -32,7 +32,7 @@ Route::group(['api_secret_check','email_db'], function () { }); -Route::group(['middleware' => ['api_db','api_secret_check','token_auth'], 'prefix' =>'api/v1', 'as' => 'api.'], function () { +Route::group(['middleware' => ['api_db','api_secret_check','token_auth','locale'], 'prefix' =>'api/v1', 'as' => 'api.'], function () { Route::resource('activities', 'ActivityController'); // name = (clients. index / create / show / update / destroy / edit @@ -111,6 +111,4 @@ Route::group(['middleware' => ['api_db','api_secret_check','token_auth'], 'prefi Route::post('support/messages/send', 'Support\Messages\SendingController'); }); -Route::get('test_email', '\App\Helpers\Mail\GmailTransportConfig@test'); - -Route::fallback('BaseController@notFound'); +Route::fallback('BaseController@notFound'); \ No newline at end of file diff --git a/routes/client.php b/routes/client.php index 98e575027d8c..3d859e537a31 100644 --- a/routes/client.php +++ b/routes/client.php @@ -2,16 +2,16 @@ Route::get('client', 'Auth\ContactLoginController@showLoginForm')->name('client.login'); //catch all -Route::get('client/login', 'Auth\ContactLoginController@showLoginForm')->name('client.login'); +Route::get('client/login', 'Auth\ContactLoginController@showLoginForm')->name('client.login')->middleware('locale'); Route::post('client/login', 'Auth\ContactLoginController@login')->name('client.login.submit'); -Route::get('client/password/reset', 'Auth\ContactForgotPasswordController@showLinkRequestForm')->name('client.password.request'); -Route::post('client/password/email', 'Auth\ContactForgotPasswordController@sendResetLinkEmail')->name('client.password.email'); -Route::get('client/password/reset/{token}', 'Auth\ContactResetPasswordController@showResetForm')->name('client.password.reset'); -Route::post('client/password/reset', 'Auth\ContactResetPasswordController@reset')->name('client.password.update'); +Route::get('client/password/reset', 'Auth\ContactForgotPasswordController@showLinkRequestForm')->name('client.password.request')->middleware('locale'); +Route::post('client/password/email', 'Auth\ContactForgotPasswordController@sendResetLinkEmail')->name('client.password.email')->middleware('locale'); +Route::get('client/password/reset/{token}', 'Auth\ContactResetPasswordController@showResetForm')->name('client.password.reset')->middleware('locale'); +Route::post('client/password/reset', 'Auth\ContactResetPasswordController@reset')->name('client.password.update')->middleware('locale'); //todo implement domain DB -Route::group(['middleware' => ['auth:contact'], 'prefix' => 'client', 'as' => 'client.'], function () { +Route::group(['middleware' => ['auth:contact','locale'], 'prefix' => 'client', 'as' => 'client.'], function () { Route::get('dashboard', 'ClientPortal\DashboardController@index')->name('dashboard'); // name = (dashboard. index / create / show / update / destroy / edit