diff --git a/public_html/dispatch.php b/public_html/dispatch.php index b961f615..da953feb 100644 --- a/public_html/dispatch.php +++ b/public_html/dispatch.php @@ -151,10 +151,10 @@ $userValidation = \Chibi\Router::register(['UserController', 'activationView'], 'GET', '/activation', $userValidation); \Chibi\Router::register(['UserController', 'activationAction'], 'POST', '/activation', $userValidation); -\Chibi\Router::register(['UserController', 'activationAction'], 'GET', '/activation/{token}', $userValidation); +\Chibi\Router::register(['UserController', 'activationAction'], 'GET', '/activation/{tokenText}', $userValidation); \Chibi\Router::register(['UserController', 'passwordResetView'], 'GET', '/password-reset', $userValidation); \Chibi\Router::register(['UserController', 'passwordResetAction'], 'POST', '/password-reset', $userValidation); -\Chibi\Router::register(['UserController', 'passwordResetAction'], 'GET', '/password-reset/{token}', $userValidation); +\Chibi\Router::register(['UserController', 'passwordResetAction'], 'GET', '/password-reset/{tokenText}', $userValidation); \Chibi\Router::register(['UserController', 'flagAction'], 'POST', '/user/{name}/flag', $userValidation); \Chibi\Router::register(['UserController', 'banAction'], 'POST', '/user/{name}/ban', $userValidation); diff --git a/src/Api/Jobs/ActivateUserEmailJob.php b/src/Api/Jobs/ActivateUserEmailJob.php new file mode 100644 index 00000000..c47feec0 --- /dev/null +++ b/src/Api/Jobs/ActivateUserEmailJob.php @@ -0,0 +1,65 @@ +hasArgument(self::TOKEN)) + { + $user = UserModel::findByNameOrEmail($this->getArgument(self::USER_NAME)); + + if (empty($user->emailUnconfirmed)) + { + if (!empty($user->emailConfirmed)) + throw new SimpleException('E-mail was already confirmed; activation skipped'); + else + throw new SimpleException('This user has no e-mail specified; activation cannot proceed'); + } + + self::sendEmail($user); + + return $user; + } + else + { + $tokenText = $this->getArgument(self::TOKEN); + $token = TokenModel::findByToken($tokenText); + TokenModel::checkValidity($token); + + $user = $token->getUser(); + $user->confirmEmail(); + $token->used = true; + TokenModel::save($token); + UserModel::save($user); + + LogHelper::log('{subject} just activated account', [ + 'subject' => TextHelper::reprUser($user)]); + + return $user; + } + } + + public static function sendEmail($user) + { + $regConfig = getConfig()->registration; + + if (!$regConfig->confirmationEmailEnabled) + { + $user->confirmEmail(); + return; + } + + $mail = new Mail(); + $mail->body = $regConfig->confirmationEmailBody; + $mail->subject = $regConfig->confirmationEmailSubject; + $mail->senderName = $regConfig->confirmationEmailSenderName; + $mail->senderEmail = $regConfig->confirmationEmailSenderEmail; + $mail->recipientEmail = $user->emailUnconfirmed; + + return Mailer::sendMailWithTokenLink( + $user, + ['UserController', 'activationAction'], + $mail); + } +} diff --git a/src/Api/Jobs/AddUserJob.php b/src/Api/Jobs/AddUserJob.php index 6a426207..537f0c25 100644 --- a/src/Api/Jobs/AddUserJob.php +++ b/src/Api/Jobs/AddUserJob.php @@ -24,11 +24,8 @@ class AddUserJob extends AbstractJob Api::enablePrivilegeChecking(); LogHelper::setBuffer([]); - if ($firstUser and empty($user->emailConfirmed)) - { - $user->emailConfirmed = $user->emailUnconfirmed; - $user->emailUnconfirmed = null; - } + if ($firstUser) + $user->confirmEmail(); //load the user after edits $user = UserModel::findById($user->id); diff --git a/src/Api/Jobs/EditUserEmailJob.php b/src/Api/Jobs/EditUserEmailJob.php index 375e138e..af3f82a7 100644 --- a/src/Api/Jobs/EditUserEmailJob.php +++ b/src/Api/Jobs/EditUserEmailJob.php @@ -16,20 +16,17 @@ class EditUserEmailJob extends AbstractUserJob if ($oldEmail == $newEmail) return $user; + $user->emailUnconfirmed = $newEmail; + $user->emailConfirmed = null; + if (Auth::getCurrentUser()->id == $user->id) { - $user->emailUnconfirmed = $newEmail; - $user->emailConfirmed = null; - if (!empty($newEmail)) - { - $this->sendEmail($user); - } + ActivateUserEmailJob::sendEmail($user); } else { - $user->emailUnconfirmed = null; - $user->emailConfirmed = $newEmail; + $user->confirmEmail(); } UserModel::save($user); @@ -50,29 +47,4 @@ class EditUserEmailJob extends AbstractUserJob Access::getIdentity($this->user), ]; } - - //todo: change to private once finished refactors to UserController - public function sendEmail($user) - { - $regConfig = getConfig()->registration; - - if (!$regConfig->confirmationEmailEnabled) - { - $user->emailUnconfirmed = null; - $user->emailConfirmed = $user->emailUnconfirmed; - return; - } - - $mail = new Mail(); - $mail->body = $regConfig->confirmationEmailBody; - $mail->subject = $regConfig->confirmationEmailSubject; - $mail->senderName = $regConfig->confirmationEmailSenderName; - $mail->senderEmail = $regConfig->confirmationEmailSenderEmail; - $mail->recipientEmail = $user->emailUnconfirmed; - - return Mailer::sendMailWithTokenLink( - $user, - ['UserController', 'activationAction'], - $mail); - } } diff --git a/src/Api/Jobs/PasswordResetJob.php b/src/Api/Jobs/PasswordResetJob.php new file mode 100644 index 00000000..266818fc --- /dev/null +++ b/src/Api/Jobs/PasswordResetJob.php @@ -0,0 +1,64 @@ +hasArgument(self::TOKEN)) + { + $user = UserModel::findByNameOrEmail($this->getArgument(self::USER_NAME)); + + if (empty($user->emailConfirmed)) + throw new SimpleException('This user has no e-mail confirmed; password reset cannot proceed'); + + self::sendEmail($user); + + return $user; + } + else + { + $tokenText = $this->getArgument(self::TOKEN); + $token = TokenModel::findByToken($tokenText); + TokenModel::checkValidity($token); + + $alphabet = array_merge(range('A', 'Z'), range('a', 'z'), range('0', '9')); + $newPassword = join('', array_map(function($x) use ($alphabet) + { + return $alphabet[$x]; + }, array_rand($alphabet, 8))); + + $user = $token->getUser(); + $user->passHash = UserModel::hashPassword($newPassword, $user->passSalt); + $token->used = true; + TokenModel::save($token); + UserModel::save($user); + + LogHelper::log('{subject} just reset password', [ + 'subject' => TextHelper::reprUser($user)]); + + $x = new StdClass; + $x->user = $user; + $x->newPassword = $newPassword; + return $x; + } + } + + public static function sendEmail($user) + { + $regConfig = getConfig()->registration; + + $mail = new Mail(); + $mail->body = $regConfig->passwordResetEmailBody; + $mail->subject = $regConfig->passwordResetEmailSubject; + $mail->senderName = $regConfig->passwordResetEmailSenderName; + $mail->senderEmail = $regConfig->passwordResetEmailSenderEmail; + $mail->recipientEmail = $user->emailConfirmed; + + return Mailer::sendMailWithTokenLink( + $user, + ['UserController', 'passwordResetAction'], + $mail); + } +} diff --git a/src/Controllers/UserController.php b/src/Controllers/UserController.php index b032e439..b8c9a664 100644 --- a/src/Controllers/UserController.php +++ b/src/Controllers/UserController.php @@ -223,51 +223,30 @@ class UserController Assets::setSubTitle('account activation'); } - public function activationAction($token) + public function activationAction($tokenText) { $context = getContext(); $context->viewName = 'message'; Assets::setSubTitle('account activation'); + $name = InputHelper::get('name'); - if (empty($token)) + if (empty($tokenText)) { - $name = InputHelper::get('name'); - $user = UserModel::findByNameOrEmail($name); - if (empty($user->emailUnconfirmed)) - { - if (!empty($user->emailConfirmed)) - throw new SimpleException('E-mail was already confirmed; activation skipped'); - else - throw new SimpleException('This user has no e-mail specified; activation cannot proceed'); - } - EditUserEmailJob::sendEmail($user); + Api::run(new ActivateUserEmailJob(), [ ActivateUserEmailJob::USER_NAME => $name ]); + Messenger::message('Activation e-mail resent.'); } else { - $dbToken = TokenModel::findByToken($token); - TokenModel::checkValidity($dbToken); + $user = Api::run(new ActivateUserEmailJob(), [ ActivateUserEmailJob::TOKEN => $tokenText ]); - $dbUser = $dbToken->getUser(); - if (empty($dbUser->emailConfirmed)) - { - $dbUser->emailConfirmed = $dbUser->emailUnconfirmed; - $dbUser->emailUnconfirmed = null; - } - $dbToken->used = true; - TokenModel::save($dbToken); - UserModel::save($dbUser); - - LogHelper::log('{subject} just activated account', ['subject' => TextHelper::reprUser($dbUser)]); $message = 'Activation completed successfully.'; if (getConfig()->registration->staffActivation) $message .= ' However, your account still must be confirmed by staff.'; Messenger::message($message); if (!getConfig()->registration->staffActivation) - { - Auth::setCurrentUser($dbUser); - } + Auth::setCurrentUser($user); } } @@ -278,64 +257,31 @@ class UserController Assets::setSubTitle('password reset'); } - public function passwordResetAction($token) + public function passwordResetAction($tokenText) { $context = getContext(); $context->viewName = 'message'; Assets::setSubTitle('password reset'); + $name = InputHelper::get('name'); - if (empty($token)) + if (empty($tokenText)) { - $name = InputHelper::get('name'); - $user = UserModel::findByNameOrEmail($name); - if (empty($user->emailConfirmed)) - throw new SimpleException('This user has no e-mail confirmed; password reset cannot proceed'); + Api::run(new PasswordResetJob(), [ PasswordResetJob::USER_NAME => $name ]); - self::sendPasswordResetConfirmation($user); Messenger::message('E-mail sent. Follow instructions to reset password.'); } else { - $dbToken = TokenModel::findByToken($token); - TokenModel::checkValidity($dbToken); + $ret = Api::run(new PasswordResetJob(), [ PasswordResetJob::TOKEN => $tokenText ]); - $alphabet = array_merge(range('A', 'Z'), range('a', 'z'), range('0', '9')); - $randomPassword = join('', array_map(function($x) use ($alphabet) - { - return $alphabet[$x]; - }, array_rand($alphabet, 8))); + Messenger::message(sprintf( + 'Password reset successful. Your new password is **%s**.', + $ret->newPassword)); - $dbUser = $dbToken->getUser(); - $dbUser->passHash = UserModel::hashPassword($randomPassword, $dbUser->passSalt); - $dbToken->used = true; - TokenModel::save($dbToken); - UserModel::save($dbUser); - - LogHelper::log('{subject} just reset password', ['subject' => TextHelper::reprUser($dbUser)]); - $message = 'Password reset successful. Your new password is **' . $randomPassword . '**.'; - Messenger::message($message); - - Auth::setCurrentUser($dbUser); + Auth::setCurrentUser($ret->user); } } - private static function sendPasswordResetConfirmation($user) - { - $regConfig = getConfig()->registration; - - $mail = new Mail(); - $mail->body = $regConfig->passwordResetEmailBody; - $mail->subject = $regConfig->passwordResetEmailSubject; - $mail->senderName = $regConfig->passwordResetEmailSenderName; - $mail->senderEmail = $regConfig->passwordResetEmailSenderEmail; - $mail->recipientEmail = $user->emailConfirmed; - - return Mailer::sendMailWithTokenLink( - $user, - ['UserController', 'passwordResetAction'], - $mail); - } - private function requirePasswordConfirmation() { $user = getContext()->transport->user; diff --git a/src/Models/Entities/UserEntity.php b/src/Models/Entities/UserEntity.php index 07a4d7c3..083a8a11 100644 --- a/src/Models/Entities/UserEntity.php +++ b/src/Models/Entities/UserEntity.php @@ -112,6 +112,15 @@ class UserEntity extends AbstractEntity $this->setSetting(UserModel::SETTING_ENDLESS_SCROLLING, $enabled ? 1 : 0); } + public function confirmEmail() + { + if (!empty($this->emailConfirmed)) + return; + + $this->emailConfirmed = $user->emailUnconfirmed; + $this->emailUnconfirmed = null; + } + public function hasFavorited($post) { $stmt = new Sql\SelectStatement();