From ad24f8ac28812e24128e3caa9dc0bfa786e9bc5d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?P=C5=91cze=20Bence?= Date: Thu, 25 Jun 2020 16:44:34 +0200 Subject: [PATCH 1/6] MAPG-156 rename profile to account --- public/static/js/{profile.js => account.js} | 10 +++++----- src/Controller/UserController.php | 6 +++--- views/{profile.php => account.php} | 8 ++++---- views/templates/header.php | 2 +- web.php | 6 +++--- 5 files changed, 16 insertions(+), 16 deletions(-) rename public/static/js/{profile.js => account.js} (81%) rename views/{profile.php => account.php} (86%) diff --git a/public/static/js/profile.js b/public/static/js/account.js similarity index 81% rename from public/static/js/profile.js rename to public/static/js/account.js index 0c2a2d3..2c32b27 100644 --- a/public/static/js/profile.js +++ b/public/static/js/account.js @@ -1,5 +1,5 @@ (function () { - var form = document.getElementById('profileForm'); + var form = document.getElementById('accountForm'); form.elements.password_new.onkeyup = function () { MapGuesser.toggleDisableOnChange(this, form.elements.save); @@ -33,14 +33,14 @@ break; } - var profileFormError = document.getElementById('profileFormError'); - profileFormError.style.display = 'block'; - profileFormError.innerHTML = errorText; + var accountFormError = document.getElementById('accountFormError'); + accountFormError.style.display = 'block'; + accountFormError.innerHTML = errorText; return; } - document.getElementById('profileFormError').style.display = 'none'; + document.getElementById('accountFormError').style.display = 'none'; form.reset(); form.elements.save.disabled = true; }, formData); diff --git a/src/Controller/UserController.php b/src/Controller/UserController.php index 14ed237..2546da0 100644 --- a/src/Controller/UserController.php +++ b/src/Controller/UserController.php @@ -27,7 +27,7 @@ class UserController implements ISecured return $user !== null; } - public function getProfile(): IContent + public function getAccount(): IContent { /** * @var User $user @@ -35,10 +35,10 @@ class UserController implements ISecured $user = $this->request->user(); $data = ['user' => $user->toArray()]; - return new HtmlContent('profile', $data); + return new HtmlContent('account', $data); } - public function saveProfile(): IContent + public function saveAccount(): IContent { /** * @var User $user diff --git a/views/profile.php b/views/account.php similarity index 86% rename from views/profile.php rename to views/account.php index 03810a5..62aef33 100644 --- a/views/profile.php +++ b/views/account.php @@ -1,20 +1,20 @@ -

Profile

+

Account

-
+
-

+

diff --git a/views/templates/header.php b/views/templates/header.php index 32518cf..c903207 100644 --- a/views/templates/header.php +++ b/views/templates/header.php @@ -7,7 +7,7 @@

user()) : ?> - + diff --git a/web.php b/web.php index 807d96b..688f7fb 100644 --- a/web.php +++ b/web.php @@ -32,9 +32,9 @@ Container::$routeCollection->group('signup', function (MapGuesser\Routing\RouteC $routeCollection->get('signup.cancel', 'cancel/{token}', [MapGuesser\Controller\LoginController::class, 'cancel']); }); Container::$routeCollection->get('logout', 'logout', [MapGuesser\Controller\LoginController::class, 'logout']); -Container::$routeCollection->group('profile', function (MapGuesser\Routing\RouteCollection $routeCollection) { - $routeCollection->get('profile', '', [MapGuesser\Controller\UserController::class, 'getProfile']); - $routeCollection->post('profile-action', '', [MapGuesser\Controller\UserController::class, 'saveProfile']); +Container::$routeCollection->group('account', function (MapGuesser\Routing\RouteCollection $routeCollection) { + $routeCollection->get('account', '', [MapGuesser\Controller\UserController::class, 'getAccount']); + $routeCollection->post('account-action', '', [MapGuesser\Controller\UserController::class, 'saveAccount']); }); //Container::$routeCollection->get('maps', 'maps', [MapGuesser\Controller\MapsController::class, 'getMaps']); Container::$routeCollection->group('game', function (MapGuesser\Routing\RouteCollection $routeCollection) { From 87c4c06aa6c70df88d59b75476996c830b9709dc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?P=C5=91cze=20Bence?= Date: Thu, 25 Jun 2020 19:14:46 +0200 Subject: [PATCH 2/6] MAPG-156 add some form helper functions --- public/static/js/mapguesser.js | 66 +++++++++++++++++++++++++++++++++- 1 file changed, 65 insertions(+), 1 deletion(-) diff --git a/public/static/js/mapguesser.js b/public/static/js/mapguesser.js index 0af777a..c0078e4 100644 --- a/public/static/js/mapguesser.js +++ b/public/static/js/mapguesser.js @@ -15,7 +15,7 @@ var MapGuesser = { document.head.appendChild(script); window.dataLayer = window.dataLayer || []; - function gtag(){dataLayer.push(arguments);} + function gtag() { dataLayer.push(arguments); } gtag('js', new Date()); gtag('config', GOOGLE_ANALITICS_ID); }, @@ -66,6 +66,46 @@ var MapGuesser = { } }, + setOnsubmitForForm: function (form, redirectOnSuccess) { + form.onsubmit = function (e) { + e.preventDefault(); + + document.getElementById('loading').style.visibility = 'visible'; + + var formData = new FormData(form); + var formError = form.getElementsByClassName('formError')[0]; + var pageLeaveOnSuccess = typeof redirectOnSuccess === 'string'; + + MapGuesser.httpRequest('POST', form.action, function () { + if (!pageLeaveOnSuccess) { + document.getElementById('loading').style.visibility = 'hidden'; + } + + if (this.response.error) { + if (pageLeaveOnSuccess) { + document.getElementById('loading').style.visibility = 'hidden'; + } + + formError.style.display = 'block'; + formError.innerHTML = this.response.error.errorText; + + return; + } + + if (!pageLeaveOnSuccess) { + formError.style.display = 'none'; + form.reset(); + } else { + if (redirectOnSuccess === '') { + window.location.reload(); + } else { + window.location.replace(redirectOnSuccess); + } + } + }, formData); + } + }, + showModal: function (id) { document.getElementById(id).style.visibility = 'visible'; document.getElementById('cover').style.visibility = 'visible'; @@ -142,6 +182,30 @@ var MapGuesser = { } else { button.disabled = true; } + }, + + toggleFormSubmitButtonDisableOnChange: function (form, observedInputs) { + for (var i = 0; i < observedInputs.length; i++) { + var input = form.elements[observedInputs[i]]; + + switch (input.tagName) { + case 'INPUT': + case 'TEXTAREA': + input.oninput = function () { + MapGuesser.toggleDisableOnChange(this, form.elements.submit); + }; + break; + case 'SELECT': + input.onchange = function () { + MapGuesser.toggleDisableOnChange(this, form.elements.submit); + }; + break; + } + } + + form.onreset = function () { + form.elements.submit.disabled = true; + } } }; From 910fdddf348d9483e1baac6ce0df4b354bbd5931 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?P=C5=91cze=20Bence?= Date: Thu, 25 Jun 2020 19:18:28 +0200 Subject: [PATCH 3/6] MAPG-156 adapt account's JS to the new helper functions --- public/static/js/account.js | 45 ++----------------------------------- views/account.php | 2 +- 2 files changed, 3 insertions(+), 44 deletions(-) diff --git a/public/static/js/account.js b/public/static/js/account.js index 2c32b27..8ee7b1f 100644 --- a/public/static/js/account.js +++ b/public/static/js/account.js @@ -1,48 +1,7 @@ (function () { var form = document.getElementById('accountForm'); - form.elements.password_new.onkeyup = function () { - MapGuesser.toggleDisableOnChange(this, form.elements.save); - }; + MapGuesser.toggleFormSubmitButtonDisableOnChange(form, ['password_new', 'password_new_confirm']) - form.elements.password_new_confirm.onkeyup = function () { - MapGuesser.toggleDisableOnChange(this, form.elements.save); - }; - - form.onsubmit = function (e) { - document.getElementById('loading').style.visibility = 'visible'; - - e.preventDefault(); - - var formData = new FormData(form); - - MapGuesser.httpRequest('POST', form.action, function () { - document.getElementById('loading').style.visibility = 'hidden'; - - if (this.response.error) { - var errorText; - switch (this.response.error) { - case 'password_not_match': - errorText = 'The given current password is wrong.' - break; - case 'password_too_short': - errorText = 'The given new password is too short. Please choose a password that is at least 6 characters long!' - break; - case 'passwords_not_match': - errorText = 'The given new passwords do not match.' - break; - } - - var accountFormError = document.getElementById('accountFormError'); - accountFormError.style.display = 'block'; - accountFormError.innerHTML = errorText; - - return; - } - - document.getElementById('accountFormError').style.display = 'none'; - form.reset(); - form.elements.save.disabled = true; - }, formData); - }; + MapGuesser.setOnsubmitForForm(form); })(); diff --git a/views/account.php b/views/account.php index 62aef33..e0a5424 100644 --- a/views/account.php +++ b/views/account.php @@ -16,7 +16,7 @@ $jsFiles = [

- +
From 70d8807f3872a744fc58b61d77f66c5a49710b34 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?P=C5=91cze=20Bence?= Date: Thu, 25 Jun 2020 20:24:37 +0200 Subject: [PATCH 4/6] MAPG-156 extend PDM to be able to return with Generator --- src/PersistentData/PersistentDataManager.php | 65 +++++++++++++------- 1 file changed, 42 insertions(+), 23 deletions(-) diff --git a/src/PersistentData/PersistentDataManager.php b/src/PersistentData/PersistentDataManager.php index 3965066..3f2c817 100644 --- a/src/PersistentData/PersistentDataManager.php +++ b/src/PersistentData/PersistentDataManager.php @@ -1,5 +1,6 @@ createSelect($select, $type, $withRelations); - $select->from($table); - - //TODO: only with some relations? - if ($withRelations) { - $relations = call_user_func([$type, 'getRelations']); - - $columns = []; - - foreach ($fields as $field) { - $columns[] = [$table, $field]; - } - - $columns = array_merge($columns, $this->getRelationColumns($relations)); - - $this->leftJoinRelations($select, $table, $relations); - $select->columns($columns); - } else { - $select->columns($fields); - } - - //TODO: return with array? $data = $select->execute()->fetch(IResultSet::FETCH_ASSOC); if ($data === null) { @@ -45,6 +24,18 @@ class PersistentDataManager return $model; } + public function selectMultipleFromDb(Select $select, string $type, bool $withRelations = false): Generator + { + $select = $this->createSelect($select, $type, $withRelations); + + while ($data = $select->execute()->fetch(IResultSet::FETCH_ASSOC)) { + $model = new $type(); + $this->fillWithData($data, $model); + + yield $model; + } + } + public function selectFromDbById($id, string $type, bool $withRelations = false): ?Model { $select = new Select(\Container::$dbConnection); @@ -136,6 +127,34 @@ class PersistentDataManager $model->resetSnapshot(); } + private function createSelect(Select $select, string $type, bool $withRelations = false): Select + { + $table = call_user_func([$type, 'getTable']); + $fields = call_user_func([$type, 'getFields']); + + $select->from($table); + + //TODO: only with some relations? + if ($withRelations) { + $relations = call_user_func([$type, 'getRelations']); + + $columns = []; + + foreach ($fields as $field) { + $columns[] = [$table, $field]; + } + + $columns = array_merge($columns, $this->getRelationColumns($relations)); + + $this->leftJoinRelations($select, $table, $relations); + $select->columns($columns); + } else { + $select->columns($fields); + } + + return $select; + } + private function getRelationColumns(array $relations): array { $columns = []; From 8987b563dd2f8bfa3cb63a5925bf1061adf62ad6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?P=C5=91cze=20Bence?= Date: Thu, 25 Jun 2020 20:25:34 +0200 Subject: [PATCH 5/6] MAPG-156 add getByUser to UserConfirmationRepository --- src/Repository/UserConfirmationRepository.php | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/Repository/UserConfirmationRepository.php b/src/Repository/UserConfirmationRepository.php index 62df3ae..5c0723e 100644 --- a/src/Repository/UserConfirmationRepository.php +++ b/src/Repository/UserConfirmationRepository.php @@ -1,6 +1,9 @@ pdm->selectFromDb($select, UserConfirmation::class); } + + public function getByUser(User $user): Generator + { + $select = new Select(\Container::$dbConnection); + $select->where('user_id', '=', $user->getId()); + + yield from $this->pdm->selectMultipleFromDb($select, UserConfirmation::class); + } } From b1ae7391e76d8c5bb0adca0e01c6e429adc5f8c6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?P=C5=91cze=20Bence?= Date: Thu, 25 Jun 2020 20:26:33 +0200 Subject: [PATCH 6/6] MAPG-156 implement user deletion --- public/static/js/{ => account}/account.js | 0 public/static/js/account/delete.js | 5 +++ public/static/js/{ => login}/login.js | 0 public/static/js/{ => login}/signup.js | 0 src/Controller/UserController.php | 52 +++++++++++++++++++++-- views/{ => account}/account.php | 8 +++- views/account/delete.php | 20 +++++++++ views/login/login.php | 2 +- views/login/signup.php | 2 +- web.php | 2 + 10 files changed, 83 insertions(+), 8 deletions(-) rename public/static/js/{ => account}/account.js (100%) create mode 100644 public/static/js/account/delete.js rename public/static/js/{ => login}/login.js (100%) rename public/static/js/{ => login}/signup.js (100%) rename views/{ => account}/account.php (81%) create mode 100644 views/account/delete.php diff --git a/public/static/js/account.js b/public/static/js/account/account.js similarity index 100% rename from public/static/js/account.js rename to public/static/js/account/account.js diff --git a/public/static/js/account/delete.js b/public/static/js/account/delete.js new file mode 100644 index 0000000..60e9aca --- /dev/null +++ b/public/static/js/account/delete.js @@ -0,0 +1,5 @@ +(function () { + var form = document.getElementById('deleteAccountForm'); + + MapGuesser.setOnsubmitForForm(form, '/'); +})(); diff --git a/public/static/js/login.js b/public/static/js/login/login.js similarity index 100% rename from public/static/js/login.js rename to public/static/js/login/login.js diff --git a/public/static/js/signup.js b/public/static/js/login/signup.js similarity index 100% rename from public/static/js/signup.js rename to public/static/js/login/signup.js diff --git a/src/Controller/UserController.php b/src/Controller/UserController.php index 2546da0..06d3d35 100644 --- a/src/Controller/UserController.php +++ b/src/Controller/UserController.php @@ -1,10 +1,14 @@ request = $request; $this->pdm = new PersistentDataManager(); + $this->userConfirmationRepository = new UserConfirmationRepository(); } public function authorize(): bool @@ -35,7 +42,18 @@ class UserController implements ISecured $user = $this->request->user(); $data = ['user' => $user->toArray()]; - return new HtmlContent('account', $data); + return new HtmlContent('account/account', $data); + } + + public function getDeleteAccount(): IContent + { + /** + * @var User $user + */ + $user = $this->request->user(); + + $data = ['user' => $user->toArray()]; + return new HtmlContent('account/delete', $data); } public function saveAccount(): IContent @@ -46,18 +64,18 @@ class UserController implements ISecured $user = $this->request->user(); if (!$user->checkPassword($this->request->post('password'))) { - $data = ['error' => 'password_not_match']; + $data = ['error' => ['errorText' => 'The given current password is wrong.']]; return new JsonContent($data); } if (strlen($this->request->post('password_new')) > 0) { if (strlen($this->request->post('password_new')) < 6) { - $data = ['error' => 'password_too_short']; + $data = ['error' => ['errorText' => 'The given new password is too short. Please choose a password that is at least 6 characters long!']]; return new JsonContent($data); } if ($this->request->post('password_new') !== $this->request->post('password_new_confirm')) { - $data = ['error' => 'passwords_not_match']; + $data = ['error' => ['errorText' => 'The given new passwords do not match.']]; return new JsonContent($data); } @@ -69,4 +87,30 @@ class UserController implements ISecured $data = ['success' => true]; return new JsonContent($data); } + + public function deleteAccount(): IContent + { + /** + * @var User $user + */ + $user = $this->request->user(); + + if (!$user->checkPassword($this->request->post('password'))) { + $data = ['error' => ['errorText' => 'The given current password is wrong.']]; + return new JsonContent($data); + } + + \Container::$dbConnection->startTransaction(); + + foreach ($this->userConfirmationRepository->getByUser($user) as $userConfirmation) { + $this->pdm->deleteFromDb($userConfirmation); + } + + $this->pdm->deleteFromDb($user); + + \Container::$dbConnection->commit(); + + $data = ['success' => true]; + return new JsonContent($data); + } } diff --git a/views/account.php b/views/account/account.php similarity index 81% rename from views/account.php rename to views/account/account.php index e0a5424..67469cc 100644 --- a/views/account.php +++ b/views/account/account.php @@ -1,6 +1,6 @@ @@ -8,7 +8,7 @@ $jsFiles = [

Account

- +
@@ -18,6 +18,10 @@ $jsFiles = [
+
+
diff --git a/views/account/delete.php b/views/account/delete.php new file mode 100644 index 0000000..461218d --- /dev/null +++ b/views/account/delete.php @@ -0,0 +1,20 @@ + + + +

Delete account

+
+
+

Are you sure you want to delete your account? This cannot be undone!

+ +

+
+ +
+
+
+ + \ No newline at end of file diff --git a/views/login/login.php b/views/login/login.php index fe6f48b..709f11e 100644 --- a/views/login/login.php +++ b/views/login/login.php @@ -1,6 +1,6 @@ diff --git a/views/login/signup.php b/views/login/signup.php index b61f80d..1902295 100644 --- a/views/login/signup.php +++ b/views/login/signup.php @@ -1,6 +1,6 @@ diff --git a/web.php b/web.php index 688f7fb..5eaffae 100644 --- a/web.php +++ b/web.php @@ -35,6 +35,8 @@ Container::$routeCollection->get('logout', 'logout', [MapGuesser\Controller\Logi Container::$routeCollection->group('account', function (MapGuesser\Routing\RouteCollection $routeCollection) { $routeCollection->get('account', '', [MapGuesser\Controller\UserController::class, 'getAccount']); $routeCollection->post('account-action', '', [MapGuesser\Controller\UserController::class, 'saveAccount']); + $routeCollection->get('account.delete', 'delete', [MapGuesser\Controller\UserController::class, 'getDeleteAccount']); + $routeCollection->post('account.delete-action', 'delete', [MapGuesser\Controller\UserController::class, 'deleteAccount']); }); //Container::$routeCollection->get('maps', 'maps', [MapGuesser\Controller\MapsController::class, 'getMaps']); Container::$routeCollection->group('game', function (MapGuesser\Routing\RouteCollection $routeCollection) {