| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465 |
- <?php
- /**
- * Login
- *
- * Copyright 2010 by Shaun McCormick <shaun+login@modx.com>
- *
- * Login is free software; you can redistribute it and/or modify it under the
- * terms of the GNU General Public License as published by the Free Software
- * Foundation; either version 2 of the License, or (at your option) any later
- * version.
- *
- * Login is distributed in the hope that it will be useful, but WITHOUT ANY
- * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
- * A PARTICULAR PURPOSE. See the GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along with
- * Login; if not, write to the Free Software Foundation, Inc., 59 Temple
- * Place, Suite 330, Boston, MA 02111-1307 USA
- *
- * @package login
- */
- /**
- * Handles registration of users
- *
- * @package login
- * @subpackage controllers
- */
- class LoginRegisterController extends LoginController {
- /** @var boolean $hasPosted */
- public $hasPosted = false;
- public $success = false;
- /**
- * Load default properties for this controller
- * @return void
- */
- public function initialize() {
- $this->modx->lexicon->load('login:register');
- $this->setDefaultProperties(array(
- 'activation' => true,
- 'activationEmail' => '',
- 'activationEmailSubject' => $this->modx->lexicon('register.activation_email_subject'),
- 'activationEmailTpl' => 'lgnActivateEmailTpl',
- 'activationEmailTplType' => 'modChunk',
- 'activationEmailTplAlt' => '',
- 'activationResourceId' => '',
- 'emailField' => 'email',
- 'errTpl' => '<span class="error">[[+error]]</span>',
- 'excludeExtended' => '',
- 'moderatedResourceId' => '',
- 'passwordField' => 'password',
- 'persistParams' => '',
- 'placeholderPrefix' => '',
- 'preHooks' => '',
- 'postHooks' => '',
- 'redirectBack' => '',
- 'redirectBackParams' => '',
- 'redirectUnsetDefaultParams' => false,
- 'submittedResourceId' => '',
- 'submitVar' => 'login-register-btn',
- 'successMsg' => '',
- 'useExtended' => true,
- 'usergroups' => '',
- 'usernameField' => 'username',
- 'validate' => '',
- 'validatePassword' => true,
- 'autoLogin' => false,
- 'jsonResponse' => false,
- 'validationErrorMessage' => 'A form validation error occurred. Please check the values you have entered.',
- 'preserveFieldsAfterRegister' => true
- ));
- }
- /**
- * Handle the Register snippet business logic
- * @return string
- */
- public function process() {
- $this->checkForPost();
- $this->preLoad();
- if (!$this->hasPosted) {
- return '';
- }
- if (!$this->loadDictionary()) {
- return '';
- }
- $fields = $this->validateFields();
- $this->dictionary->reset();
- $this->dictionary->fromArray($fields);
- $this->validateUsername();
- if ($this->getProperty('validatePassword',true,'isset')) {
- $this->validatePassword();
- }
- if ($this->getProperty('ensurePasswordStrength',false,'isset')) {
- $this->ensurePasswordStrength();
- }
- if ($this->getProperty('generatePassword',false,'isset')) {
- $this->generatePassword();
- }
- $this->validateEmail();
- $placeholderPrefix = rtrim($this->getProperty('placeholderPrefix', ''), '.');
- $errorPrefix = ($placeholderPrefix) ? $placeholderPrefix . '.error' : 'error';
- if ($this->validator->hasErrors()) {
- $errors = $this->validator->getErrors();
- // Return JSON error response if requested.
- if ($this->getProperty('jsonResponse')) {
- $jsonErrorOutput = array(
- 'success' => false,
- 'message' => $this->getProperty('validationErrorMessage'),
- 'errors' => $errors
- );
- header('Content-Type: application/json;charset=utf-8');
- exit($this->modx->toJSON($jsonErrorOutput));
- }
- $this->modx->toPlaceholders($errors, $errorPrefix);
- $this->modx->toPlaceholder('validation_error', true, $placeholderPrefix);
- $this->modx->toPlaceholder('validation_error_message', $this->getProperty('validationErrorMessage'), $placeholderPrefix);
- } else {
- $this->loadPreHooks();
- /* process hooks */
- if ($this->preHooks->hasErrors()) {
- $this->modx->toPlaceholders($this->preHooks->getErrors(), $errorPrefix);
- $errorMsg = $this->preHooks->getErrorMessage();
- $this->modx->toPlaceholder('error.message', $errorMsg, $placeholderPrefix);
- } else {
- /* everything good, go ahead and register */
- $result = $this->runProcessor('register');
- if ($result !== true) {
- $this->modx->toPlaceholder('error.message', $result, $placeholderPrefix);
- } else {
- // Return JSON success response if requested
- if ($this->getProperty('jsonResponse')) {
- $jsonSuccessOutput = array(
- 'success' => true,
- 'message' => $this->getProperty('successMsg','User registration successful.')
- );
- header('Content-Type: application/json;charset=utf-8');
- exit($this->modx->toJSON($jsonSuccessOutput));
- }
- $this->modx->toPlaceholder('successMsg', $this->getProperty('successMsg','User registration successful.'), $placeholderPrefix);
- $this->success = true;
- }
- }
- }
- $placeholders = $this->dictionary->toArray();
- $placeholders = $this->escapePlaceholders($placeholders);
- $this->modx->toPlaceholders($placeholders, $placeholderPrefix);
- foreach ($placeholders as $k => $v) {
- if (is_array($v)) {
- $this->modx->toPlaceholder($k, json_encode($v), $placeholderPrefix);
- }
- }
- if (!$this->success || $this->getProperty('preserveFieldsAfterRegister')) {
- $this->modx->setPlaceholders($this->dictionary->toArray(), $placeholderPrefix);
- }
- return '';
- }
- /**
- * Load any pre-registration hooks
- * @return void
- */
- public function loadPreHooks() {
- $preHooks = $this->getProperty('preHooks','');
- $this->loadHooks('preHooks');
- if (!empty($preHooks)) {
- $fields = $this->dictionary->toArray();
- /* do pre-register hooks */
- $this->preHooks->loadMultiple($preHooks,$fields,array(
- 'submitVar' => $this->getProperty('submitVar'),
- 'usernameField' => $this->getProperty('usernameField','username'),
- ));
- $values = $this->preHooks->getValues();
- if (!empty($values)) {
- $this->dictionary->fromArray($values);
- }
- }
- }
- /**
- * Validate the fields in the form
- * @return array
- */
- public function validateFields() {
- $this->loadValidator();
- $fields = $this->validator->validateFields($this->dictionary,$this->getProperty('validate',''));
- foreach ($fields as $k => $v) {
- $fields[$k] = str_replace(array('[',']'),array('[',']'),$v);
- }
- return $fields;
- }
- /**
- * Ensure the username field is being sent and the username is not taken
- *
- * @return boolean
- */
- public function validateUsername() {
- $usernameField = $this->getProperty('usernameField','username');
- $username = $this->dictionary->get($usernameField);
- $success = true;
- /* ensure username field exists and isn't empty */
- if (empty($username) && !$this->validator->hasErrorsInField($usernameField)) {
- $this->validator->addError($usernameField,$this->modx->lexicon('register.field_required'));
- $success = false;
- } else {
- /* make sure username isnt taken */
- /** @var modUser $alreadyExists */
- $alreadyExists = $this->modx->getObject('modUser',array('username' => $username));
- if ($alreadyExists) {
- $cachePwd = $alreadyExists->get('cachepwd');
- if ($this->getProperty('removeExpiredRegistrations',true,'isset') && $alreadyExists->get('active') == 0 && !empty($cachePwd)) {
- /* if inactive and has a cachepwd, probably an expired
- * activation account, so let's remove it
- * and let user re-register
- */
- if (!$alreadyExists->remove()) {
- $this->modx->log(modX::LOG_LEVEL_ERROR,'[Login] Could not remove old, deactive user with cachepwd.');
- $success = false;
- }
- } else {
- $this->validator->addError($usernameField,$this->modx->lexicon('register.username_taken'));
- $success = false;
- }
- }
- }
- return $success;
- }
- public function getPassword() {
- $passwordField = $this->getProperty('passwordField','password');
- $password = $this->dictionary->get($passwordField);
- if ($this->getProperty('trimPassword',true,'isset')) {
- $password = trim($password);
- }
- return $password;
- }
- /**
- * Validate the password field and trim it if specified
- *
- * @return boolean
- */
- public function validatePassword() {
- $password = $this->getPassword();
- $passwordField = $this->getProperty('passwordField','password');
- $success = true;
- /* ensure password field isn't empty */
- if (empty($password) && !$this->validator->hasErrorsInField($passwordField)) {
- $this->validator->addError($passwordField,$this->modx->lexicon('register.field_required'));
- $success = false;
- }
- return $success;
- }
- /**
- * Automatically generate a password for the user
- * @return string
- */
- public function generatePassword() {
- $classKey = $this->dictionary->get('class_key');
- if (empty($classKey)) $classKey = 'modUser';
- /** @var modUser $user */
- $user = $this->modx->newObject($classKey);
- $password = $user->generatePassword();
- $this->dictionary->set($this->getProperty('passwordField','password'),$password);
- $this->dictionary->set('password_confirm',$password);
- return $password;
- }
- /**
- * Validate the email address, and ensure it is not empty or already taken
- * @return boolean
- */
- public function validateEmail() {
- $emailField = $this->getProperty('emailField','email');
- $email = $this->dictionary->get($emailField);
- $success = true;
- /* ensure email field isn't empty */
- if (empty($email) && !$this->validator->hasErrorsInField($emailField)) {
- $this->validator->addError($emailField,$this->modx->lexicon('register.field_required'));
- $success = false;
- /* ensure if allow_multiple_emails setting is false, prevent duplicate emails */
- } else if (!$this->modx->getOption('allow_multiple_emails',null,false)) {
- /** @var modUserProfile $emailTaken */
- $emailTaken = $this->modx->getObject('modUserProfile',array('email' => $email));
- if ($emailTaken) {
- $this->validator->addError($emailField,$this->modx->lexicon('register.email_taken',array('email' => $email)));
- $success = false;
- }
- }
- return $success;
- }
- /**
- * Check for a POST submission
- * @return void
- */
- public function checkForPost() {
- $this->hasPosted = !empty($_POST) && (empty($this->scriptProperties['submitVar']) || !empty($_POST[$this->scriptProperties['submitVar']]));
- }
- /**
- * Do any pre-processing before POST
- * @return void
- */
- public function preLoad() {
- $preHooks = $this->getProperty('preHooks','');
- /* if using recaptcha, load recaptcha html */
- if (strpos($preHooks,'recaptcha') !== false) {
- $this->loadReCaptcha();
- }
- /* if using math hook, load default placeholders */
- if (strpos($preHooks,'math') !== false && !$this->hasPosted) {
- $this->preLoadMath();
- }
- }
- /**
- * Pre-Load the data values for the math hook
- * @return void
- */
- public function preLoadMath() {
- $mathMaxRange = $this->getProperty('mathMaxRange',100);
- $mathMinRange = $this->getProperty('mathMinRange',10);
- $op1 = rand($mathMinRange,$mathMaxRange);
- $op2 = rand($mathMinRange,$mathMaxRange);
- $placeholderPrefix = rtrim($this->getProperty('placeholderPrefix', ''), '.');
- if ($op2 > $op1) { $t = $op2; $op2 = $op1; $op1 = $t; } /* swap so we always get positive #s */
- $operators = array('+','-');
- $operator = rand(0,1);
- $this->modx->toPlaceholders(array(
- $this->getProperty('mathOp1Field','op1') => $op1,
- $this->getProperty('mathOp2Field','op2') => $op2,
- $this->getProperty('mathOperatorField','operator') => $operators[$operator],
- ), $placeholderPrefix);
- }
- public function loadReCaptcha() {
- $placeholderPrefix = rtrim($this->getProperty('placeholderPrefix', ''), '.');
- /** @var reCaptcha $recaptcha */
- $recaptcha = $this->modx->getService('recaptcha','reCaptcha',$this->login->config['modelPath'].'recaptcha/');
- if ($recaptcha instanceof reCaptcha) {
- $this->modx->lexicon->load('login:recaptcha');
- $recaptchaTheme = $this->getProperty('recaptchaTheme','clean');
- $recaptchaWidth = $this->getProperty('recaptchaWidth',500);
- $recaptchaHeight = $this->getProperty('recaptchaHeight',300);
- $html = $recaptcha->getHtml($recaptchaTheme,$recaptchaWidth,$recaptchaHeight);
- $this->modx->toPlaceholder('recaptcha_html', $html, $placeholderPrefix);
- } else {
- $this->modx->log(modX::LOG_LEVEL_ERROR,'[Register] '.$this->modx->lexicon('register.recaptcha_err_load'));
- }
- }
- /**
- * Algorithm to ensure and suggest password strength
- * @return bool
- */
- public function ensurePasswordStrength() {
- $ensured = false;
- $password = $this->getPassword();
- $passwordField = $this->getProperty('passwordField','password');
- $passwordWordSeparator = $this->getProperty('passwordWordSeparator',' ','isset');
- if (strlen($passwordWordSeparator) == 0) $passwordWordSeparator = ' ';
- $wordCount = $this->getWordsInString($password,$passwordWordSeparator);
- $minimumStrongPasswordWordCount = $this->getProperty('minimumStrongPasswordWordCount',4,'!empty');
- if ($wordCount < $minimumStrongPasswordWordCount || $minimumStrongPasswordWordCount == 0) {
- $passwordStrengthThreshold = $this->getProperty('maximumPossibleStrongerPasswords',25,'!empty');
- if ($passwordStrengthThreshold > 0) {
- $possible = $this->getPossibleStrongerPasswords($password);
- if (count($possible) > $passwordStrengthThreshold) {
- $ensurePasswordStrengthSuggestions = $this->getProperty('ensurePasswordStrengthSuggestions',5,'!empty');
- $suggestionIndexes = array_rand($possible,$ensurePasswordStrengthSuggestions);
- $suggestions = array();
- foreach ($suggestionIndexes as $idx) {
- $suggestions[] = $possible[$idx];
- }
- $this->validator->addError($passwordField,$this->modx->lexicon('register.use_stronger_password',array(
- 'suggestions' => implode(', ',$suggestions),
- )));
- }
- } else {
- $ensured = true;
- }
- } else {
- $ensured = true;
- }
- return $ensured;
- }
- public function getWordsInString($str,$separator) {
- return count(explode($separator,$str));
- }
- /** @var array $strongPasswordMap */
- public $strongPasswordMap = array(
- 'a' => array('@','A','4'),
- 'b' => array('8','B'),
- 'c' => array('('),
- 'e' => array('3','E'),
- 'f' => array('ph'),
- 'g' => array('6'),
- 'i' => array('1','!','|'),
- 'l' => array('1','L'),
- 'n' => array('en'),
- 'o' => array('0','O'),
- 's' => array('$','5'),
- 't' => array('7','+'),
- 'x' => array('X'),
- 'z' => array('2'),
- );
- /**
- * Given a password, find stronger ones
- *
- * @param string $password
- * @return array
- */
- public function getPossibleStrongerPasswords($password) {
- $passwordLength = strlen($password);
- if ($passwordLength == 1) return isset($this->strongPasswordMap[$password]) ? $this->strongPasswordMap[$password] : $password;
- $rest = $this->getPossibleStrongerPasswords(substr($password,1));
- $restLength = count($rest);
- $current = isset($this->strongPasswordMap[$password[0]]) ? $this->strongPasswordMap[$password[0]] : null;
- $currentLength = count($current);
- $result = array();
- if ($current) {
- for ($i=0;$i<$currentLength;$i++) {
- for ($j=0;$j<$restLength;$j++) {
- $result[] = $current[$i].$rest[$j];
- }
- }
- } else {
- for ($j=0;$j<$restLength;$j++) {
- $result[] = $password[0].$rest[$j];
- }
- }
- return $result;
- }
- }
- return 'LoginRegisterController';
|