diff --git a/core/lexicon/en/user.inc.php b/core/lexicon/en/user.inc.php index c70b7b441ca..984b0326fe0 100644 --- a/core/lexicon/en/user.inc.php +++ b/core/lexicon/en/user.inc.php @@ -25,6 +25,7 @@ $_lang['password_gen_specify'] = 'Let me specify the password:'; $_lang['password_method'] = 'Password notification method'; $_lang['password_method_screen'] = 'Show the new password on screen.'; +$_lang['password_gen_user_email_specify'] = 'Let the user choose their own password via email'; $_lang['notify_new_user'] = 'Email this user about their new login for this website.'; $_lang['password_new'] = 'New Password'; $_lang['password_notification'] = 'Password Notification'; @@ -197,6 +198,5 @@ $_lang['users'] = 'Users'; $_lang['user_createdon'] = 'Created On'; $_lang['user_createdon_desc'] = 'The date the user was created.'; - -// Renamed and/or deprecated as of 3.0.4; remove in 3.1.0 -$_lang['ugc_mutate'] = 'User Group Access to Context'; // now in access.inc.php, access_context_create \ No newline at end of file +$_lang['user_password_email_subject'] = 'Set up your password'; +$_lang['user_password_email'] = '

Set up your password

We received a request to set up your MODX Revolution password. You can set up your password by clicking the button below and following the instructions on screen.

Set up my password

If you did not send this request, please ignore this email.

'; diff --git a/core/src/Revolution/Processors/Security/User/Create.php b/core/src/Revolution/Processors/Security/User/Create.php index 7a5045f81cb..fa7b2019ace 100644 --- a/core/src/Revolution/Processors/Security/User/Create.php +++ b/core/src/Revolution/Processors/Security/User/Create.php @@ -12,6 +12,7 @@ use Exception; +use MODX\Revolution\Hashing\modHashing; use MODX\Revolution\Processors\Model\CreateProcessor; use MODX\Revolution\Processors\Processor; use MODX\Revolution\modUser; @@ -19,6 +20,8 @@ use MODX\Revolution\modUserGroupMember; use MODX\Revolution\modUserProfile; use MODX\Revolution\modX; +use MODX\Revolution\Registry\modRegister; +use MODX\Revolution\Registry\modRegistry; use MODX\Revolution\Smarty\modSmarty; /** @@ -232,6 +235,54 @@ public function sendNotificationEmail() { 'html' => true, ]); } + + if ( + $this->getProperty('passwordgenmethod') === 'user_email_specify' + ) { + $activationHash = bin2hex(random_bytes(32)); + + /** @var modRegistry $registry */ + $registry = $this->modx->getService('registry', 'registry.modRegistry'); + /** @var modRegister $register */ + $register = $registry->getRegister('user', 'registry.modDbRegister'); + $register->connect(); + $register->subscribe('/pwd/change/'); + $register->send('/pwd/change/', [$activationHash => $this->object->get('username')], ['ttl' => 86400]); + + // Send activation email + $message = $this->modx->lexicon('user_password_email'); + $placeholders = array_merge($this->modx->config, $this->object->toArray()); + $placeholders['hash'] = $activationHash; + + // Store previous placeholders + $ph = $this->modx->placeholders; + // now set those useful for modParser + $this->modx->setPlaceholders($placeholders); + $this->modx->getParser()->processElementTags('', $message, true, false, '[[', ']]', [], 10); + $this->modx->getParser()->processElementTags('', $message, true, true, '[[', ']]', [], 10); + // Then restore previous placeholders to prevent any breakage + $this->modx->placeholders = $ph; + + $this->modx->getService('smarty', 'smarty.modSmarty', '', ['template_dir' => $this->modx->getOption('manager_path') . 'templates/default/']); + + $this->modx->smarty->assign('_config', $this->modx->config); + $this->modx->smarty->assign('content', $message, true); + + $sent = $this->object->sendEmail( + $this->modx->smarty->fetch('email/default.tpl'), + [ + 'from' => $this->modx->getOption('emailsender'), + 'fromName' => $this->modx->getOption('site_name'), + 'sender' => $this->modx->getOption('emailsender'), + 'subject' => $this->modx->lexicon('user_password_email_subject'), + 'html' => true, + ] + ); + + if (!$sent) { + return $this->failure($this->modx->lexicon('error_sending_email_to') . $this->object->get('email')); + } + } } /** diff --git a/core/src/Revolution/Processors/Security/User/Update.php b/core/src/Revolution/Processors/Security/User/Update.php index 8aaad74ec5f..757271b2da3 100644 --- a/core/src/Revolution/Processors/Security/User/Update.php +++ b/core/src/Revolution/Processors/Security/User/Update.php @@ -19,6 +19,8 @@ use MODX\Revolution\modUserGroupMember; use MODX\Revolution\modUserProfile; use MODX\Revolution\modX; +use MODX\Revolution\Registry\modRegister; +use MODX\Revolution\Registry\modRegistry; /** * Update a user. @@ -285,6 +287,7 @@ public function setUserGroups() { */ public function afterSave() { $this->setUserGroups(); + $this->sendNotificationEmail(); if ($this->activeStatusChanged) { $this->fireAfterActiveStatusChange(); } @@ -305,6 +308,62 @@ public function fireAfterActiveStatusChange() { ); } + /** + * Send the password notification email, if specified + * + * @return void + * @throws \Exception + */ + public function sendNotificationEmail() { + if ($this->getProperty('passwordgenmethod') === 'user_email_specify') { + $activationHash = bin2hex(random_bytes(32)); + + /** @var modRegistry $registry */ + $registry = $this->modx->getService('registry', 'registry.modRegistry'); + /** @var modRegister $register */ + $register = $registry->getRegister('user', 'registry.modDbRegister'); + $register->connect(); + $register->subscribe('/pwd/change/'); + $register->send('/pwd/change/', [$activationHash => $this->object->get('username')], ['ttl' => 86400]); + + $this->modx->lexicon->load('core:login'); + + // Send activation email + $message = $this->modx->lexicon('user_password_email'); + $placeholders = array_merge($this->modx->config, $this->object->toArray()); + $placeholders['hash'] = $activationHash; + + // Store previous placeholders + $ph = $this->modx->placeholders; + // now set those useful for modParser + $this->modx->setPlaceholders($placeholders); + $this->modx->getParser()->processElementTags('', $message, true, false, '[[', ']]', [], 10); + $this->modx->getParser()->processElementTags('', $message, true, true, '[[', ']]', [], 10); + // Then restore previous placeholders to prevent any breakage + $this->modx->placeholders = $ph; + + $this->modx->getService('smarty', 'smarty.modSmarty', '', ['template_dir' => $this->modx->getOption('manager_path') . 'templates/default/']); + + $this->modx->smarty->assign('_config', $this->modx->config); + $this->modx->smarty->assign('content', $message, true); + + $sent = $this->object->sendEmail( + $this->modx->smarty->fetch('email/default.tpl'), + [ + 'from' => $this->modx->getOption('emailsender'), + 'fromName' => $this->modx->getOption('site_name'), + 'sender' => $this->modx->getOption('emailsender'), + 'subject' => $this->modx->lexicon('user_password_email_subject'), + 'html' => true, + ] + ); + + if (!$sent) { + return $this->failure($this->modx->lexicon('error_sending_email_to') . $this->object->get('email')); + } + } + } + /** * {@inheritDoc} * @return array|string @@ -314,20 +373,23 @@ public function cleanup() $userArray = $this->object->toArray(); $profile = $this->object->getOne('Profile'); if ($profile) { - $userArray = array_merge($profile->toArray(),$userArray); + $userArray = array_merge($profile->toArray(), $userArray); } unset($userArray['password'], $userArray['cachepwd'], $userArray['sessionid'], $userArray['salt']); $passwordNotifyMethod = $this->getProperty('passwordnotifymethod'); if (!empty($passwordNotifyMethod) && !empty($this->newPassword) && $passwordNotifyMethod == 's') { - return $this->success($this->modx->lexicon('user_updated_password_message', - [ - 'username' => $this->object->get('username'), - 'password' => $this->newPassword, - ] - ), $this->object); - } else { - return $this->success('',$this->object); + return $this->success( + $this->modx->lexicon( + 'user_updated_password_message', + [ + 'username' => $this->object->get('username'), + 'password' => $this->newPassword, + ] + ), + $this->object + ); } + return $this->success('', $this->object); } } diff --git a/core/src/Revolution/Processors/Security/User/Validation.php b/core/src/Revolution/Processors/Security/User/Validation.php index daa34885759..bb789ef763d 100644 --- a/core/src/Revolution/Processors/Security/User/Validation.php +++ b/core/src/Revolution/Processors/Security/User/Validation.php @@ -76,12 +76,14 @@ public function alreadyExists($name) { public function checkPassword() { $newPassword = $this->processor->getProperty('newpassword',null); $id = $this->processor->getProperty('id'); - if ($newPassword !== null && $newPassword != 'false' || empty($id)) { + + $passwordGenerationMethod = $this->processor->getProperty('passwordgenmethod','g'); + if ($passwordGenerationMethod !== 'user_email_specify' && ($newPassword !== null && $newPassword != 'false' || empty($id))) { $passwordNotifyMethod = $this->processor->getProperty('passwordnotifymethod',null); if (empty($passwordNotifyMethod)) { $this->processor->addFieldError('password_notify_method',$this->modx->lexicon('user_err_not_specified_notification_method')); } - $passwordGenerationMethod = $this->processor->getProperty('passwordgenmethod','g'); + if ($passwordGenerationMethod == 'g') { $autoPassword = $this->user->generatePassword(); $this->user->set('password', $autoPassword); diff --git a/manager/assets/modext/widgets/security/modx.panel.user.js b/manager/assets/modext/widgets/security/modx.panel.user.js index de41d251cb1..24c8ee737fe 100644 --- a/manager/assets/modext/widgets/security/modx.panel.user.js +++ b/manager/assets/modext/widgets/security/modx.panel.user.js @@ -483,6 +483,13 @@ Ext.extend(MODx.panel.User,MODx.FormPanel,{ ,xtype: 'radio' ,inputValue: 'spec' ,value: 'spec' + },{ + id: 'modx-user-password-genmethod-user-email-specify' + ,name: 'passwordgenmethod' + ,boxLabel: _('password_gen_user_email_specify') + ,xtype: 'radio' + ,inputValue: 'user_email_specify' + ,value: 'user_email_specify' }] },{ id: 'modx-user-panel-newpassword'