diff --git a/public/static/js/account/account.js b/public/static/js/account/account.js new file mode 100644 index 0000000..8ee7b1f --- /dev/null +++ b/public/static/js/account/account.js @@ -0,0 +1,7 @@ +(function () { + var form = document.getElementById('accountForm'); + + MapGuesser.toggleFormSubmitButtonDisableOnChange(form, ['password_new', 'password_new_confirm']) + + MapGuesser.setOnsubmitForForm(form); +})(); 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/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; + } } }; diff --git a/public/static/js/profile.js b/public/static/js/profile.js deleted file mode 100644 index 0c2a2d3..0000000 --- a/public/static/js/profile.js +++ /dev/null @@ -1,48 +0,0 @@ -(function () { - var form = document.getElementById('profileForm'); - - form.elements.password_new.onkeyup = function () { - MapGuesser.toggleDisableOnChange(this, form.elements.save); - }; - - 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 profileFormError = document.getElementById('profileFormError'); - profileFormError.style.display = 'block'; - profileFormError.innerHTML = errorText; - - return; - } - - document.getElementById('profileFormError').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..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 @@ -27,7 +34,7 @@ class UserController implements ISecured return $user !== null; } - public function getProfile(): IContent + public function getAccount(): IContent { /** * @var User $user @@ -35,10 +42,21 @@ class UserController implements ISecured $user = $this->request->user(); $data = ['user' => $user->toArray()]; - return new HtmlContent('profile', $data); + return new HtmlContent('account/account', $data); } - public function saveProfile(): IContent + 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 { /** * @var User $user @@ -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/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 = []; 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); + } } diff --git a/views/profile.php b/views/account/account.php similarity index 66% rename from views/profile.php rename to views/account/account.php index 03810a5..67469cc 100644 --- a/views/profile.php +++ b/views/account/account.php @@ -1,22 +1,26 @@ -
user()) : ?> - +