From ff8b3421f4b999ff0aa0ae6bb82a0367ccd24075 Mon Sep 17 00:00:00 2001 From: edalzell Date: Tue, 10 May 2022 20:42:49 -0700 Subject: [PATCH 01/23] user update route --- routes/web.php | 1 + 1 file changed, 1 insertion(+) diff --git a/routes/web.php b/routes/web.php index 0839506aa7c..b92cb349fd8 100755 --- a/routes/web.php +++ b/routes/web.php @@ -34,6 +34,7 @@ Route::post('login', 'UserController@login')->name('login'); Route::get('logout', 'UserController@logout')->name('logout'); Route::post('register', 'UserController@register')->name('register'); + Route::post('update', 'UserController@update')->name('user.update'); Route::post('password/email', 'ForgotPasswordController@sendResetLinkEmail')->name('password.email'); Route::get('password/reset/{token}', 'ResetPasswordController@showResetForm')->name('password.reset'); From 111ea20e68ea9e7594a147e4023d0d19f974494f Mon Sep 17 00:00:00 2001 From: edalzell Date: Tue, 10 May 2022 20:43:25 -0700 Subject: [PATCH 02/23] form (wip) --- src/Auth/UserTags.php | 38 +++++++++++++++++++++++++++++++++++++- 1 file changed, 37 insertions(+), 1 deletion(-) diff --git a/src/Auth/UserTags.php b/src/Auth/UserTags.php index d1c1c30fdea..051a1ca7dd7 100644 --- a/src/Auth/UserTags.php +++ b/src/Auth/UserTags.php @@ -276,6 +276,42 @@ public function resetPasswordForm() return $html; } + /** + * Output a update form. + * + * Maps to {{ user:update_form }} + * + * @return string + */ + public function updateForm() + { + if (session()->has('status')) { + return $this->parse(['success' => true]); + } + + $data = $this->getFormSession(); + + if (! $this->params->has('redirect')) { + $this->params->put('redirect', request()->getPathInfo()); + } + + $knownParams = ['redirect']; + + $html = $this->formOpen(route('statamic.user.update'), 'POST', $knownParams); + + $html .= ''; + + if ($redirect = $this->params->get('redirect')) { + $html .= ''; + } + + $html .= $this->parse($data); + + $html .= $this->formClose(); + + return $html; + } + /** * Displays content if a user has permission. * @@ -424,7 +460,7 @@ public function notIn() } /** - * {@inheritdoc} + * @inheritdoc */ public function eventUrl($url, $relative = false) { From 7744387e7160e56354977dd8599f3fd0ce533126 Mon Sep 17 00:00:00 2001 From: Jack Sleight Date: Fri, 29 Jul 2022 17:10:48 +0100 Subject: [PATCH 03/23] WIP --- routes/web.php | 3 +- src/Auth/UserTags.php | 44 +++++++++-- src/Http/Controllers/UserController.php | 97 +++++++++++++++++++++++++ 3 files changed, 137 insertions(+), 7 deletions(-) diff --git a/routes/web.php b/routes/web.php index b92cb349fd8..7a43e350551 100755 --- a/routes/web.php +++ b/routes/web.php @@ -34,7 +34,8 @@ Route::post('login', 'UserController@login')->name('login'); Route::get('logout', 'UserController@logout')->name('logout'); Route::post('register', 'UserController@register')->name('register'); - Route::post('update', 'UserController@update')->name('user.update'); + Route::post('profile', 'UserController@profile')->name('profile'); + Route::post('password', 'UserController@password')->name('password'); Route::post('password/email', 'ForgotPasswordController@sendResetLinkEmail')->name('password.email'); Route::get('password/reset/{token}', 'ResetPasswordController@showResetForm')->name('password.reset'); diff --git a/src/Auth/UserTags.php b/src/Auth/UserTags.php index 051a1ca7dd7..5185a8a28a2 100644 --- a/src/Auth/UserTags.php +++ b/src/Auth/UserTags.php @@ -277,19 +277,19 @@ public function resetPasswordForm() } /** - * Output a update form. + * Output a profile form. * - * Maps to {{ user:update_form }} + * Maps to {{ user:profile_form }} * * @return string */ - public function updateForm() + public function profileForm() { if (session()->has('status')) { return $this->parse(['success' => true]); } - $data = $this->getFormSession(); + $data = $this->getFormSession('user.profile'); if (! $this->params->has('redirect')) { $this->params->put('redirect', request()->getPathInfo()); @@ -297,9 +297,41 @@ public function updateForm() $knownParams = ['redirect']; - $html = $this->formOpen(route('statamic.user.update'), 'POST', $knownParams); + $html = $this->formOpen(route('statamic.profile'), 'POST', $knownParams); - $html .= ''; + if ($redirect = $this->params->get('redirect')) { + $html .= ''; + } + + $html .= $this->parse($data); + + $html .= $this->formClose(); + + return $html; + } + + /** + * Output a password change form. + * + * Maps to {{ user:password_form }} + * + * @return string + */ + public function passwordForm() + { + if (session()->has('status')) { + return $this->parse(['success' => true]); + } + + $data = $this->getFormSession('user.password'); + + if (! $this->params->has('redirect')) { + $this->params->put('redirect', request()->getPathInfo()); + } + + $knownParams = ['redirect']; + + $html = $this->formOpen(route('statamic.password'), 'POST', $knownParams); if ($redirect = $this->params->get('redirect')) { $html .= ''; diff --git a/src/Http/Controllers/UserController.php b/src/Http/Controllers/UserController.php index d98013953fe..27eea07132c 100644 --- a/src/Http/Controllers/UserController.php +++ b/src/Http/Controllers/UserController.php @@ -11,6 +11,7 @@ use Statamic\Events\UserRegistered; use Statamic\Events\UserRegistering; use Statamic\Exceptions\SilentFormFailureException; +use Statamic\Exceptions\UnauthorizedHttpException; use Statamic\Facades\User; class UserController extends Controller @@ -69,6 +70,7 @@ public function register(Request $request) return $this->userRegistrationFailure($validator->errors()); } + // $values = array_merge($request->all(), $this->uploadAssetFiles($fields)); $values = $fields->process()->values()->except(['email', 'groups', 'roles']); $user = User::make() @@ -101,6 +103,65 @@ public function register(Request $request) return $this->userRegistrationSuccess(); } + public function profile(Request $request) + { + throw_unless($user = User::current(), new UnauthorizedHttpException(403)); + + $blueprint = User::blueprint(); + + $fields = $blueprint->fields()->addValues($request->all()); + + $fieldRules = $fields->validator()->withRules([ + 'email' => ['required', 'email', 'unique_user_value:'.$user->id()], + ])->rules(); + + $validator = Validator::make($request->all(), $fieldRules); + + if ($validator->fails()) { + $errors = $validator->errors(); + + return back()->withInput()->withErrors($errors, 'user.profile'); + } + + // $values = array_merge($request->all(), $this->uploadAssetFiles($fields)); + $values = $fields->process()->values()->except(['email', 'password', 'groups', 'roles']); + + $user->email($request->email); + foreach ($values as $key => $value) { + $user->set($key, $value); + } + + $user->save(); + + session()->flash('user.profile.success', __('Update successful.')); + + return request()->has('_redirect') ? redirect(request()->get('_redirect')) : back(); + } + + public function changePassword(Request $request) + { + throw_unless($user = User::current(), new UnauthorizedHttpException(403)); + + $validator = Validator::make($request->all(), [ + 'current_password' => ['required', 'current_password'], + 'password' => ['required', 'confirmed', PasswordDefaults::rules()], + ]); + + if ($validator->fails()) { + $errors = $validator->errors(); + + return back()->withInput()->withErrors($errors, 'user.password'); + } + + $user->password($request->password); + + $user->save(); + + session()->flash('user.password.success', __('Change successful.')); + + return request()->has('_redirect') ? redirect(request()->get('_redirect')) : back(); + } + public function username() { return 'email'; @@ -122,4 +183,40 @@ private function userRegistrationSuccess(bool $silentFailure = false) return $response; } + + protected function assetRules($fields) + { + return $fields->all() + ->filter(function ($field) { + return $field->fieldtype()->handle() === 'assets'; + }) + ->mapWithKeys(function ($field) { + return [$field->handle().'.*' => 'file']; + }) + ->all(); + } + + protected function normalizeAssetValues($fields, $request) + { + return $fields->all() + ->filter(function ($field) { + return $field->fieldtype()->handle() === 'assets' && $field->get('max_files') === 1; + }) + ->map(function ($field) use ($request) { + return Arr::wrap($request->file($field->handle())); + }) + ->all(); + } + + protected function uploadAssetFiles($fields) + { + return $fields->all() + ->filter(function ($field) { + return $field->fieldtype()->handle() === 'assets' && request()->hasFile($field->handle()); + }) + ->map(function ($field) { + return AssetsUploader::field($field)->upload(request()->file($field->handle())); + }) + ->all(); + } } From 85c06a3f96e99d71d8db9150325b2ccbf8eb8854 Mon Sep 17 00:00:00 2001 From: Jack Sleight Date: Mon, 1 Aug 2022 10:18:26 +0100 Subject: [PATCH 04/23] WIP --- src/Auth/UserTags.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Auth/UserTags.php b/src/Auth/UserTags.php index 5185a8a28a2..a15f1ccd89b 100644 --- a/src/Auth/UserTags.php +++ b/src/Auth/UserTags.php @@ -492,7 +492,7 @@ public function notIn() } /** - * @inheritdoc + * {@inheritdoc} */ public function eventUrl($url, $relative = false) { From 4f60539cbdf46b8da12d842793b22e0296899ae5 Mon Sep 17 00:00:00 2001 From: Jack Sleight Date: Mon, 1 Aug 2022 15:15:51 +0100 Subject: [PATCH 05/23] WIP --- src/Http/Controllers/UserController.php | 43 ++++++++++++++++++------- 1 file changed, 31 insertions(+), 12 deletions(-) diff --git a/src/Http/Controllers/UserController.php b/src/Http/Controllers/UserController.php index 27eea07132c..d8d1898516e 100644 --- a/src/Http/Controllers/UserController.php +++ b/src/Http/Controllers/UserController.php @@ -12,7 +12,9 @@ use Statamic\Events\UserRegistering; use Statamic\Exceptions\SilentFormFailureException; use Statamic\Exceptions\UnauthorizedHttpException; +use Statamic\Forms\Uploaders\AssetsUploader; use Statamic\Facades\User; +use Statamic\Support\Arr; class UserController extends Controller { @@ -57,12 +59,14 @@ public function register(Request $request) { $blueprint = User::blueprint(); - $fields = $blueprint->fields()->addValues($request->all()); + $fields = $blueprint->fields(); + $values = array_merge($request->all(), $this->normalizeAssetValues($fields, $request)); + $fields = $fields->addValues($values); - $fieldRules = $fields->validator()->withRules([ + $fieldRules = $fields->validator()->withRules(array_merge([ 'email' => ['required', 'email', 'unique_user_value'], 'password' => ['required', 'confirmed', PasswordDefaults::rules()], - ])->rules(); + ], $this->assetRules($fields)))->rules(); $validator = Validator::make($request->all(), $fieldRules); @@ -70,8 +74,9 @@ public function register(Request $request) return $this->userRegistrationFailure($validator->errors()); } - // $values = array_merge($request->all(), $this->uploadAssetFiles($fields)); - $values = $fields->process()->values()->except(['email', 'groups', 'roles']); + $values = array_merge($request->all(), $this->uploadAssetFiles($fields)); + $fields = $fields->addValues($values); + $values = $fields->process()->values()->only(array_keys($values))->except(['email', 'groups', 'roles']); $user = User::make() ->email($request->email) @@ -109,13 +114,15 @@ public function profile(Request $request) $blueprint = User::blueprint(); - $fields = $blueprint->fields()->addValues($request->all()); + $fields = $blueprint->fields(); + $values = array_merge($request->all(), $this->normalizeAssetValues($fields, $request)); + $fields = $fields->addValues($values); - $fieldRules = $fields->validator()->withRules([ + $fieldRules = $fields->validator()->withRules(array_merge([ 'email' => ['required', 'email', 'unique_user_value:'.$user->id()], - ])->rules(); + ], $this->assetRules($fields)))->rules(); - $validator = Validator::make($request->all(), $fieldRules); + $validator = Validator::make($values, $fieldRules); if ($validator->fails()) { $errors = $validator->errors(); @@ -123,10 +130,13 @@ public function profile(Request $request) return back()->withInput()->withErrors($errors, 'user.profile'); } - // $values = array_merge($request->all(), $this->uploadAssetFiles($fields)); - $values = $fields->process()->values()->except(['email', 'password', 'groups', 'roles']); + $values = array_merge($request->all(), $this->uploadAssetFiles($fields)); + $fields = $fields->addValues($values); + $values = $fields->process()->values()->only(array_keys($values))->except(['email', 'password', 'groups', 'roles']); - $user->email($request->email); + if ($request->email) { + $user->email($request->email); + } foreach ($values as $key => $value) { $user->set($key, $value); } @@ -219,4 +229,13 @@ protected function uploadAssetFiles($fields) }) ->all(); } + + protected function filterEmptyAssetFields($fields) + { + return $fields->all() + ->filter(function ($field) { + return $field->fieldtype()->handle() !== 'assets' || request()->hasFile($field->handle()); + }) + ->all(); + } } From df4424c3abfbf9de32a0b9d0c2e3904ecc3e3b11 Mon Sep 17 00:00:00 2001 From: Jack Sleight Date: Mon, 1 Aug 2022 15:16:20 +0100 Subject: [PATCH 06/23] WIP --- src/Http/Controllers/UserController.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Http/Controllers/UserController.php b/src/Http/Controllers/UserController.php index d8d1898516e..97b13a6238f 100644 --- a/src/Http/Controllers/UserController.php +++ b/src/Http/Controllers/UserController.php @@ -12,8 +12,8 @@ use Statamic\Events\UserRegistering; use Statamic\Exceptions\SilentFormFailureException; use Statamic\Exceptions\UnauthorizedHttpException; -use Statamic\Forms\Uploaders\AssetsUploader; use Statamic\Facades\User; +use Statamic\Forms\Uploaders\AssetsUploader; use Statamic\Support\Arr; class UserController extends Controller From 126c88f42717cbbc87339dcb518d1b4285ef6672 Mon Sep 17 00:00:00 2001 From: Jack Sleight Date: Mon, 1 Aug 2022 15:30:50 +0100 Subject: [PATCH 07/23] WIP --- src/Http/Controllers/UserController.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Http/Controllers/UserController.php b/src/Http/Controllers/UserController.php index 97b13a6238f..cd560d6a413 100644 --- a/src/Http/Controllers/UserController.php +++ b/src/Http/Controllers/UserController.php @@ -68,7 +68,7 @@ public function register(Request $request) 'password' => ['required', 'confirmed', PasswordDefaults::rules()], ], $this->assetRules($fields)))->rules(); - $validator = Validator::make($request->all(), $fieldRules); + $validator = Validator::make($values, $fieldRules); if ($validator->fails()) { return $this->userRegistrationFailure($validator->errors()); @@ -148,7 +148,7 @@ public function profile(Request $request) return request()->has('_redirect') ? redirect(request()->get('_redirect')) : back(); } - public function changePassword(Request $request) + public function password(Request $request) { throw_unless($user = User::current(), new UnauthorizedHttpException(403)); From cb918573ad70bac3452e1139bf9012c17111c738 Mon Sep 17 00:00:00 2001 From: Jack Sleight Date: Mon, 1 Aug 2022 15:35:23 +0100 Subject: [PATCH 08/23] WIP --- src/Http/Controllers/UserController.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Http/Controllers/UserController.php b/src/Http/Controllers/UserController.php index cd560d6a413..b9bebf53a71 100644 --- a/src/Http/Controllers/UserController.php +++ b/src/Http/Controllers/UserController.php @@ -76,7 +76,7 @@ public function register(Request $request) $values = array_merge($request->all(), $this->uploadAssetFiles($fields)); $fields = $fields->addValues($values); - $values = $fields->process()->values()->only(array_keys($values))->except(['email', 'groups', 'roles']); + $values = $fields->process()->values()->except(['email', 'groups', 'roles']); $user = User::make() ->email($request->email) From 82020bd4a7ce0df3d6e2746015a9a4c743681419 Mon Sep 17 00:00:00 2001 From: Jack Sleight Date: Wed, 3 Aug 2022 13:53:58 +0100 Subject: [PATCH 09/23] WIP --- src/Http/Controllers/UserController.php | 10 +- tests/Tags/User/ProfileFormTest.php | 380 ++++++++++++++++++++++++ 2 files changed, 381 insertions(+), 9 deletions(-) create mode 100644 tests/Tags/User/ProfileFormTest.php diff --git a/src/Http/Controllers/UserController.php b/src/Http/Controllers/UserController.php index b9bebf53a71..6edddbc5d4a 100644 --- a/src/Http/Controllers/UserController.php +++ b/src/Http/Controllers/UserController.php @@ -132,6 +132,7 @@ public function profile(Request $request) $values = array_merge($request->all(), $this->uploadAssetFiles($fields)); $fields = $fields->addValues($values); + // only() added here to filter out fields that weren't actualy submitted $values = $fields->process()->values()->only(array_keys($values))->except(['email', 'password', 'groups', 'roles']); if ($request->email) { @@ -229,13 +230,4 @@ protected function uploadAssetFiles($fields) }) ->all(); } - - protected function filterEmptyAssetFields($fields) - { - return $fields->all() - ->filter(function ($field) { - return $field->fieldtype()->handle() !== 'assets' || request()->hasFile($field->handle()); - }) - ->all(); - } } diff --git a/tests/Tags/User/ProfileFormTest.php b/tests/Tags/User/ProfileFormTest.php new file mode 100644 index 00000000000..0580ac6553f --- /dev/null +++ b/tests/Tags/User/ProfileFormTest.php @@ -0,0 +1,380 @@ +tag('{{ user:profile_form }}{{ /user:profile_form }}'); + + $this->assertStringStartsWith('
', $output); + $this->assertStringContainsString('', $output); + $this->assertStringEndsWith('
', $output); + } + + /** @test */ + public function it_renders_form_with_params() + { + $output = $this->tag('{{ user:profile_form redirect="/submitted" error_redirect="/errors" class="form" id="form" }}{{ /user:profile_form }}'); + + $this->assertStringStartsWith('
', $output); + $this->assertStringContainsString('', $output); + $this->assertStringContainsString('', $output); + } + + /** @test */ + public function it_renders_form_with_redirects_to_anchor() + { + $output = $this->tag('{{ user:register_form redirect="#form" error_redirect="#form" }}{{ /user:register_form }}'); + + $this->assertStringContainsString('', $output); + $this->assertStringContainsString('', $output); + } + + /** @test */ + public function it_renders_form_with_fields_array() + { + $output = $this->normalizeHtml($this->tag(<<<'EOT' +{{ user:register_form }} + {{ fields }} + {{ field }} + {{ /fields }} +{{ /user:register_form }} +EOT +)); + + preg_match_all('/