From 2215f40ec3514b6f0f7529a9148d58b89311667d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Benjamin=20Beganovi=C4=87?= Date: Thu, 16 Apr 2020 23:19:21 +0200 Subject: [PATCH] Password protected invoices (#3635) * Password protected invoices (wip) * Add support for invitations * Update comments & php-cs-fixer * Add Forgot your password --- .../ClientPortal/EntityViewController.php | 114 ++++++++++++++++++ resources/lang/en/texts.php | 1 + .../auth/passwords/request.blade.php | 2 +- .../ninja2020/view_entity/index.blade.php | 58 +++++++++ .../ninja2020/view_entity/password.blade.php | 31 +++++ routes/client.php | 4 + 6 files changed, 209 insertions(+), 1 deletion(-) create mode 100644 app/Http/Controllers/ClientPortal/EntityViewController.php create mode 100644 resources/views/themes/ninja2020/view_entity/index.blade.php create mode 100644 resources/views/themes/ninja2020/view_entity/password.blade.php diff --git a/app/Http/Controllers/ClientPortal/EntityViewController.php b/app/Http/Controllers/ClientPortal/EntityViewController.php new file mode 100644 index 0000000000..72fc44b6bb --- /dev/null +++ b/app/Http/Controllers/ClientPortal/EntityViewController.php @@ -0,0 +1,114 @@ +entity_types)) { + abort(404); + } + + $invitation_entity = sprintf('App\\Models\\%sInvitation', ucfirst($entity_type)); + + $key = $entity_type . '_id'; + + $invitation = $invitation_entity::whereRaw("BINARY `key`= ?", [$invitation_key])->firstOrFail(); + + $contact = $invitation->contact; + + if (is_null($contact->password) || empty($contact->password)) { + return redirect("/client/password/reset?email={$contact->email}"); + } + + $entity_class = sprintf('App\\Models\\%s', ucfirst($entity_type)); + $entity = $entity_class::findOrFail($invitation->{$key}); + + if ((bool) $invitation->contact->client->getSetting('enable_client_portal_password') !== false) { + session()->flash("{$entity_type}_VIEW_{$entity->hashed_id}", true); + } + + if (!session("{$entity_type}_VIEW_{$entity->hashed_id}")) { + return redirect()->route('client.entity_view.password', compact('entity_type', 'invitation_key')); + } + + return $this->render('view_entity.index', [ + 'root' => 'themes', + 'entity' => $entity, + ]); + } + + /** + * Show the form for entering password. + * + * @param string $entity_type + * @param string $invitation_key + * + * @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View + */ + public function password(string $entity_type, string $invitation_key) + { + return $this->render('view_entity.password', [ + 'root' => 'themes', + 'entity_type' => $entity_type, + ]); + } + + /**` + * Handle the password check. + * + * @param string $entity_type + * @param string $invitation_key + * + * @return \Illuminate\Routing\Redirector|\Illuminate\Http\RedirectResponse|mixed + */ + public function handlePassword(string $entity_type, string $invitation_key) + { + if (!in_array($entity_type, $this->entity_types)) { + abort(404); + } + + $invitation_entity = sprintf('App\\Models\\%sInvitation', ucfirst($entity_type)); + + $key = $entity_type . '_id'; + + $invitation = $invitation_entity::whereRaw("BINARY `key`= ?", [$invitation_key])->firstOrFail(); + + $contact = $invitation->contact; + + $check = Hash::check(request()->password, $contact->password); + + $entity_class = sprintf('App\\Models\\%s', ucfirst($entity_type)); + + $entity = $entity_class::findOrFail($invitation->{$key}); + + if ($check) { + session()->flash("{$entity_type}_VIEW_{$entity->hashed_id}", true); + + return redirect()->route('client.entity_view', compact('entity_type', 'invitation_key')); + } + + session()->flash('PASSWORD_FAILED', true); + return back(); + } +} diff --git a/resources/lang/en/texts.php b/resources/lang/en/texts.php index 497ac8f81c..7e3be03f6d 100644 --- a/resources/lang/en/texts.php +++ b/resources/lang/en/texts.php @@ -3201,4 +3201,5 @@ return [ 'page' => 'Page', 'of' => 'Of', 'view_credit' => 'View Credit', + 'to_view_entity_password' => 'To view the :entity you need to enter password.', ]; diff --git a/resources/views/portal/ninja2020/auth/passwords/request.blade.php b/resources/views/portal/ninja2020/auth/passwords/request.blade.php index e7d9a16366..9da2833852 100644 --- a/resources/views/portal/ninja2020/auth/passwords/request.blade.php +++ b/resources/views/portal/ninja2020/auth/passwords/request.blade.php @@ -24,7 +24,7 @@ @error('email')
diff --git a/resources/views/themes/ninja2020/view_entity/index.blade.php b/resources/views/themes/ninja2020/view_entity/index.blade.php new file mode 100644 index 0000000000..8e8ed6bf8f --- /dev/null +++ b/resources/views/themes/ninja2020/view_entity/index.blade.php @@ -0,0 +1,58 @@ +@extends('portal.ninja2020.layout.clean') + +@push('head') + + + +@endpush + +@section('body') +
+
+
+ + +
+
+ @if($entity instanceof App\Models\Invoice) + + @elseif($$entity instanceof App\Models\Quote) + + @endif + +
+
+ +
+ +
+
+
+ +
+ +
+
+@endsection + +@section('footer') + +@endsection \ No newline at end of file diff --git a/resources/views/themes/ninja2020/view_entity/password.blade.php b/resources/views/themes/ninja2020/view_entity/password.blade.php new file mode 100644 index 0000000000..dd31462d37 --- /dev/null +++ b/resources/views/themes/ninja2020/view_entity/password.blade.php @@ -0,0 +1,31 @@ +@extends('portal.ninja2020.layout.clean') + +@section('body') +
+
+
+

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

+

{{ ctrans('texts.to_view_entity_password', ['entity' => $entity_type]) }}

+
+ @csrf +
+
+ + {{ trans('texts.forgot_password') }} +
+ + + @if(session('PASSWORD_FAILED')) +
+ {{ ctrans('auth.failed') }} +
+ @endif +
+
+ +
+
+
+
+
+@endsection \ No newline at end of file diff --git a/routes/client.php b/routes/client.php index 3874dcc9ec..d153c8284b 100644 --- a/routes/client.php +++ b/routes/client.php @@ -12,6 +12,10 @@ Route::post('client/password/email', 'Auth\ContactForgotPasswordController@sendR 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'); +Route::get('view/{entity_type}/{invitation_key}', 'ClientPortal\EntityViewController@index')->name('client.entity_view'); +Route::get('view/{entity_type}/{invitation_key}/password', 'ClientPortal\EntityViewController@password')->name('client.entity_view.password'); +Route::post('view/{entity_type}/{invitation_key}/password', 'ClientPortal\EntityViewController@handlePassword'); + //todo implement domain DB 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