Account activation
+Activation failed. Please check the link you entered or retry sign up!
+diff --git a/mail/signup.tpl b/mail/signup.tpl new file mode 100644 index 0000000..c93b404 --- /dev/null +++ b/mail/signup.tpl @@ -0,0 +1,13 @@ +Hi, + +You recently signed up on MapGuesser with this email address ({{EMAIL}}). To activate your account, please click on the following link: +{{ACTIVATE_LINK}} + +If you did not sign up on MapGuesser or changed your mind, no further action is required, your email address will be deleted soon. +However if you want to immediately delete it, please click on the following link: +{{CANCEL_LINK}} + +Have fun on MapGuesser! + +Regards, +MapGuesser diff --git a/public/static/js/signup.js b/public/static/js/signup.js new file mode 100644 index 0000000..4031b32 --- /dev/null +++ b/public/static/js/signup.js @@ -0,0 +1,47 @@ +(function () { + var form = document.getElementById('signupForm'); + + 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 'passwords_too_short': + errorText = 'The given password is too short. Please choose a password that is at least 6 characters long!' + break; + case 'passwords_not_match': + errorText = 'The given passwords do not match.' + break; + case 'user_found': + errorText = 'There is a user already registered with the given email address. Please login here!'; + break; + case 'not_active_user_found': + errorText = 'There is a user already registered with the given email address. Please check your email and click on the activation link!'; + break; + } + + var signupFormError = document.getElementById('signupFormError'); + signupFormError.style.display = 'block'; + signupFormError.innerHTML = errorText; + + form.elements.email.select(); + + return; + } + + document.getElementById('signupFormError').style.display = 'none'; + form.reset(); + form.elements.email.focus(); + + MapGuesser.showModalWithContent('Sign up successful', 'Sign up was successful. Please check your email and click on the activation link to activate your account!'); + }, formData); + }; +})(); diff --git a/src/Controller/SignupController.php b/src/Controller/SignupController.php new file mode 100644 index 0000000..ea049bc --- /dev/null +++ b/src/Controller/SignupController.php @@ -0,0 +1,192 @@ +request = $request; + } + + public function getSignupForm() + { + $session = $this->request->session(); + + if ($session->get('user')) { + return new Redirect([\Container::$routeCollection->getRoute('index'), []], IRedirect::TEMPORARY); + } + + $data = []; + return new HtmlContent('signup/signup', $data); + } + + public function signup(): IContent + { + $session = $this->request->session(); + + if ($session->get('user')) { + //TODO: return with some error + $data = ['success' => true]; + return new JsonContent($data); + } + + $select = new Select(\Container::$dbConnection, 'users'); + $select->columns(User::getFields()); + $select->where('email', '=', $this->request->post('email')); + + $userData = $select->execute()->fetch(IResultSet::FETCH_ASSOC); + + if ($userData !== null) { + $user = new User($userData); + + if ($user->getActive()) { + $data = ['error' => 'user_found']; + } else { + $data = ['error' => 'not_active_user_found']; + } + return new JsonContent($data); + } + + if (strlen($this->request->post('password')) < 6) { + $data = ['error' => 'passwords_too_short']; + return new JsonContent($data); + } + + if ($this->request->post('password') !== $this->request->post('password_confirm')) { + $data = ['error' => 'passwords_not_match']; + return new JsonContent($data); + } + + $user = new User([ + 'email' => $this->request->post('email'), + ]); + + $user->setPlainPassword($this->request->post('password')); + + \Container::$dbConnection->startTransaction(); + + $modify = new Modify(\Container::$dbConnection, 'users'); + $modify->fill($user->toArray()); + $modify->save(); + $userId = $modify->getId(); + + $token = hash('sha256', serialize($user) . random_bytes(10) . microtime()); + + $modify = new Modify(\Container::$dbConnection, 'user_confirmations'); + $modify->set('user_id', $userId); + $modify->set('token', $token); + $modify->save(); + + \Container::$dbConnection->commit(); + + $this->sendConfirmationEmail($user->getEmail(), $token); + + $data = ['success' => true]; + return new JsonContent($data); + } + + public function activate() + { + $session = $this->request->session(); + + if ($session->get('user')) { + return new Redirect([\Container::$routeCollection->getRoute('index'), []], IRedirect::TEMPORARY); + } + + $select = new Select(\Container::$dbConnection, 'user_confirmations'); + $select->columns(['id', 'user_id']); + $select->where('token', '=', $this->request->query('token')); + + $confirmation = $select->execute()->fetch(IResultSet::FETCH_ASSOC); + + if ($confirmation === null) { + $data = []; + return new HtmlContent('signup/activate', $data); + } + + \Container::$dbConnection->startTransaction(); + + $modify = new Modify(\Container::$dbConnection, 'user_confirmations'); + $modify->setId($confirmation['id']); + $modify->delete(); + + $modify = new Modify(\Container::$dbConnection, 'users'); + $modify->setId($confirmation['user_id']); + $modify->set('active', true); + $modify->save(); + + \Container::$dbConnection->commit(); + + $select = new Select(\Container::$dbConnection, 'users'); + $select->columns(User::getFields()); + $select->whereId($confirmation['user_id']); + + $userData = $select->execute()->fetch(IResultSet::FETCH_ASSOC); + $user = new User($userData); + + $session->set('user', $user); + + return new Redirect([\Container::$routeCollection->getRoute('index'), []], IRedirect::TEMPORARY); + } + + public function cancel() + { + $session = $this->request->session(); + + if ($session->get('user')) { + return new Redirect([\Container::$routeCollection->getRoute('index'), []], IRedirect::TEMPORARY); + } + + $select = new Select(\Container::$dbConnection, 'user_confirmations'); + $select->columns(['id', 'user_id']); + $select->where('token', '=', $this->request->query('token')); + + $confirmation = $select->execute()->fetch(IResultSet::FETCH_ASSOC); + + if ($confirmation === null) { + $data = ['success' => false]; + return new HtmlContent('signup/cancel', $data); + } + + \Container::$dbConnection->startTransaction(); + + $modify = new Modify(\Container::$dbConnection, 'user_confirmations'); + $modify->setId($confirmation['id']); + $modify->delete(); + + $modify = new Modify(\Container::$dbConnection, 'users'); + $modify->setId($confirmation['user_id']); + $modify->delete(); + + \Container::$dbConnection->commit(); + + $data = ['success' => true]; + return new HtmlContent('signup/cancel', $data); + } + + private function sendConfirmationEmail($email, $token): void + { + $mail = new Mail(); + $mail->addRecipient($email); + $mail->setSubject('Welcome to MapGuesser - Activate your account'); + $mail->setBodyFromTemplate('signup', [ + 'EMAIL' => $email, + 'ACTIVATE_LINK' => $this->request->getBase() . '/signup/activate/' . $token, + 'CANCEL_LINK' => $this->request->getBase() . '/signup/cancel/' . $token, + ]); + $mail->send(); + } +} diff --git a/views/signup/activate.php b/views/signup/activate.php new file mode 100644 index 0000000..6ad9b78 --- /dev/null +++ b/views/signup/activate.php @@ -0,0 +1,9 @@ + + +
Activation failed. Please check the link you entered or retry sign up!
+Cancellation was successfull. You can sign up any time if you want!
+ +Cancellation failed. Please check the link you entered! Maybe the account was already deleted, in this case no further action is required.
+ +