From c1f2af5174ae93e86b0f0712838d4dbeb520808a Mon Sep 17 00:00:00 2001 From: yogesh-gohil Date: Sat, 11 Mar 2023 12:39:48 +0530 Subject: [PATCH 1/7] add mail sender crud --- .../GetAllMailSendersController.php | 24 ++++ .../Admin/MailSender/MailSenderController.php | 111 +++++++++++++++++ app/Http/Requests/MailSenderRequest.php | 79 ++++++++++++ app/Http/Resources/MailSenderResource.php | 30 +++++ app/Models/MailSender.php | 77 ++++++++++++ ...03_10_125202_create_mail_senders_table.php | 114 ++++++++++++++++++ routes/api.php | 11 +- 7 files changed, 443 insertions(+), 3 deletions(-) create mode 100644 app/Http/Controllers/V1/Admin/MailSender/GetAllMailSendersController.php create mode 100644 app/Http/Controllers/V1/Admin/MailSender/MailSenderController.php create mode 100644 app/Http/Requests/MailSenderRequest.php create mode 100644 app/Http/Resources/MailSenderResource.php create mode 100644 app/Models/MailSender.php create mode 100644 database/migrations/2023_03_10_125202_create_mail_senders_table.php diff --git a/app/Http/Controllers/V1/Admin/MailSender/GetAllMailSendersController.php b/app/Http/Controllers/V1/Admin/MailSender/GetAllMailSendersController.php new file mode 100644 index 000000000..60cf26590 --- /dev/null +++ b/app/Http/Controllers/V1/Admin/MailSender/GetAllMailSendersController.php @@ -0,0 +1,24 @@ +get(); + + return MailSenderResource::collection($mailSenders); + } +} diff --git a/app/Http/Controllers/V1/Admin/MailSender/MailSenderController.php b/app/Http/Controllers/V1/Admin/MailSender/MailSenderController.php new file mode 100644 index 000000000..9b30ec5b0 --- /dev/null +++ b/app/Http/Controllers/V1/Admin/MailSender/MailSenderController.php @@ -0,0 +1,111 @@ +authorize('viewAny', MailSender::class); + + $limit = $request->has('limit') ? $request->limit : 10; + + $mailSenders = MailSender::whereCompany() + ->applyFilters($request->all()) + ->paginateData($limit); + + return (MailSenderResource::collection($mailSenders)) + ->additional(['meta' => [ + 'mail_sender_total_count' => MailSender::whereCompany()->count(), + ]]); + } + + /** + * Store a newly created resource in storage. + * + * @param \Illuminate\Http\Request $request + * @return \Illuminate\Http\Response + */ + public function store(MailSenderRequest $request) + { + $this->authorize('create', MailSender::class); + + $mailConfiguration = MailSender::where('company_id', $request->header('company')) + ->where('is_default', true) + ->count(); + + if ($mailConfiguration > 0 && $request['is_default'] == true) { + return respondJson('default_record_exists', 'Default mail sender already exist'); + } + + $mailSender = MailSender::createFromRequest($request); + + return new MailSenderResource($mailSender); + } + + /** + * Display the specified resource. + * + * @param \Crater\Models\SenderMail $senderMail + * @return \Illuminate\Http\Response + */ + public function show(MailSender $mailSender) + { + $this->authorize('view', $mailSender); + + return new MailSenderResource($mailSender); + } + + /** + * Update the specified resource in storage. + * + * @param \Illuminate\Http\Request $request + * @param \Crater\Models\SenderMail $senderMail + * @return \Illuminate\Http\Response + */ + public function update(MailSenderRequest $request, MailSender $mailSender) + { + $this->authorize('update', $mailSender); + + $mailConfiguration = MailSender::where('company_id', $request->header('company')) + ->where('is_default', true) + ->where('id', '<>', $mailSender->id) + ->count(); + + if ($mailConfiguration > 0 && $request['is_default'] == true) { + return respondJson('default_record_exists', 'Default mail sender already exist'); + } + + $mailSender->updateFromRequest($request); + + return new MailSenderResource($mailSender); + } + + /** + * Remove the specified resource from storage. + * + * @param \Crater\Models\SenderMail $senderMail + * @return \Illuminate\Http\Response + */ + public function destroy(MailSender $mailSender) + { + $this->authorize('delete', $mailSender); + + $mailSender->delete(); + + return response()->json([ + 'success' => true, + ]); + } +} diff --git a/app/Http/Requests/MailSenderRequest.php b/app/Http/Requests/MailSenderRequest.php new file mode 100644 index 000000000..70be59f62 --- /dev/null +++ b/app/Http/Requests/MailSenderRequest.php @@ -0,0 +1,79 @@ + [ + 'required', + Rule::unique('mail_senders') + ->where('company_id', $this->header('company')) + ], + 'driver' => [ + 'required', + ], + 'is_default' => [ + 'nullable' + ], + 'bcc' => [ + 'nullable' + ], + 'cc' => [ + 'nullable' + ], + 'from_address' => [ + 'nullable' + ], + 'from_name' => [ + 'nullable' + ], + 'settings' => [ + 'nullable' + ], + 'settings.*' => [ + 'nullable' + ] + ]; + + if ($this->isMethod('PUT')) { + $rules['name'] = [ + 'nullable', + Rule::unique('mail_senders') + ->ignore($this->route('mail_sender')->id) + ->where('company_id', $this->header('company')) + ]; + } + + return $rules; + } + + public function getMailSenderPayload() + { + return collect($this->validated()) + ->merge([ + 'company_id' => $this->header('company'), + ]) + ->toArray(); + } +} diff --git a/app/Http/Resources/MailSenderResource.php b/app/Http/Resources/MailSenderResource.php new file mode 100644 index 000000000..e4b14504d --- /dev/null +++ b/app/Http/Resources/MailSenderResource.php @@ -0,0 +1,30 @@ + $this->id, + 'name' => $this->name, + 'driver' => $this->driver, + 'is_default' => $this->is_default, + 'bcc' => $this->bcc, + 'cc' => $this->cc, + 'from_address' => $this->from_address, + 'from_name' => $this->from_name, + 'company_id' => $this->company_id, + 'settings' => $this->settings + ]; + } +} diff --git a/app/Models/MailSender.php b/app/Models/MailSender.php new file mode 100644 index 000000000..61534a12f --- /dev/null +++ b/app/Models/MailSender.php @@ -0,0 +1,77 @@ + 'array', + 'is_default' => 'boolean' + ]; + + public function company() + { + return $this->belongsTo(Company::class); + } + + public function setSettingsAttribute($value) + { + $this->attributes['settings'] = json_encode($value); + } + + public function scopeWhereOrder($query, $orderByField, $orderBy) + { + $query->orderBy($orderByField, $orderBy); + } + + public function scopeApplyFilters($query, array $filters) + { + $filters = collect($filters); + + if ($filters->get('orderByField') || $filters->get('orderBy')) { + $field = $filters->get('orderByField') ? $filters->get('orderByField') : 'name'; + $orderBy = $filters->get('orderBy') ? $filters->get('orderBy') : 'desc'; + $query->whereOrder($field, $orderBy); + } + } + + public function scopePaginateData($query, $limit) + { + if ($limit == 'all') { + return $query->get(); + } + + return $query->paginate($limit); + } + + public function scopeWhereCompany($query) + { + $query->where('mail_senders.company_id', request()->header('company')); + } + + public static function createFromRequest(MailSenderRequest $request) + { + $senderMail = self::create($request->getMailSenderPayload()); + + return $senderMail; + } + + public function updateFromRequest(MailSenderRequest $request) + { + $data = $request->getMailSenderPayload(); + + $this->update($data); + + return $this; + } +} diff --git a/database/migrations/2023_03_10_125202_create_mail_senders_table.php b/database/migrations/2023_03_10_125202_create_mail_senders_table.php new file mode 100644 index 000000000..4573a593a --- /dev/null +++ b/database/migrations/2023_03_10_125202_create_mail_senders_table.php @@ -0,0 +1,114 @@ +id(); + $table->string('name'); + $table->string('driver'); + $table->boolean('is_default')->default(false); + $table->string('bcc')->nullable(); + $table->string('cc')->nullable(); + $table->string('from_address')->nullable(); + $table->string('from_name')->nullable(); + $table->json('settings')->nullable(); + $table->integer('company_id')->unsigned()->nullable(); + $table->foreign('company_id')->references('id')->on('companies'); + $table->timestamps(); + }); + + $user = User::where('role', 'super admin')->first(); + + if ($user) { + $users = User::where('role', 'super admin')->get(); + + foreach ($users as $user) { + $user->allow('view-mail-sender'); + $user->allow('create-mail-sender'); + $user->allow('edit-mail-sender'); + $user->allow('delete-mail-sender'); + BouncerFacade::allow($user)->toOwn(MailSender::class); + } + + $companies = Company::all(); + + $companies->map(function ($company) { + if (env('MAIL_DRIVER') == 'smtp') { + $settings = [ + 'MAIL_HOST' => env('MAIL_HOST'), + 'MAIL_PORT' => env('MAIL_PORT'), + 'MAIL_USERNAME' => env('MAIL_USERNAME'), + 'MAIL_PASSWORD' => env('MAIL_PASSWORD'), + 'MAIL_ENCRYPTION' => env('MAIL_ENCRYPTION') + ]; + $this->insertData($settings, $company->id); + } + + if (env('MAIL_DRIVER') == 'mail' || env('MAIL_DRIVER') == 'sendmail') { + $this->insertData(null, $company->id); + } + + if (env('MAIL_DRIVER') == 'mailgun') { + $settings = [ + 'MAILGUN_DOMAIN' => env('MAILGUN_DOMAIN'), + 'MAILGUN_SECRET' => env('MAILGUN_SECRET'), + 'MAILGUN_ENDPOINT' => env('MAILGUN_ENDPOINT'), + ]; + $this->insertData($settings, $company->id); + } + + if (env('MAIL_DRIVER') == 'ses') { + $settings = [ + 'MAIL_HOST' => env('MAIL_HOST'), + 'MAIL_PORT' => env('MAIL_PORT'), + 'MAIL_ENCRYPTION' => env('MAIL_ENCRYPTION'), + 'MAILGUN_DOMAIN' => env('MAILGUN_DOMAIN'), + 'SES_KEY' => env('SES_KEY'), + 'SES_SECRET' => env('SES_SECRET'), + ]; + $this->insertData($settings, $company->id); + } + }); + } + } + + public function insertData($settings, $company_id) + { + $data = [ + 'name' => env('MAIL_DRIVER'), + 'driver' => env('MAIL_DRIVER'), + 'is_default' => true, + 'from_address' => env('MAIL_FROM_ADDRESS'), + 'from_name' => env('MAIL_FROM_NAME'), + 'settings' => $settings ?? null, + 'company_id' => $company_id + ]; + + MailSender::create($data); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::dropIfExists('mail_senders'); + } +} diff --git a/routes/api.php b/routes/api.php index b81cbd79b..76bc69bd7 100644 --- a/routes/api.php +++ b/routes/api.php @@ -47,6 +47,8 @@ use Crater\Http\Controllers\V1\Admin\Invoice\SendInvoicePreviewController; use Crater\Http\Controllers\V1\Admin\Item\ItemsController; use Crater\Http\Controllers\V1\Admin\Item\UnitsController; +use Crater\Http\Controllers\V1\Admin\MailSender\GetAllMailSendersController; +use Crater\Http\Controllers\V1\Admin\MailSender\MailSenderController; use Crater\Http\Controllers\V1\Admin\Mobile\AuthController; use Crater\Http\Controllers\V1\Admin\Modules\ApiTokenController; use Crater\Http\Controllers\V1\Admin\Modules\CompleteModuleInstallationController; @@ -403,11 +405,14 @@ Route::get('/mail/drivers', [MailConfigurationController::class, 'getMailDrivers']); - Route::get('/mail/config', [MailConfigurationController::class, 'getMailEnvironment']); + Route::post('/mail/test', [MailConfigurationController::class, 'testEmailConfig']); + // Route::get('/mail/config', [MailConfigurationController::class, 'getMailEnvironment']); - Route::post('/mail/config', [MailConfigurationController::class, 'saveMailEnvironment']); + // Route::post('/mail/config', [MailConfigurationController::class, 'saveMailEnvironment']); - Route::post('/mail/test', [MailConfigurationController::class, 'testEmailConfig']); + Route::apiResource('mail-sender', MailSenderController::class); + + Route::get('mail-senders', GetAllMailSendersController::class); Route::get('/company/mail/config', GetCompanyMailConfigurationController::class); From b4aa254b68241a2d72b2819b5607c8f19ef372de Mon Sep 17 00:00:00 2001 From: yogesh-gohil Date: Sat, 11 Mar 2023 15:31:05 +0530 Subject: [PATCH 2/7] add mail-sender abilities --- config/abilities.php | 36 +++++++++++++++++++++++ resources/scripts/admin/stub/abilities.js | 7 +++++ 2 files changed, 43 insertions(+) diff --git a/config/abilities.php b/config/abilities.php index 278050ad9..dfbd1f88d 100644 --- a/config/abilities.php +++ b/config/abilities.php @@ -7,6 +7,7 @@ use Crater\Models\Expense; use Crater\Models\Invoice; use Crater\Models\Item; +use Crater\Models\MailSender; use Crater\Models\Note; use Crater\Models\Payment; use Crater\Models\RecurringInvoice; @@ -397,6 +398,41 @@ ] ], + // Mail Sender + [ + "name" => "view mail sender", + "ability" => "view-mail-sender", + "model" => MailSender::class, + 'owner_only' => false, + ], + [ + "name" => "create mail sender", + "ability" => "create-mail-sender", + "model" => MailSender::class, + 'owner_only' => false, + "depends_on" => [ + 'view-mail-sender', + ] + ], + [ + "name" => "edit mail sender", + "ability" => "edit-mail-sender", + "model" => MailSender::class, + 'owner_only' => false, + "depends_on" => [ + 'view-mail-sender', + ] + ], + [ + "name" => "delete mail sender", + "ability" => "delete-mail-sender", + "model" => MailSender::class, + 'owner_only' => false, + "depends_on" => [ + 'view-mail-sender', + ] + ], + // Settings [ "name" => "view company dashboard", diff --git a/resources/scripts/admin/stub/abilities.js b/resources/scripts/admin/stub/abilities.js index 024e9992e..f01b73690 100644 --- a/resources/scripts/admin/stub/abilities.js +++ b/resources/scripts/admin/stub/abilities.js @@ -64,6 +64,13 @@ export default { EDIT_ROLE: 'edit-role', VIEW_ROLE: 'view-role', + // Mail Sender + CREATE_MAIL_SENDER: 'view-mail-sender', + DELETE_MAIL_SENDER: 'delete-mail-sender', + EDIT_MAIL_SENDER: 'edit-mail-sender', + VIEW_MAIL_SENDER: 'view-mail-sender', + + // exchange rates VIEW_EXCHANGE_RATE: 'view-exchange-rate-provider', CREATE_EXCHANGE_RATE: 'create-exchange-rate-provider', From 959aa257b46cdb410101e2c886305ca9f882a153 Mon Sep 17 00:00:00 2001 From: yashkanakiya Date: Sat, 11 Mar 2023 18:58:59 +0530 Subject: [PATCH 3/7] add mail sender in setting --- .../Settings/MailConfigurationController.php | 86 +----- app/Http/Requests/MailSenderRequest.php | 2 +- app/Http/Requests/TestMailDriverRequest.php | 39 +++ app/Models/MailSender.php | 22 ++ app/Policies/MailSenderPolicy.php | 123 ++++++++ app/Providers/AuthServiceProvider.php | 1 + config/crater.php | 23 +- resources/scripts/admin/admin-router.js | 18 +- .../dropdowns/MailSenderIndexDropdown.vue | 111 +++++++ .../modal-components/MailSenderModal.vue | 290 ++++++++++++++++++ .../modal-components/MailSenderTestModal.vue | 208 +++++++++++++ resources/scripts/admin/stores/mail-sender.js | 209 +++++++++++++ resources/scripts/admin/stores/users.js | 1 + resources/scripts/admin/stub/customer.js | 1 + resources/scripts/admin/stub/mail-sender.js | 31 ++ .../scripts/admin/views/customers/Create.vue | 45 +++ .../views/settings/mail-sender/Index.vue | 136 ++++++++ .../settings/mail-sender/MailgunDriver.vue | 104 +++++++ .../views/settings/mail-sender/SesDriver.vue | 143 +++++++++ .../views/settings/mail-sender/SmtpDriver.vue | 120 ++++++++ .../scripts/admin/views/users/Create.vue | 45 ++- resources/scripts/locales/en.json | 59 +++- routes/api.php | 9 +- 23 files changed, 1717 insertions(+), 109 deletions(-) create mode 100644 app/Http/Requests/TestMailDriverRequest.php create mode 100644 app/Policies/MailSenderPolicy.php create mode 100644 resources/scripts/admin/components/dropdowns/MailSenderIndexDropdown.vue create mode 100644 resources/scripts/admin/components/modal-components/MailSenderModal.vue create mode 100644 resources/scripts/admin/components/modal-components/MailSenderTestModal.vue create mode 100644 resources/scripts/admin/stores/mail-sender.js create mode 100644 resources/scripts/admin/stub/mail-sender.js create mode 100644 resources/scripts/admin/views/settings/mail-sender/Index.vue create mode 100644 resources/scripts/admin/views/settings/mail-sender/MailgunDriver.vue create mode 100644 resources/scripts/admin/views/settings/mail-sender/SesDriver.vue create mode 100644 resources/scripts/admin/views/settings/mail-sender/SmtpDriver.vue diff --git a/app/Http/Controllers/V1/Admin/Settings/MailConfigurationController.php b/app/Http/Controllers/V1/Admin/Settings/MailConfigurationController.php index 29794d5c9..f07029dcc 100755 --- a/app/Http/Controllers/V1/Admin/Settings/MailConfigurationController.php +++ b/app/Http/Controllers/V1/Admin/Settings/MailConfigurationController.php @@ -3,80 +3,29 @@ namespace Crater\Http\Controllers\V1\Admin\Settings; use Crater\Http\Controllers\Controller; -use Crater\Http\Requests\MailEnvironmentRequest; +use Crater\Http\Requests\TestMailDriverRequest; use Crater\Mail\TestMail; -use Crater\Models\Setting; -use Crater\Space\EnvironmentManager; -use Illuminate\Http\JsonResponse; +use Crater\Models\MailSender; use Illuminate\Http\Request; use Mail; class MailConfigurationController extends Controller { - /** - * @var EnvironmentManager - */ - protected $environmentManager; - - /** - * @param EnvironmentManager $environmentManager - */ - public function __construct(EnvironmentManager $environmentManager) - { - $this->environmentManager = $environmentManager; - } - - /** - * - * @param MailEnvironmentRequest $request - * @return JsonResponse - */ - public function saveMailEnvironment(MailEnvironmentRequest $request) + public function TestMailDriver(TestMailDriverRequest $request) { $this->authorize('manage email config'); - $setting = Setting::getSetting('profile_complete'); - $results = $this->environmentManager->saveMailVariables($request); - - if ($setting !== 'COMPLETED') { - Setting::setSetting('profile_complete', 4); - } - - return response()->json($results); - } - - public function getMailEnvironment() - { - $this->authorize('manage email config'); - - $MailData = [ - 'mail_driver' => config('mail.driver'), - 'mail_host' => config('mail.host'), - 'mail_port' => config('mail.port'), - 'mail_username' => config('mail.username'), - 'mail_password' => config('mail.password'), - 'mail_encryption' => config('mail.encryption'), - 'from_name' => config('mail.from.name'), - 'from_mail' => config('mail.from.address'), - 'mail_mailgun_endpoint' => config('services.mailgun.endpoint'), - 'mail_mailgun_domain' => config('services.mailgun.domain'), - 'mail_mailgun_secret' => config('services.mailgun.secret'), - 'mail_ses_key' => config('services.ses.key'), - 'mail_ses_secret' => config('services.ses.secret'), - ]; + MailSender::setMailConfiguration($request->mail_sender_id); + Mail::to($request->to)->send(new TestMail($request->subject, $request->message)); - return response()->json($MailData); + return response()->json([ + 'success' => true, + ]); } - /** - * - * @return JsonResponse - */ - public function getMailDrivers() + public function getMailDrivers(Request $request) { - $this->authorize('manage email config'); - $drivers = [ 'smtp', 'mail', @@ -87,21 +36,4 @@ public function getMailDrivers() return response()->json($drivers); } - - public function testEmailConfig(Request $request) - { - $this->authorize('manage email config'); - - $this->validate($request, [ - 'to' => 'required|email', - 'subject' => 'required', - 'message' => 'required', - ]); - - Mail::to($request->to)->send(new TestMail($request->subject, $request->message)); - - return response()->json([ - 'success' => true, - ]); - } } diff --git a/app/Http/Requests/MailSenderRequest.php b/app/Http/Requests/MailSenderRequest.php index 70be59f62..44e64a2fc 100644 --- a/app/Http/Requests/MailSenderRequest.php +++ b/app/Http/Requests/MailSenderRequest.php @@ -14,7 +14,7 @@ class MailSenderRequest extends FormRequest */ public function authorize() { - return false; + return true; } /** diff --git a/app/Http/Requests/TestMailDriverRequest.php b/app/Http/Requests/TestMailDriverRequest.php new file mode 100644 index 000000000..b3bc9e2e3 --- /dev/null +++ b/app/Http/Requests/TestMailDriverRequest.php @@ -0,0 +1,39 @@ + [ + 'required', + 'email' + ], + 'subject' => [ + 'required' + ], + 'message' => [ + 'required' + ], + ]; + } +} diff --git a/app/Models/MailSender.php b/app/Models/MailSender.php index 61534a12f..625baaf65 100644 --- a/app/Models/MailSender.php +++ b/app/Models/MailSender.php @@ -5,6 +5,7 @@ use Crater\Http\Requests\MailSenderRequest; use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Model; +use Config; class MailSender extends Model { @@ -74,4 +75,25 @@ public function updateFromRequest(MailSenderRequest $request) return $this; } + + public static function setMailConfiguration($id, $check = null) + { + $mailSender = MailSender::find($id); + + $settings = $mailSender->settings; + $settings['driver'] = $mailSender->driver; + $settings['from'] = [ + 'address' => $mailSender->from_address, + 'name' => $mailSender->from_name + ]; + + Config::set('mail', $settings); + + if ($check) { + return $mailSender; + } + + return true; + } + } diff --git a/app/Policies/MailSenderPolicy.php b/app/Policies/MailSenderPolicy.php new file mode 100644 index 000000000..079afe756 --- /dev/null +++ b/app/Policies/MailSenderPolicy.php @@ -0,0 +1,123 @@ +hasCompany($mailSender->company_id)) { + return true; + } + + return false; + } + + /** + * Determine whether the user can create models. + * + * @param \Crater\Models\User $user + * @return \Illuminate\Auth\Access\Response|bool + */ + public function create(User $user) + { + if (BouncerFacade::can('create-mail-sender', MailSender::class)) { + return true; + } + + return false; + } + + /** + * Determine whether the user can update the model. + * + * @param \Crater\Models\User $user + * @param \Crater\Models\MailSender $mailSender + * @return \Illuminate\Auth\Access\Response|bool + */ + public function update(User $user, MailSender $mailSender) + { + if (BouncerFacade::can('edit-mail-sender', $mailSender) && $user->hasCompany($mailSender->company_id)) { + return true; + } + + return false; + } + + /** + * Determine whether the user can delete the model. + * + * @param \Crater\Models\User $user + * @param \Crater\Models\MailSender $mailSender + * @return \Illuminate\Auth\Access\Response|bool + */ + public function delete(User $user, MailSender $mailSender) + { + if (BouncerFacade::can('delete-mail-sender', $mailSender) && $user->hasCompany($mailSender->company_id)) { + return true; + } + + return false; + } + + /** + * Determine whether the user can restore the model. + * + * @param \Crater\Models\User $user + * @param \Crater\Models\MailSender $mailSender + * @return \Illuminate\Auth\Access\Response|bool + */ + public function restore(User $user, MailSender $mailSender) + { + if (BouncerFacade::can('delete-mail-sender', $mailSender) && $user->hasCompany($mailSender->company_id)) { + return true; + } + + return false; + } + + /** + * Determine whether the user can permanently delete the model. + * + * @param \Crater\Models\User $user + * @param \Crater\Models\MailSender $mailSender + * @return \Illuminate\Auth\Access\Response|bool + */ + public function forceDelete(User $user, MailSender $mailSender) + { + if (BouncerFacade::can('delete-mail-sender', $mailSender) && $user->hasCompany($mailSender->company_id)) { + return true; + } + + return false; + } +} diff --git a/app/Providers/AuthServiceProvider.php b/app/Providers/AuthServiceProvider.php index 61cd43057..63a6d28a0 100644 --- a/app/Providers/AuthServiceProvider.php +++ b/app/Providers/AuthServiceProvider.php @@ -39,6 +39,7 @@ class AuthServiceProvider extends ServiceProvider \Crater\Models\CustomField::class => \Crater\Policies\CustomFieldPolicy::class, \Crater\Models\User::class => \Crater\Policies\UserPolicy::class, \Crater\Models\Item::class => \Crater\Policies\ItemPolicy::class, + \Crater\Models\MailSender::class => \Crater\Policies\MailSenderPolicy::class, \Silber\Bouncer\Database\Role::class => \Crater\Policies\RolePolicy::class, \Crater\Models\Unit::class => \Crater\Policies\UnitPolicy::class, \Crater\Models\RecurringInvoice::class => \Crater\Policies\RecurringInvoicePolicy::class, diff --git a/config/crater.php b/config/crater.php index 4de981054..894ba767e 100644 --- a/config/crater.php +++ b/config/crater.php @@ -7,6 +7,7 @@ use Crater\Models\Expense; use Crater\Models\Invoice; use Crater\Models\Item; +use Crater\Models\MailSender; use Crater\Models\Note; use Crater\Models\Payment; use Crater\Models\RecurringInvoice; @@ -225,6 +226,17 @@ 'ability' => 'view-all-notes', 'model' => Note::class ], + [ + 'title' => 'settings.menu_title.mail_sender', + 'group' => '', + 'name' => 'Mail Sender', + 'link' => '/admin/settings/mail-sender', + 'icon' => 'MailIcon', + 'owner_only' => false, + 'ability' => 'view-mail-sender', + 'model' => MailSender::class + ], + [ 'title' => 'settings.menu_title.expense_category', 'group' => '', @@ -235,16 +247,6 @@ 'ability' => 'view-expense', 'model' => Expense::class ], - [ - 'title' => 'settings.mail.mail_config', - 'group' => '', - 'name' => 'Mail Configuration', - 'link' => '/admin/settings/mail-configuration', - 'icon' => 'MailIcon', - 'owner_only' => true, - 'ability' => '', - 'model' => '' - ], [ 'title' => 'settings.menu_title.file_disk', 'group' => '', @@ -275,6 +277,7 @@ 'ability' => '', 'model' => '' ], + ], /* diff --git a/resources/scripts/admin/admin-router.js b/resources/scripts/admin/admin-router.js index d11952e4b..15de1d40c 100644 --- a/resources/scripts/admin/admin-router.js +++ b/resources/scripts/admin/admin-router.js @@ -47,8 +47,6 @@ const ExpenseCategory = () => import('@/scripts/admin/views/settings/ExpenseCategorySetting.vue') const ExchangeRateSetting = () => import('@/scripts/admin/views/settings/ExchangeRateProviderSetting.vue') -const MailConfig = () => - import('@/scripts/admin/views/settings/MailConfigSetting.vue') const FileDisk = () => import('@/scripts/admin/views/settings/FileDiskSetting.vue') const Backup = () => import('@/scripts/admin/views/settings/BackupSetting.vue') @@ -56,6 +54,8 @@ const UpdateApp = () => import('@/scripts/admin/views/settings/UpdateAppSetting.vue') const RolesSettings = () => import('@/scripts/admin/views/settings/RolesSettings.vue') +const MailSender = () => + import('@/scripts/admin/views/settings/mail-sender/Index.vue') // Items const ItemsIndex = () => import('@/scripts/admin/views/items/Index.vue') @@ -302,13 +302,6 @@ export default [ meta: { ability: abilities.VIEW_EXPENSE }, component: ExpenseCategory, }, - - { - path: 'mail-configuration', - name: 'mailconfig', - meta: { isOwner: true }, - component: MailConfig, - }, { path: 'file-disk', name: 'file-disk', @@ -327,6 +320,13 @@ export default [ meta: { isOwner: true }, component: UpdateApp, }, + { + path: 'mail-sender', + name: 'mailsender', + meta: { ability: abilities.VIEW_MAIL_SENDER }, + component: MailSender, + }, + ], }, diff --git a/resources/scripts/admin/components/dropdowns/MailSenderIndexDropdown.vue b/resources/scripts/admin/components/dropdowns/MailSenderIndexDropdown.vue new file mode 100644 index 000000000..365382b3d --- /dev/null +++ b/resources/scripts/admin/components/dropdowns/MailSenderIndexDropdown.vue @@ -0,0 +1,111 @@ + + + diff --git a/resources/scripts/admin/components/modal-components/MailSenderModal.vue b/resources/scripts/admin/components/modal-components/MailSenderModal.vue new file mode 100644 index 000000000..d1532644b --- /dev/null +++ b/resources/scripts/admin/components/modal-components/MailSenderModal.vue @@ -0,0 +1,290 @@ + + + diff --git a/resources/scripts/admin/components/modal-components/MailSenderTestModal.vue b/resources/scripts/admin/components/modal-components/MailSenderTestModal.vue new file mode 100644 index 000000000..06c4e39a4 --- /dev/null +++ b/resources/scripts/admin/components/modal-components/MailSenderTestModal.vue @@ -0,0 +1,208 @@ + + + diff --git a/resources/scripts/admin/stores/mail-sender.js b/resources/scripts/admin/stores/mail-sender.js new file mode 100644 index 000000000..588d52417 --- /dev/null +++ b/resources/scripts/admin/stores/mail-sender.js @@ -0,0 +1,209 @@ +import axios from 'axios' +import { defineStore } from 'pinia' +import { useNotificationStore } from '@/scripts/stores/notification' +import { handleError } from '@/scripts/helpers/error-handling' +import mailSenderStub from '@/scripts/admin/stub/mail-sender.js' + +export const useMailSenderStore = (useWindow = false) => { + const pre_t = 'settings.mail_sender' + const defineStoreFunc = useWindow ? window.pinia.defineStore : defineStore + const { global } = window.i18n + + return defineStoreFunc({ + id: 'mailSender', + + state: () => ({ + mailSenders: [], + mail_drivers: [], // list of mail drivers + currentMailSender: { ...mailSenderStub.basicConfig }, + smtpConfig: { ...mailSenderStub.smtpConfig }, + mailgunConfig: { ...mailSenderStub.mailgunConfig }, + sesConfig: { ...mailSenderStub.sesConfig }, + mail_encryptions: ['none', 'tls', 'ssl', 'starttls'], + }), + + getters: { + isEdit: (state) => (state.currentMailSender.id ? true : false), + }, + + actions: { + resetCurrentMailSender() { + this.currentMailSender = { ...mailSenderStub.basicConfig } + this.smtpConfig = { ...mailSenderStub.smtpConfig } + this.mailgunConfig = { ...mailSenderStub.mailgunConfig } + this.sesConfig = { ...mailSenderStub.sesConfig } + }, + + fetchMailDrivers() { + return new Promise((resolve, reject) => { + axios + .get('/api/v1/mail-drivers') + .then((response) => { + if (response.data) { + this.mail_drivers = response.data + } + resolve(response) + }) + .catch((err) => { + handleError(err) + reject(err) + }) + }) + }, + + fetchMailSenderList(params) { + return new Promise((resolve, reject) => { + axios + .get(`/api/v1/mail-senders`, { params }) + .then((response) => { + resolve(response) + }) + .catch((err) => { + handleError(err) + reject(err) + }) + }) + }, + + fetchMailSenders(params) { + return new Promise((resolve, reject) => { + axios + .get(`/api/v1/mail-sender`, { params }) + .then((response) => { + this.mailSenders = response.data.data + resolve(response) + }) + .catch((err) => { + handleError(err) + reject(err) + }) + }) + }, + + fetchMailSender(id) { + return new Promise((resolve, reject) => { + axios + .get(`/api/v1/mail-sender/${id}`) + .then((response) => { + this.currentMailSender = response.data.data + if (response.data.data.settings) { + var settings = response.data.data.settings + switch (response.data.data.driver) { + case 'smtp': + this.smtpConfig = settings + break + case 'mailgun': + this.mailgunConfig = settings + break + case 'ses': + this.sesConfig = settings + break + } + } + resolve(response) + }) + .catch((err) => { + handleError(err) + reject(err) + }) + }) + }, + + addMailSender(data) { + const notificationStore = useNotificationStore() + return new Promise((resolve, reject) => { + axios + .post('/api/v1/mail-sender', data) + .then((response) => { + this.mailSenders.push(response.data.data) + notificationStore.showNotification({ + type: 'success', + message: global.t(`${pre_t}.created_message`), + }) + resolve(response) + }) + .catch((err) => { + handleError(err) + reject(err) + }) + }) + }, + + updateMailSender(data) { + const notificationStore = useNotificationStore() + return new Promise((resolve, reject) => { + axios + .put(`/api/v1/mail-sender/${data.id}`, data) + .then((response) => { + if (response.data) { + let pos = this.mailSenders.findIndex( + (mailSender) => mailSender.id === response.data.data.id + ) + this.mailSenders[pos] = data + notificationStore.showNotification({ + type: 'success', + message: global.t(`${pre_t}.updated_message`), + }) + } + resolve(response) + }) + .catch((err) => { + handleError(err) + reject(err) + }) + }) + }, + + deleteMailSender(id) { + return new Promise((resolve, reject) => { + axios + .delete(`/api/v1/mail-sender/${id}`) + .then((response) => { + if (response.data.success) { + let index = this.mailSenders.findIndex( + (mailSender) => mailSender.id === id + ) + this.mailSenders.splice(index, 1) + const notificationStore = useNotificationStore() + notificationStore.showNotification({ + type: 'success', + message: global.t(`${pre_t}.deleted_message`), + }) + } + resolve(response) + }) + .catch((err) => { + handleError(err) + reject(err) + }) + }) + }, + + sendTestMail(data) { + return new Promise((resolve, reject) => { + axios + .post('/api/v1/mail-test', data) + .then((response) => { + const notificationStore = useNotificationStore() + if (response.data.success) { + notificationStore.showNotification({ + type: 'success', + message: global.t('general.send_mail_successfully'), + }) + } else { + notificationStore.showNotification({ + type: 'error', + message: global.t('validation.something_went_wrong'), + }) + } + resolve(response) + }) + .catch((err) => { + handleError(err) + reject(err) + }) + }) + }, + }, + })() +} diff --git a/resources/scripts/admin/stores/users.js b/resources/scripts/admin/stores/users.js index 30a5bfd41..8e337f70c 100644 --- a/resources/scripts/admin/stores/users.js +++ b/resources/scripts/admin/stores/users.js @@ -25,6 +25,7 @@ export const useUsersStore = (useWindow = false) => { password: null, phone: null, companies: [], + sender_id: null, }, }), diff --git a/resources/scripts/admin/stub/customer.js b/resources/scripts/admin/stub/customer.js index 55512add5..512c96417 100644 --- a/resources/scripts/admin/stub/customer.js +++ b/resources/scripts/admin/stub/customer.js @@ -15,5 +15,6 @@ export default function () { customFields: [], fields: [], enable_portal: false, + mail_sender_id: null, } } diff --git a/resources/scripts/admin/stub/mail-sender.js b/resources/scripts/admin/stub/mail-sender.js new file mode 100644 index 000000000..3e608a183 --- /dev/null +++ b/resources/scripts/admin/stub/mail-sender.js @@ -0,0 +1,31 @@ +export default { + basicConfig: { + name: '', + from_name: '', + from_address: '', + cc: '', + bcc: '', + is_default: false, + driver: 'smtp', // 'smtp', 'mail', 'sendmail', 'mailgun', 'ses' + settings: '', + }, + smtpConfig: { + host: '', + port: null, + username: '', + password: '', + encryption: 'tls', // 'tls', 'ssl', 'starttls' + }, + mailgunConfig: { + domain: '', + secret: '', + endpoint: '', + }, + sesConfig: { + host: '', + port: null, + encryption: 'tls', // 'tls', 'ssl', 'starttls' + ses_key: '', + ses_secret: '', + }, +} diff --git a/resources/scripts/admin/views/customers/Create.vue b/resources/scripts/admin/views/customers/Create.vue index a40a2a6b0..eea559ae7 100644 --- a/resources/scripts/admin/views/customers/Create.vue +++ b/resources/scripts/admin/views/customers/Create.vue @@ -256,6 +256,31 @@ /> + + + + @@ -601,11 +626,13 @@ import CustomerCustomFields from '@/scripts/admin/components/custom-fields/Creat import { useGlobalStore } from '@/scripts/admin/stores/global' import CopyInputField from '@/scripts/admin/components/CopyInputField.vue' import { useCompanyStore } from '@/scripts/admin/stores/company' +import { useMailSenderStore } from '@/scripts/admin/stores/mail-sender' const customerStore = useCustomerStore() const customFieldStore = useCustomFieldStore() const globalStore = useGlobalStore() const companyStore = useCompanyStore() +const mailSenderStore = useMailSenderStore() const customFieldValidationScope = 'customFields' @@ -617,6 +644,7 @@ const route = useRoute() let isFetchingInitialData = ref(false) let isShowPassword = ref(false) let isShowConfirmPassword = ref(false) +const mailSenders = ref(null) let active = ref(false) const isSaving = ref(false) @@ -679,6 +707,9 @@ const rules = computed(() => { website: { url: helpers.withMessage(t('validation.invalid_url'), url), }, + mail_sender_id: { + required: helpers.withMessage(t('validation.required'), required), + }, billing: { address_street_1: { maxLength: helpers.withMessage( @@ -722,6 +753,20 @@ const v$ = useVuelidate(rules, customerStore, { $scope: customFieldValidationScope, }) +onMounted(async () => { + await mailSenderStore.fetchMailSenders() + let mailSenderData = await mailSenderStore.fetchMailSenderList() + if (mailSenderData.data) { + mailSenders.value = mailSenderData.data.data + let defaultMailSender = mailSenderData.data.data.find( + (mailSender) => mailSender.is_default == true + ) + customerStore.currentCustomer.mail_sender_id = defaultMailSender + ? defaultMailSender.id + : null + } +}) + customerStore.resetCurrentCustomer() customerStore.fetchCustomerInitialSettings(isEdit.value) diff --git a/resources/scripts/admin/views/settings/mail-sender/Index.vue b/resources/scripts/admin/views/settings/mail-sender/Index.vue new file mode 100644 index 000000000..d46cf160e --- /dev/null +++ b/resources/scripts/admin/views/settings/mail-sender/Index.vue @@ -0,0 +1,136 @@ + + + diff --git a/resources/scripts/admin/views/settings/mail-sender/MailgunDriver.vue b/resources/scripts/admin/views/settings/mail-sender/MailgunDriver.vue new file mode 100644 index 000000000..1889c9f04 --- /dev/null +++ b/resources/scripts/admin/views/settings/mail-sender/MailgunDriver.vue @@ -0,0 +1,104 @@ + + + diff --git a/resources/scripts/admin/views/settings/mail-sender/SesDriver.vue b/resources/scripts/admin/views/settings/mail-sender/SesDriver.vue new file mode 100644 index 000000000..454bdda67 --- /dev/null +++ b/resources/scripts/admin/views/settings/mail-sender/SesDriver.vue @@ -0,0 +1,143 @@ + + + diff --git a/resources/scripts/admin/views/settings/mail-sender/SmtpDriver.vue b/resources/scripts/admin/views/settings/mail-sender/SmtpDriver.vue new file mode 100644 index 000000000..36a387b03 --- /dev/null +++ b/resources/scripts/admin/views/settings/mail-sender/SmtpDriver.vue @@ -0,0 +1,120 @@ + + + diff --git a/resources/scripts/admin/views/users/Create.vue b/resources/scripts/admin/views/users/Create.vue index e909601ff..3219bf8ac 100644 --- a/resources/scripts/admin/views/users/Create.vue +++ b/resources/scripts/admin/views/users/Create.vue @@ -128,6 +128,29 @@ + + + + diff --git a/resources/scripts/admin/components/modal-components/MailSenderModal.vue b/resources/scripts/admin/components/modal-components/MailSenderModal.vue index d1532644b..6a942ce56 100644 --- a/resources/scripts/admin/components/modal-components/MailSenderModal.vue +++ b/resources/scripts/admin/components/modal-components/MailSenderModal.vue @@ -171,19 +171,14 @@ const loadMailDriver = computed(() => { switch (mailSenderStore.currentMailSender.driver) { case 'smtp': return SmtpDriver - break case 'mail': return false - break case 'sendmail': return false - break case 'mailgun': return MailgunDriver - break case 'ses': return SesDriver - break default: return false } diff --git a/resources/scripts/admin/components/modal-components/SendEstimateModal.vue b/resources/scripts/admin/components/modal-components/SendEstimateModal.vue index 795cdfebb..640c0c3c4 100644 --- a/resources/scripts/admin/components/modal-components/SendEstimateModal.vue +++ b/resources/scripts/admin/components/modal-components/SendEstimateModal.vue @@ -16,18 +16,28 @@
-
+ +
-
+ +
+ + + +
+
@@ -75,6 +124,7 @@ { }) const rules = { - from: { + mail_sender_id: { required: helpers.withMessage(t('validation.required'), required), - email: helpers.withMessage(t('validation.email_incorrect'), email), }, to: { required: helpers.withMessage(t('validation.required'), required), @@ -207,20 +262,26 @@ function cancelPreview() { } async function setInitialData() { - let admin = await companyStore.fetchBasicMailConfig() - estimateMailForm.id = modalStore.id - - if (admin.data) { - estimateMailForm.from = admin.data.from_mail - } - if (modalData.value) { estimateMailForm.to = modalData.value.customer.email } estimateMailForm.body = companyStore.selectedCompanySettings.estimate_mail_body + + isFetchingInitialData.value = true + let mailSenderData = await mailSenderStore.fetchMailSenderList() + if (mailSenderData.data) { + mailSenders.value = mailSenderData.data.data + let defaultMailSender = mailSenderData.data.data.find( + (mailSender) => mailSender.is_default == true + ) + estimateMailForm.mail_sender_id = defaultMailSender + ? defaultMailSender.id + : null + isFetchingInitialData.value = false + } } async function submitForm() { @@ -274,4 +335,18 @@ function closeSendEstimateModal() { templateUrl.value = null }, 300) } + +function getTickImage() { + const imgUrl = new URL('/img/tick.png', import.meta.url) + return imgUrl +} + +const isMailSenderExist = computed(() => { + return mailSenders.value && mailSenders.value.length +}) + +function gotoMailSender() { + closeSendEstimateModal() + router.push('/admin/settings/mail-sender') +} diff --git a/resources/scripts/admin/components/modal-components/SendInvoiceModal.vue b/resources/scripts/admin/components/modal-components/SendInvoiceModal.vue index 63e7e6af4..c685b8c6c 100644 --- a/resources/scripts/admin/components/modal-components/SendInvoiceModal.vue +++ b/resources/scripts/admin/components/modal-components/SendInvoiceModal.vue @@ -15,18 +15,28 @@
-
+ +
-
+ +
+ + + +
+
@@ -77,6 +126,7 @@ {{ $t('general.cancel') }} { }) const rules = { - from: { + mail_sender_id: { required: helpers.withMessage(t('validation.required'), required), - email: helpers.withMessage(t('validation.email_incorrect'), email), }, to: { required: helpers.withMessage(t('validation.required'), required), @@ -224,19 +279,25 @@ function cancelPreview() { } async function setInitialData() { - let admin = await companyStore.fetchBasicMailConfig() - invoiceMailForm.id = modalStore.id - - if (admin.data) { - invoiceMailForm.from = admin.data.from_mail - } - if (modalData.value) { invoiceMailForm.to = modalData.value.customer.email } invoiceMailForm.body = companyStore.selectedCompanySettings.invoice_mail_body + + isFetchingInitialData.value = true + let mailSenderData = await mailSenderStore.fetchMailSenderList() + if (mailSenderData.data) { + mailSenders.value = mailSenderData.data.data + let defaultMailSender = mailSenderData.data.data.find( + (mailSender) => mailSender.is_default == true + ) + invoiceMailForm.mail_sender_id = defaultMailSender + ? defaultMailSender.id + : null + isFetchingInitialData.value = false + } } async function submitForm() { @@ -287,4 +348,18 @@ function closeSendInvoiceModal() { templateUrl.value = null }, 300) } + +function getTickImage() { + const imgUrl = new URL('/img/tick.png', import.meta.url) + return imgUrl +} + +const isMailSenderExist = computed(() => { + return mailSenders.value && mailSenders.value.length +}) + +function gotoMailSender() { + closeSendInvoiceModal() + router.push('/admin/settings/mail-sender') +} diff --git a/resources/scripts/admin/components/modal-components/SendPaymentModal.vue b/resources/scripts/admin/components/modal-components/SendPaymentModal.vue index 6c786312b..b25e9b220 100644 --- a/resources/scripts/admin/components/modal-components/SendPaymentModal.vue +++ b/resources/scripts/admin/components/modal-components/SendPaymentModal.vue @@ -15,18 +15,28 @@
-
+ +
-
+ +
+ + + +
+
@@ -77,6 +126,7 @@ {{ $t('general.cancel') }} { }) const rules = { - from: { + mail_sender_id: { required: helpers.withMessage(t('validation.required'), required), - email: helpers.withMessage(t('validation.email_incorrect'), email), }, to: { required: helpers.withMessage(t('validation.required'), required), @@ -221,18 +276,25 @@ function cancelPreview() { } async function setInitialData() { - let admin = await companyStore.fetchBasicMailConfig() paymentMailForm.id = modalStore.id - - if (admin.data) { - paymentMailForm.from = admin.data.from_mail - } - if (modalData.value) { paymentMailForm.to = modalData.value.customer.email } paymentMailForm.body = companyStore.selectedCompanySettings.payment_mail_body + + isFetchingInitialData.value = true + let mailSenderData = await mailSenderStore.fetchMailSenderList() + if (mailSenderData.data) { + mailSenders.value = mailSenderData.data.data + let defaultMailSender = mailSenderData.data.data.find( + (mailSender) => mailSender.is_default == true + ) + paymentMailForm.mail_sender_id = defaultMailSender + ? defaultMailSender.id + : null + isFetchingInitialData.value = false + } } async function sendPaymentData() { @@ -280,4 +342,18 @@ function closeSendPaymentModal() { modalStore.resetModalData() }, 300) } + +function getTickImage() { + const imgUrl = new URL('/img/tick.png', import.meta.url) + return imgUrl +} + +const isMailSenderExist = computed(() => { + return mailSenders.value && mailSenders.value.length +}) + +function gotoMailSender() { + closeSendPaymentModal() + router.push('/admin/settings/mail-sender') +} diff --git a/resources/scripts/admin/stores/mail-driver.js b/resources/scripts/admin/stores/mail-driver.js deleted file mode 100644 index f6086efd2..000000000 --- a/resources/scripts/admin/stores/mail-driver.js +++ /dev/null @@ -1,146 +0,0 @@ -import axios from 'axios' -import { defineStore } from 'pinia' -import { useNotificationStore } from '@/scripts/stores/notification' -import { handleError } from '@/scripts/helpers/error-handling' - -export const useMailDriverStore = (useWindow = false) => { - const defineStoreFunc = useWindow ? window.pinia.defineStore : defineStore - const { global } = window.i18n - - return defineStoreFunc({ - id: 'mail-driver', - - state: () => ({ - mailConfigData: null, - mail_driver: 'smtp', - mail_drivers: [], - - basicMailConfig: { - mail_driver: '', - mail_host: '', - from_mail: '', - from_name: '', - }, - - mailgunConfig: { - mail_driver: '', - mail_mailgun_domain: '', - mail_mailgun_secret: '', - mail_mailgun_endpoint: '', - from_mail: '', - from_name: '', - }, - - sesConfig: { - mail_driver: '', - mail_host: '', - mail_port: null, - mail_ses_key: '', - mail_ses_secret: '', - mail_encryption: 'tls', - from_mail: '', - from_name: '', - }, - - smtpConfig: { - mail_driver: '', - mail_host: '', - mail_port: null, - mail_username: '', - mail_password: '', - mail_encryption: 'tls', - from_mail: '', - from_name: '', - }, - }), - - actions: { - fetchMailDrivers() { - return new Promise((resolve, reject) => { - axios - .get('/api/v1/mail/drivers') - .then((response) => { - if (response.data) { - this.mail_drivers = response.data - } - resolve(response) - }) - .catch((err) => { - handleError(err) - reject(err) - }) - }) - }, - - fetchMailConfig() { - return new Promise((resolve, reject) => { - axios - .get('/api/v1/mail/config') - .then((response) => { - if (response.data) { - this.mailConfigData = response.data - this.mail_driver = response.data.mail_driver - } - resolve(response) - }) - .catch((err) => { - handleError(err) - reject(err) - }) - }) - }, - - updateMailConfig(data) { - return new Promise((resolve, reject) => { - axios - .post('/api/v1/mail/config', data) - .then((response) => { - const notificationStore = useNotificationStore() - if (response.data.success) { - notificationStore.showNotification({ - type: 'success', - message: global.t('wizard.success.' + response.data.success), - }) - } else { - notificationStore.showNotification({ - type: 'error', - message: global.t('wizard.errors.' + response.data.error), - }) - } - resolve(response) - }) - .catch((err) => { - handleError(err) - reject(err) - }) - }) - }, - - sendTestMail(data) { - return new Promise((resolve, reject) => { - axios - .post('/api/v1/mail/test', data) - .then((response) => { - const notificationStore = useNotificationStore() - if (response.data.success) { - notificationStore.showNotification({ - type: 'success', - message: global.t('general.send_mail_successfully'), - }) - } else { - notificationStore.showNotification({ - type: 'error', - message: global.t('validation.something_went_wrong'), - }) - } - resolve(response) - }) - .catch((err) => { - handleError(err) - reject(err) - }) - }) - }, - }, - })() -} diff --git a/resources/scripts/admin/stores/mail-sender.js b/resources/scripts/admin/stores/mail-sender.js index 588d52417..cfaed7993 100644 --- a/resources/scripts/admin/stores/mail-sender.js +++ b/resources/scripts/admin/stores/mail-sender.js @@ -88,15 +88,18 @@ export const useMailSenderStore = (useWindow = false) => { this.currentMailSender = response.data.data if (response.data.data.settings) { var settings = response.data.data.settings + const encryptionNone = settings.encryption == '' || settings.encryption == undefined switch (response.data.data.driver) { case 'smtp': this.smtpConfig = settings + encryptionNone ? this.smtpConfig.encryption = 'none' : '' break case 'mailgun': this.mailgunConfig = settings break case 'ses': this.sesConfig = settings + encryptionNone ? this.sesConfig.encryption = 'none' : '' break } } diff --git a/resources/scripts/admin/views/customers/Create.vue b/resources/scripts/admin/views/customers/Create.vue index eea559ae7..141ada458 100644 --- a/resources/scripts/admin/views/customers/Create.vue +++ b/resources/scripts/admin/views/customers/Create.vue @@ -708,7 +708,10 @@ const rules = computed(() => { url: helpers.withMessage(t('validation.invalid_url'), url), }, mail_sender_id: { - required: helpers.withMessage(t('validation.required'), required), + required: helpers.withMessage( + t('validation.required'), + requiredIf(customerStore.currentCustomer.enable_portal == true) + ), }, billing: { address_street_1: { diff --git a/resources/scripts/admin/views/installation/Step5EmailConfig.vue b/resources/scripts/admin/views/installation/Step5EmailConfig.vue index ad2813d47..8d6a0ca35 100644 --- a/resources/scripts/admin/views/installation/Step5EmailConfig.vue +++ b/resources/scripts/admin/views/installation/Step5EmailConfig.vue @@ -3,75 +3,259 @@ :title="$t('wizard.mail.mail_config')" :description="$t('wizard.mail.mail_config_desc')" > - - + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + {{ $t('wizard.save_cont') }} + +
- diff --git a/resources/scripts/admin/views/settings/mail-driver/BasicMailDriver.vue b/resources/scripts/admin/views/settings/mail-driver/BasicMailDriver.vue deleted file mode 100644 index 031c9412a..000000000 --- a/resources/scripts/admin/views/settings/mail-driver/BasicMailDriver.vue +++ /dev/null @@ -1,158 +0,0 @@ - - - diff --git a/resources/scripts/admin/views/settings/mail-driver/MailgunMailDriver.vue b/resources/scripts/admin/views/settings/mail-driver/MailgunMailDriver.vue deleted file mode 100644 index fa2ebbcf4..000000000 --- a/resources/scripts/admin/views/settings/mail-driver/MailgunMailDriver.vue +++ /dev/null @@ -1,247 +0,0 @@ - - - diff --git a/resources/scripts/admin/views/settings/mail-driver/SesMailDriver.vue b/resources/scripts/admin/views/settings/mail-driver/SesMailDriver.vue deleted file mode 100644 index 5eae5a074..000000000 --- a/resources/scripts/admin/views/settings/mail-driver/SesMailDriver.vue +++ /dev/null @@ -1,294 +0,0 @@ - - - diff --git a/resources/scripts/admin/views/settings/mail-driver/SmtpMailDriver.vue b/resources/scripts/admin/views/settings/mail-driver/SmtpMailDriver.vue deleted file mode 100644 index dbb947f2d..000000000 --- a/resources/scripts/admin/views/settings/mail-driver/SmtpMailDriver.vue +++ /dev/null @@ -1,275 +0,0 @@ - - - diff --git a/resources/scripts/locales/en.json b/resources/scripts/locales/en.json index 6265fec3c..7d44e0a3f 100644 --- a/resources/scripts/locales/en.json +++ b/resources/scripts/locales/en.json @@ -101,6 +101,7 @@ "login_successfully": "Logged in successfully!", "logged_out_successfully": "Logged out successfully", "mark_as_default": "Mark as default", + "select_option": "Select option", "send_test_mail": "Send Test Mail" }, "dashboard": { @@ -1572,4 +1573,4 @@ "pdf_ship_to": "Ship to,", "pdf_received_from": "Received from:", "pdf_tax_label": "Tax" -} \ No newline at end of file +} From 2bea727d193c1a216bb5cdd130219ec6c7f66b4a Mon Sep 17 00:00:00 2001 From: yogesh-gohil Date: Tue, 14 Mar 2023 17:45:58 +0530 Subject: [PATCH 5/7] fix migration and api changes --- app/Models/MailSender.php | 5 -- ...03_10_125202_create_mail_senders_table.php | 88 +++++++++---------- resources/scripts/admin/stores/mail-sender.js | 10 +-- routes/api.php | 4 +- 4 files changed, 46 insertions(+), 61 deletions(-) diff --git a/app/Models/MailSender.php b/app/Models/MailSender.php index 625baaf65..1dedd9aba 100644 --- a/app/Models/MailSender.php +++ b/app/Models/MailSender.php @@ -25,11 +25,6 @@ public function company() return $this->belongsTo(Company::class); } - public function setSettingsAttribute($value) - { - $this->attributes['settings'] = json_encode($value); - } - public function scopeWhereOrder($query, $orderByField, $orderBy) { $query->orderBy($orderByField, $orderBy); diff --git a/database/migrations/2023_03_10_125202_create_mail_senders_table.php b/database/migrations/2023_03_10_125202_create_mail_senders_table.php index 4573a593a..8e1cf725a 100644 --- a/database/migrations/2023_03_10_125202_create_mail_senders_table.php +++ b/database/migrations/2023_03_10_125202_create_mail_senders_table.php @@ -32,62 +32,54 @@ public function up() $table->timestamps(); }); - $user = User::where('role', 'super admin')->first(); + $users = User::where('role', 'super admin')->get(); - if ($user) { - $users = User::where('role', 'super admin')->get(); - - foreach ($users as $user) { - $user->allow('view-mail-sender'); - $user->allow('create-mail-sender'); - $user->allow('edit-mail-sender'); - $user->allow('delete-mail-sender'); - BouncerFacade::allow($user)->toOwn(MailSender::class); - } + foreach ($users as $user) { + BouncerFacade::allow($user)->toManage(MailSender::class); + } - $companies = Company::all(); + $companies = Company::all(); - $companies->map(function ($company) { - if (env('MAIL_DRIVER') == 'smtp') { - $settings = [ - 'MAIL_HOST' => env('MAIL_HOST'), - 'MAIL_PORT' => env('MAIL_PORT'), - 'MAIL_USERNAME' => env('MAIL_USERNAME'), - 'MAIL_PASSWORD' => env('MAIL_PASSWORD'), - 'MAIL_ENCRYPTION' => env('MAIL_ENCRYPTION') - ]; - $this->insertData($settings, $company->id); - } + $companies->map(function ($company) { + if (env('MAIL_DRIVER') == 'smtp') { + $settings = [ + 'MAIL_HOST' => env('MAIL_HOST'), + 'MAIL_PORT' => env('MAIL_PORT'), + 'MAIL_USERNAME' => env('MAIL_USERNAME'), + 'MAIL_PASSWORD' => env('MAIL_PASSWORD'), + 'MAIL_ENCRYPTION' => env('MAIL_ENCRYPTION') + ]; + $this->createSender($settings, $company->id); + } - if (env('MAIL_DRIVER') == 'mail' || env('MAIL_DRIVER') == 'sendmail') { - $this->insertData(null, $company->id); - } + if (env('MAIL_DRIVER') == 'mail' || env('MAIL_DRIVER') == 'sendmail') { + $this->createSender(null, $company->id); + } - if (env('MAIL_DRIVER') == 'mailgun') { - $settings = [ - 'MAILGUN_DOMAIN' => env('MAILGUN_DOMAIN'), - 'MAILGUN_SECRET' => env('MAILGUN_SECRET'), - 'MAILGUN_ENDPOINT' => env('MAILGUN_ENDPOINT'), - ]; - $this->insertData($settings, $company->id); - } + if (env('MAIL_DRIVER') == 'mailgun') { + $settings = [ + 'MAILGUN_DOMAIN' => env('MAILGUN_DOMAIN'), + 'MAILGUN_SECRET' => env('MAILGUN_SECRET'), + 'MAILGUN_ENDPOINT' => env('MAILGUN_ENDPOINT'), + ]; + $this->createSender($settings, $company->id); + } - if (env('MAIL_DRIVER') == 'ses') { - $settings = [ - 'MAIL_HOST' => env('MAIL_HOST'), - 'MAIL_PORT' => env('MAIL_PORT'), - 'MAIL_ENCRYPTION' => env('MAIL_ENCRYPTION'), - 'MAILGUN_DOMAIN' => env('MAILGUN_DOMAIN'), - 'SES_KEY' => env('SES_KEY'), - 'SES_SECRET' => env('SES_SECRET'), - ]; - $this->insertData($settings, $company->id); - } - }); - } + if (env('MAIL_DRIVER') == 'ses') { + $settings = [ + 'MAIL_HOST' => env('MAIL_HOST'), + 'MAIL_PORT' => env('MAIL_PORT'), + 'MAIL_ENCRYPTION' => env('MAIL_ENCRYPTION'), + 'MAILGUN_DOMAIN' => env('MAILGUN_DOMAIN'), + 'SES_KEY' => env('SES_KEY'), + 'SES_SECRET' => env('SES_SECRET'), + ]; + $this->createSender($settings, $company->id); + } + }); } - public function insertData($settings, $company_id) + public function createSender($settings, $company_id) { $data = [ 'name' => env('MAIL_DRIVER'), diff --git a/resources/scripts/admin/stores/mail-sender.js b/resources/scripts/admin/stores/mail-sender.js index cfaed7993..eff7076db 100644 --- a/resources/scripts/admin/stores/mail-sender.js +++ b/resources/scripts/admin/stores/mail-sender.js @@ -68,7 +68,7 @@ export const useMailSenderStore = (useWindow = false) => { fetchMailSenders(params) { return new Promise((resolve, reject) => { axios - .get(`/api/v1/mail-sender`, { params }) + .get(`/api/v1/mail-senders`, { params }) .then((response) => { this.mailSenders = response.data.data resolve(response) @@ -83,7 +83,7 @@ export const useMailSenderStore = (useWindow = false) => { fetchMailSender(id) { return new Promise((resolve, reject) => { axios - .get(`/api/v1/mail-sender/${id}`) + .get(`/api/v1/mail-senders/${id}`) .then((response) => { this.currentMailSender = response.data.data if (response.data.data.settings) { @@ -116,7 +116,7 @@ export const useMailSenderStore = (useWindow = false) => { const notificationStore = useNotificationStore() return new Promise((resolve, reject) => { axios - .post('/api/v1/mail-sender', data) + .post('/api/v1/mail-senders', data) .then((response) => { this.mailSenders.push(response.data.data) notificationStore.showNotification({ @@ -136,7 +136,7 @@ export const useMailSenderStore = (useWindow = false) => { const notificationStore = useNotificationStore() return new Promise((resolve, reject) => { axios - .put(`/api/v1/mail-sender/${data.id}`, data) + .put(`/api/v1/mail-senders/${data.id}`, data) .then((response) => { if (response.data) { let pos = this.mailSenders.findIndex( @@ -160,7 +160,7 @@ export const useMailSenderStore = (useWindow = false) => { deleteMailSender(id) { return new Promise((resolve, reject) => { axios - .delete(`/api/v1/mail-sender/${id}`) + .delete(`/api/v1/mail-senders/${id}`) .then((response) => { if (response.data.success) { let index = this.mailSenders.findIndex( diff --git a/routes/api.php b/routes/api.php index 579636ae8..1a057a9e8 100644 --- a/routes/api.php +++ b/routes/api.php @@ -403,14 +403,12 @@ // Mails //---------------------------------- - Route::apiResource('mail-sender', MailSenderController::class); + Route::apiResource('mail-senders', MailSenderController::class); Route::get('/mail-drivers', [MailConfigurationController::class, 'getMailDrivers']); Route::post('/mail-test', [MailConfigurationController::class, 'TestMailDriver']); - Route::get('mail-senders', GetAllMailSendersController::class); - Route::get('/company/mail/config', GetCompanyMailConfigurationController::class); Route::apiResource('notes', NotesController::class); From aececb85753d887603f6060e709c25af22fe8174 Mon Sep 17 00:00:00 2001 From: yashkanakiya Date: Thu, 16 Mar 2023 11:48:15 +0530 Subject: [PATCH 6/7] refactor mail sender --- .../Admin/MailSender/MailSenderController.php | 12 ++--- app/Http/Requests/MailSenderRequest.php | 2 +- .../dropdowns/MailSenderIndexDropdown.vue | 2 +- .../modal-components/CustomerModal.vue | 3 +- .../modal-components/MailSenderModal.vue | 2 + .../modal-components/SendEstimateModal.vue | 4 +- .../modal-components/SendInvoiceModal.vue | 4 +- .../modal-components/SendPaymentModal.vue | 4 +- resources/scripts/admin/stores/mail-sender.js | 16 ++---- .../scripts/admin/views/customers/Create.vue | 52 +------------------ .../views/installation/Step5EmailConfig.vue | 31 ++--------- .../scripts/admin/views/users/Create.vue | 43 --------------- .../scripts/components/base/BaseModal.vue | 1 - 13 files changed, 26 insertions(+), 150 deletions(-) diff --git a/app/Http/Controllers/V1/Admin/MailSender/MailSenderController.php b/app/Http/Controllers/V1/Admin/MailSender/MailSenderController.php index 9b30ec5b0..697a660d5 100644 --- a/app/Http/Controllers/V1/Admin/MailSender/MailSenderController.php +++ b/app/Http/Controllers/V1/Admin/MailSender/MailSenderController.php @@ -43,10 +43,10 @@ public function store(MailSenderRequest $request) $mailConfiguration = MailSender::where('company_id', $request->header('company')) ->where('is_default', true) - ->count(); + ->first(); - if ($mailConfiguration > 0 && $request['is_default'] == true) { - return respondJson('default_record_exists', 'Default mail sender already exist'); + if ($mailConfiguration && $request['is_default'] == true) { + $mailConfiguration->update(['is_default' => false]); } $mailSender = MailSender::createFromRequest($request); @@ -81,10 +81,10 @@ public function update(MailSenderRequest $request, MailSender $mailSender) $mailConfiguration = MailSender::where('company_id', $request->header('company')) ->where('is_default', true) ->where('id', '<>', $mailSender->id) - ->count(); + ->first(); - if ($mailConfiguration > 0 && $request['is_default'] == true) { - return respondJson('default_record_exists', 'Default mail sender already exist'); + if ($mailConfiguration && $request['is_default'] == true) { + $mailConfiguration->update(['is_default' => false]); } $mailSender->updateFromRequest($request); diff --git a/app/Http/Requests/MailSenderRequest.php b/app/Http/Requests/MailSenderRequest.php index 2b2936ba3..7db202c83 100644 --- a/app/Http/Requests/MailSenderRequest.php +++ b/app/Http/Requests/MailSenderRequest.php @@ -72,7 +72,7 @@ public function getMailSenderPayload() { $data = $this->validated(); - if ($data['settings']['encryption'] == 'none') { + if ($data['settings'] && $data['settings']['encryption'] == 'none') { $data['settings']['encryption'] = ''; } diff --git a/resources/scripts/admin/components/dropdowns/MailSenderIndexDropdown.vue b/resources/scripts/admin/components/dropdowns/MailSenderIndexDropdown.vue index 365382b3d..401119293 100644 --- a/resources/scripts/admin/components/dropdowns/MailSenderIndexDropdown.vue +++ b/resources/scripts/admin/components/dropdowns/MailSenderIndexDropdown.vue @@ -17,7 +17,7 @@ - +