From 39db2a64e15a0c4a23cc52decf7f3105a6125f34 Mon Sep 17 00:00:00 2001 From: Marcin Kurczewski Date: Wed, 16 Oct 2013 18:07:23 +0200 Subject: [PATCH] Closed #32 --- config.ini | 6 +- public_html/media/css/user-list.css | 6 +- public_html/media/js/paginator-endless.js | 2 +- src/Controllers/AuthController.php | 144 +---------------- src/Controllers/PostController.php | 2 + src/Controllers/UserController.php | 150 +++++++++++++++++- src/Helpers/PrivilegesHelper.php | 6 + src/Models/Model_User.php | 6 +- src/Views/auth-activation.phtml | 6 - src/Views/auth-login.phtml | 2 +- src/Views/layout-normal.phtml | 2 +- src/Views/user-activation.phtml | 6 + ...register.phtml => user-registration.phtml} | 18 +-- src/Views/user-view.phtml | 4 +- 14 files changed, 188 insertions(+), 172 deletions(-) delete mode 100644 src/Views/auth-activation.phtml create mode 100644 src/Views/user-activation.phtml rename src/Views/{auth-register.phtml => user-registration.phtml} (75%) diff --git a/config.ini b/config.ini index 1c5ba094..2ce57da2 100644 --- a/config.ini +++ b/config.ini @@ -19,14 +19,16 @@ endlessScrolling=1 maxSearchTokens=4 [registration] -emailActivation = 0 -staffActivation = 0 +staffActivation = 1 passMinLength = 5 passRegex = "/^.+$/" userNameMinLength = 3 userNameRegex = "/^[\w_-]+$/ui" salt = "1A2/$_4xVa" +needEmailForRegistering = 1 +needEmailForCommenting = 0 +needEmailForUploading = 1 activationEmailSenderName = "{host} registration engine" activationEmailSenderEmail = "noreply@{host}" activationEmailSubject = "{host} activation" diff --git a/public_html/media/css/user-list.css b/public_html/media/css/user-list.css index 832587aa..25158075 100644 --- a/public_html/media/css/user-list.css +++ b/public_html/media/css/user-list.css @@ -1,8 +1,10 @@ -img { +.user img { + width: 100px; + height: 100px; float: left; margin-right: 0.5em; } -h1 { +.user h1 { margin-top: 0; margin-bottom: 0.25em; } diff --git a/public_html/media/js/paginator-endless.js b/public_html/media/js/paginator-endless.js index 8f698e8d..aaef4317 100644 --- a/public_html/media/js/paginator-endless.js +++ b/public_html/media/js/paginator-endless.js @@ -17,7 +17,7 @@ function scrolled() var dom = $(response); var nextPage = dom.find('.paginator .next:not(.disabled) a').attr('href'); $(document).data('page-next', nextPage); - $('.paginator-content').append($(response).find('.paginator-content').children().fadeIn('slow')); + $('.paginator-content').append($(response).find('.paginator-content').children().css({opacity: 0}).animate({opacity: 1}, 'slow')); scrolled(); }); } diff --git a/src/Controllers/AuthController.php b/src/Controllers/AuthController.php index c2199e6d..fa4fa275 100644 --- a/src/Controllers/AuthController.php +++ b/src/Controllers/AuthController.php @@ -35,8 +35,8 @@ class AuthController if ($dbUser->banned) throw new SimpleException('You are banned'); - if (!$dbUser->email_confirmed and $this->config->registration->emailActivation) - throw new SimpleException('You haven\'t confirmed your e-mail address yet'); + if ($this->config->registration->needEmailForRegistering) + PrivilegesHelper::confirmEmail($dbUser); $_SESSION['user-id'] = $dbUser->id; \Chibi\UrlHelper::forward(\Chibi\UrlHelper::route('index', 'index')); @@ -54,144 +54,4 @@ class AuthController unset($_SESSION['user-id']); \Chibi\UrlHelper::forward(\Chibi\UrlHelper::route('index', 'index')); } - - /** - * @route /register - */ - public function registerAction() - { - $this->context->handleExceptions = true; - $this->context->stylesheets []= 'auth.css'; - $this->context->subTitle = 'registration form'; - - //check if already logged in - if ($this->context->loggedIn) - { - \Chibi\UrlHelper::forward(\Chibi\UrlHelper::route('index', 'index')); - return; - } - - $suppliedName = InputHelper::get('name'); - $suppliedPassword1 = InputHelper::get('password1'); - $suppliedPassword2 = InputHelper::get('password2'); - $suppliedEmail = InputHelper::get('email'); - $this->context->suppliedName = $suppliedName; - $this->context->suppliedPassword1 = $suppliedPassword1; - $this->context->suppliedPassword2 = $suppliedPassword2; - $this->context->suppliedEmail = $suppliedEmail; - - $regConfig = $this->config->registration; - $emailActivation = $regConfig->emailActivation; - $staffActivation = $regConfig->staffActivation; - - $this->context->transport->staffActivation = $staffActivation; - $this->context->transport->emailActivation = $emailActivation; - - if ($suppliedName !== null) - { - $suppliedName = Model_User::validateUserName($suppliedName); - - if ($suppliedPassword1 != $suppliedPassword2) - throw new SimpleException('Specified passwords must be the same'); - $suppliedPassword = Model_User::validatePassword($suppliedPassword1); - - $suppliedEmail = Model_User::validateEmail($suppliedEmail); - if (empty($suppliedEmail) and $emailActivation) - throw new SimpleException('E-mail address is required - you will be sent confirmation e-mail.'); - - //register the user - $dbUser = R::dispense('user'); - $dbUser->name = $suppliedName; - $dbUser->pass_salt = md5(mt_rand() . uniqid()); - $dbUser->pass_hash = Model_User::hashPassword($suppliedPassword, $dbUser->pass_salt); - $dbUser->email = $suppliedEmail; - $dbUser->join_date = time(); - if (R::findOne('user') === null) - { - $dbUser->staff_confirmed = true; - $dbUser->email_confirmed = true; - $dbUser->access_rank = AccessRank::Admin; - } - else - { - $dbUser->staff_confirmed = false; - $dbUser->email_confirmed = false; - $dbUser->access_rank = AccessRank::Registered; - } - - //prepare unique registration token - do - { - $emailToken = md5(mt_rand() . uniqid()); - } - while (R::findOne('user', 'email_token = ?', [$emailToken]) !== null); - $dbUser->email_token = $emailToken; - - //send the e-mail - if ($emailActivation) - { - $tokens = []; - $tokens['host'] = $_SERVER['HTTP_HOST']; - $tokens['link'] = \Chibi\UrlHelper::route('auth', 'activation', ['token' => $dbUser->email_token]); - - $body = wordwrap(TextHelper::replaceTokens($regConfig->activationEmailBody, $tokens), 70); - $subject = TextHelper::replaceTokens($regConfig->activationEmailSubject, $tokens); - $senderName = TextHelper::replaceTokens($regConfig->activationEmailSenderName, $tokens); - $senderEmail = $regConfig->activationEmailSenderEmail; - - $headers = []; - $headers[] = sprintf('From: %s <%s>', $senderName, $senderEmail); - $headers[] = sprintf('Subject: %s', $subject); - $headers[] = sprintf('X-Mailer: PHP/%s', phpversion()); - mail($dbUser->email, $subject, $body, implode("\r\n", $headers)); - } - - //save the user to db if everything went okay - R::store($dbUser); - $this->context->transport->success = true; - - if (!$emailActivation and !$staffActivation) - { - $_SESSION['user-id'] = $dbUser->id; - \Chibi\Registry::getBootstrap()->attachUser(); - } - } - } - - /** - * @route /activation/{token} - */ - public function activationAction($token) - { - $this->context->subTitle = 'account activation'; - - //check if already logged in - if ($this->context->loggedIn) - { - \Chibi\UrlHelper::forward(\Chibi\UrlHelper::route('index', 'index')); - return; - } - - if (empty($token)) - throw new SimpleException('Invalid activation token'); - - $dbUser = R::findOne('user', 'email_token = ?', [$token]); - if ($dbUser === null) - throw new SimpleException('No user with such activation token'); - - if ($dbUser->email_confirmed) - throw new SimpleException('This user was already activated'); - - $dbUser->email_confirmed = true; - R::store($dbUser); - $this->context->transport->success = true; - - $staffActivation = $this->config->registration->staffActivation; - $this->context->transport->staffActivation = $staffActivation; - if (!$staffActivation) - { - $_SESSION['user-id'] = $dbUser->id; - \Chibi\Registry::getBootstrap()->attachUser(); - } - } } diff --git a/src/Controllers/PostController.php b/src/Controllers/PostController.php index c98ceb67..96efeb80 100644 --- a/src/Controllers/PostController.php +++ b/src/Controllers/PostController.php @@ -175,6 +175,8 @@ class PostController $this->context->scripts []= 'upload.js'; $this->context->subTitle = 'upload'; PrivilegesHelper::confirmWithException($this->context->user, Privilege::UploadPost); + if ($this->config->registration->needEmailForUploading) + PrivilegesHelper::confirmEmail($this->context->user); if (!empty($_FILES['file']['name'])) { diff --git a/src/Controllers/UserController.php b/src/Controllers/UserController.php index 09972eb4..5a5a64ba 100644 --- a/src/Controllers/UserController.php +++ b/src/Controllers/UserController.php @@ -9,6 +9,27 @@ class UserController return $user; } + private static function sendEmailConfirmation($user) + { + \Chibi\Registry::getContext()->mailSent = true; + $regConfig = \Chibi\Registry::getConfig()->registration; + $tokens = []; + $tokens['host'] = $_SERVER['HTTP_HOST']; + $tokens['link'] = \Chibi\UrlHelper::route('user', 'activation', ['token' => $user->email_token]); + + $body = wordwrap(TextHelper::replaceTokens($regConfig->activationEmailBody, $tokens), 70); + $subject = TextHelper::replaceTokens($regConfig->activationEmailSubject, $tokens); + $senderName = TextHelper::replaceTokens($regConfig->activationEmailSenderName, $tokens); + $senderEmail = $regConfig->activationEmailSenderEmail; + $recipientEmail = $user->email_unconfirmed; + + $headers = []; + $headers[] = sprintf('From: %s <%s>', $senderName, $senderEmail); + $headers[] = sprintf('Subject: %s', $subject); + $headers[] = sprintf('X-Mailer: PHP/%s', phpversion()); + mail($recipientEmail, $subject, $body, implode("\r\n", $headers)); + } + /** @@ -55,7 +76,8 @@ class UserController $dbQuery->orderBy('join_date')->desc(); break; case 'pending': - $dbQuery->whereNot('staff_confirmed')->and($this->config->registration->staffActivation); + $dbQuery->where('staff_confirmed IS NULL'); + $dbQuery->or('staff_confirmed = 0'); break; default: throw new SimpleException('Unknown sort style'); @@ -218,11 +240,20 @@ class UserController $edited = true; } - if ($suppliedEmail != '' and $suppliedEmail != $user->email) + if ($suppliedEmail != '' and $suppliedEmail != $user->email_confirmed) { PrivilegesHelper::confirmWithException($this->context->user, Privilege::ChangeUserEmail, $secondary); $suppliedEmail = Model_User::validateEmail($suppliedEmail); - $user->email = $suppliedEmail; + if ($this->context->user->id == $user->id) + { + $user->email_unconfirmed = $suppliedEmail; + if (!empty($user->email_unconfirmed)) + self::sendEmailConfirmation($user); + } + else + { + $user->email_confirmed = $suppliedEmail; + } $edited = true; } @@ -370,4 +401,117 @@ class UserController $this->context->transport->success = true; } + + + + /** + * @route /register + */ + public function registrationAction() + { + $this->context->handleExceptions = true; + $this->context->stylesheets []= 'auth.css'; + $this->context->subTitle = 'registration form'; + + //check if already logged in + if ($this->context->loggedIn) + { + \Chibi\UrlHelper::forward(\Chibi\UrlHelper::route('index', 'index')); + return; + } + + $suppliedName = InputHelper::get('name'); + $suppliedPassword1 = InputHelper::get('password1'); + $suppliedPassword2 = InputHelper::get('password2'); + $suppliedEmail = InputHelper::get('email'); + $this->context->suppliedName = $suppliedName; + $this->context->suppliedPassword1 = $suppliedPassword1; + $this->context->suppliedPassword2 = $suppliedPassword2; + $this->context->suppliedEmail = $suppliedEmail; + + if ($suppliedName !== null) + { + $suppliedName = Model_User::validateUserName($suppliedName); + + if ($suppliedPassword1 != $suppliedPassword2) + throw new SimpleException('Specified passwords must be the same'); + $suppliedPassword = Model_User::validatePassword($suppliedPassword1); + + $suppliedEmail = Model_User::validateEmail($suppliedEmail); + if (empty($suppliedEmail) and $this->config->registration->needEmailForRegistering) + throw new SimpleException('E-mail address is required - you will be sent confirmation e-mail.'); + + //register the user + $dbUser = R::dispense('user'); + $dbUser->name = $suppliedName; + $dbUser->pass_salt = md5(mt_rand() . uniqid()); + $dbUser->pass_hash = Model_User::hashPassword($suppliedPassword, $dbUser->pass_salt); + $dbUser->email_unconfirmed = $suppliedEmail; + + //prepare unique registration token + do + { + $emailToken = md5(mt_rand() . uniqid()); + } + while (R::findOne('user', 'email_token = ?', [$emailToken]) !== null); + $dbUser->email_token = $emailToken; + + $dbUser->join_date = time(); + if (R::findOne('user') === null) + { + $dbUser->access_rank = AccessRank::Admin; + $dbUser->staff_confirmed = true; + $dbUser->email_confirmed = $suppliedEmail; + } + else + { + $dbUser->access_rank = AccessRank::Registered; + $dbUser->staff_confirmed = false; + $dbUser->staff_confirmed = null; + if (!empty($dbUser->email_unconfirmed)) + self::sendEmailConfirmation($dbUser); + } + + //save the user to db if everything went okay + R::store($dbUser); + $this->context->transport->success = true; + + if (!$this->config->registration->needEmailForRegistering and !$this->config->registration->staffActivation) + { + $_SESSION['user-id'] = $dbUser->id; + \Chibi\Registry::getBootstrap()->attachUser(); + } + } + } + + + + /** + * @route /activation/{token} + */ + public function activationAction($token) + { + $this->context->subTitle = 'account activation'; + + if (empty($token)) + throw new SimpleException('Invalid activation token'); + + $dbUser = R::findOne('user', 'email_token = ?', [$token]); + if ($dbUser === null) + throw new SimpleException('No user with such activation token'); + + if (!$dbUser->email_unconfirmed) + throw new SimpleException('This user was already activated'); + + $dbUser->email_confirmed = $dbUser->email_unconfirmed; + $dbUser->email_unconfirmed = null; + R::store($dbUser); + $this->context->transport->success = true; + + if (!$this->config->registration->staffActivation) + { + $_SESSION['user-id'] = $dbUser->id; + \Chibi\Registry::getBootstrap()->attachUser(); + } + } } diff --git a/src/Helpers/PrivilegesHelper.php b/src/Helpers/PrivilegesHelper.php index 74bac2bc..c80e1174 100644 --- a/src/Helpers/PrivilegesHelper.php +++ b/src/Helpers/PrivilegesHelper.php @@ -48,6 +48,12 @@ class PrivilegesHelper throw new SimpleException('Insufficient privileges'); } } + + public static function confirmEmail($user) + { + if (!$user->email_confirmed) + throw new SimpleException('Need e-mail address confirmation to continue'); + } } PrivilegesHelper::init(); diff --git a/src/Models/Model_User.php b/src/Models/Model_User.php index 50d94518..3f57e86a 100644 --- a/src/Models/Model_User.php +++ b/src/Models/Model_User.php @@ -3,8 +3,8 @@ class Model_User extends RedBean_SimpleModel { public function getAvatarUrl($size = 32) { - $subject = !empty($this->email) - ? $this->email + $subject = !empty($this->email_confirmed) + ? $this->email_confirmed : $this->pass_salt . $this->name; $hash = md5(strtolower(trim($subject))); $url = 'http://www.gravatar.com/avatar/' . $hash . '?s=' . $size . '&d=retro'; @@ -59,7 +59,7 @@ class Model_User extends RedBean_SimpleModel $dbUser = R::findOne('user', 'name = ?', [$userName]); if ($dbUser !== null) { - if (!$dbUser->email_confirmed and \Chibi\Registry::getConfig()->registration->emailActivation) + if (!$dbUser->email_confirmed and \Chibi\Registry::getConfig()->registration->needEmailForRegistering) throw new SimpleException('User with this name is already registered and awaits e-mail confirmation'); if (!$dbUser->staff_confirmed and \Chibi\Registry::getConfig()->registration->staffActivation) diff --git a/src/Views/auth-activation.phtml b/src/Views/auth-activation.phtml deleted file mode 100644 index bfc8b2ea..00000000 --- a/src/Views/auth-activation.phtml +++ /dev/null @@ -1,6 +0,0 @@ -context->transport->success === true): ?> -

Activation completed successfully.

- context->transport->staffActivation): ?> -

However, your account still must be confirmed by staff.

- - diff --git a/src/Views/auth-login.phtml b/src/Views/auth-login.phtml index 06b8f948..3cb44bcc 100644 --- a/src/Views/auth-login.phtml +++ b/src/Views/auth-login.phtml @@ -1,7 +1,7 @@
-

If you don't have an account yet,
click here to create a new one.

+

If you don't have an account yet,
click here to create a new one.

diff --git a/src/Views/layout-normal.phtml b/src/Views/layout-normal.phtml index 45ee276b..26fd5311 100644 --- a/src/Views/layout-normal.phtml +++ b/src/Views/layout-normal.phtml @@ -44,7 +44,7 @@ if (!$this->context->loggedIn) { $nav []= ['Log in', \Chibi\UrlHelper::route('auth', 'login')]; - $nav []= ['Register', \Chibi\UrlHelper::route('auth', 'register')]; + $nav []= ['Register', \Chibi\UrlHelper::route('user', 'registration')]; } else { diff --git a/src/Views/user-activation.phtml b/src/Views/user-activation.phtml new file mode 100644 index 00000000..8549d2d2 --- /dev/null +++ b/src/Views/user-activation.phtml @@ -0,0 +1,6 @@ +context->transport->success === true): ?> +

Activation completed successfully. + config->registration->staffActivation): ?> +
However, your account still must be confirmed by staff. +

+ diff --git a/src/Views/auth-register.phtml b/src/Views/user-registration.phtml similarity index 75% rename from src/Views/auth-register.phtml rename to src/Views/user-registration.phtml index 7c7d1767..6c4ab083 100644 --- a/src/Views/auth-register.phtml +++ b/src/Views/user-registration.phtml @@ -1,13 +1,13 @@ context->transport->success === true): ?> -

Congratulations, your account was created.

- context->transport->emailActivation): ?> -

Please wait for activation e-mail.

- context->transport->staffActivation): ?> -

After this, your registration must be confirmed by staff.

+

Congratulations, your account was created. + context->mailSent)): ?> +
Please wait for activation e-mail. + config->registration->staffActivation): ?> +
After this, your registration must be confirmed by staff. - context->transport->staffActivation): ?> -

Your registration must be confirmed by staff.

- + config->registration->staffActivation): ?> +
Your registration must be now confirmed by staff. +

@@ -30,7 +30,7 @@
- +
diff --git a/src/Views/user-view.phtml b/src/Views/user-view.phtml index f17cb799..ce2b9a00 100644 --- a/src/Views/user-view.phtml +++ b/src/Views/user-view.phtml @@ -22,7 +22,7 @@ context->user->id == $this->context->transport->user->id): ?> @@ -199,7 +199,7 @@ context->transport->success === true): ?> -

Account settings updated!

+

Account settings updated! context->mailSent)) echo 'You will be sent new e-mail address confirmation message soon.' ?>

context->transport->errorMessage)): ?>

Error: context->transport->errorMessage ?>