diff --git a/config/logintoboggan.settings.yml b/config/logintoboggan.settings.yml new file mode 100644 index 0000000..154d300 --- /dev/null +++ b/config/logintoboggan.settings.yml @@ -0,0 +1,10 @@ +unified_login: false +pre_auth_role: 2 +redirect_on_confirm: false +override_destination_parameter: true +login_successful_message: false +minimum_password_length: false +login_with_email: false +confirm_email_at_registration: false +purge_unvalidated_user_interval: false +immediate_login_on_register: true diff --git a/lib/Drupal/logintoboggan/Access/LogintobogganReValidateAccess.php b/lib/Drupal/logintoboggan/Access/LogintobogganReValidateAccess.php new file mode 100644 index 0000000..3891882 --- /dev/null +++ b/lib/Drupal/logintoboggan/Access/LogintobogganReValidateAccess.php @@ -0,0 +1,36 @@ +id() == $user->id() || $account->hasPermission('administer users')) + ? static::ALLOW + : static::DENY; + } +} diff --git a/lib/Drupal/logintoboggan/Access/LogintobogganValidateAccess.php b/lib/Drupal/logintoboggan/Access/LogintobogganValidateAccess.php new file mode 100644 index 0000000..6e8dc80 --- /dev/null +++ b/lib/Drupal/logintoboggan/Access/LogintobogganValidateAccess.php @@ -0,0 +1,33 @@ +isAuthenticated() && arg(3) < REQUEST_TIME ? static::ALLOW : static::DENY; + } +} diff --git a/lib/Drupal/logintoboggan/Controller/LogintobogganController.php b/lib/Drupal/logintoboggan/Controller/LogintobogganController.php new file mode 100644 index 0000000..1cab82d --- /dev/null +++ b/lib/Drupal/logintoboggan/Controller/LogintobogganController.php @@ -0,0 +1,154 @@ +get('module_handler')); + } + + /** + * This will return the output of the page. + */ + public function logintobogganValidateEmail($user, $timestamp, $hashed_pass, $operation) { + + $account = user_load($user); + $cur_account = \Drupal::currentUser(); + + // Test here for a valid pre-auth -- if the pre-auth is set to the auth user, we + // handle things a bit differently. + + $validating_id = logintoboggan_validating_id(); + + $pre_auth = !\Drupal::config('user.settings')->get('verify_mail') + && $validating_id != DRUPAL_AUTHENTICATED_RID; + + // No time out for first time login. + // This conditional checks that: + // - the user is still in the pre-auth role or didn't set + // their own password. + // - the hashed password is correct. + + if (((\Drupal::config('user.settings')->get('verify_mail') + && !$account->getLastLoginTime()) || ($pre_auth && $account->hasRole($validating_id))) + && $hashed_pass == logintoboggan_eml_rehash($account->getPassword(), $timestamp, $account->getEmail())) { + + watchdog('user', 'E-mail validation URL used for %name with timestamp @timestamp.', array( + '%name' => $account->getUsername(), + '@timestamp' => $timestamp)); + + _logintoboggan_process_validation($account); + + // Where do we redirect after confirming the account? + $redirect = _logintoboggan_process_redirect(\Drupal::config('logintoboggan.settings')->get('redirect_on_confirm'), $account); + + switch ($operation) { + // Proceed with normal user login, as long as it's open registration and their + // account hasn't been blocked. + case 'login': + // Only show the validated message if there's a valid pre-auth role. + if ($pre_auth) { + drupal_set_message(t('You have successfully validated your e-mail address.')); + } + if ($account->isBlocked()) { + drupal_set_message(t('Your account is currently blocked -- login cancelled.'), 'error'); + return new RedirectResponse(\Drupal::url('')); + } + else { + $edit = array(); + $redirect = logintoboggan_process_login($account, $redirect); + return new RedirectResponse($redirect); + } + break; + // Admin validation. + case 'admin': + if ($pre_auth) { + // Mail the user, letting them know their account now has auth user perms. + _user_mail_notify('status_activated', $account); + } + + drupal_set_message(t('You have successfully validated %user.', array( + '%user' => $account->getUsername(), + ))); + + return new RedirectResponse(\Drupal::url('user.edit', array('user' => $user))); + break; + + // Catch all. + default: + drupal_set_message(t('You have successfully validated %user.', array( + '%user' => $account->getUsername(), + ))); + return new RedirectResponse(\Drupal::url('')); + break; + } + } + else { + $message = t("Sorry, you can only use your validation link once for security reasons."); + // No one currently logged in, go straight to user login page. + if ($cur_account->isAnonymous()) { + $message .= t(" Please log in with your username and password instead now."); + $goto = 'user.login'; + } + else { + $goto = 'user.page'; + } + drupal_set_message($message, 'error'); + return new RedirectResponse(\Drupal::url($goto)); + } + } + + /** + * This will return the output of the page. + */ + public function logintobogganResendValidation($user) { + $account = user_load($user); + + /**************************************************************************/ + $account->password = t('If required, you may reset your password from: !url', array( + '!url' => url('user/password', array('absolute' => TRUE)), + )); + /**************************************************************************/ + + _user_mail_notify('register_no_approval_required', $account); + + // Notify admin or user that e-mail was sent and return to user edit form. + if (user_access('administer users')) { + drupal_set_message(t("A validation e-mail has been sent to the user's e-mail address.")); + } + else { + drupal_set_message(t('A validation e-mail has been sent to your e-mail address. You will need to follow the instructions in that message in order to gain full access to the site.')); + } + + return new RedirectResponse(\Drupal::url('user.edit', array('user' => $user))); + } + + /** + * This will return the output of the page. + */ + public function logintobogganDenied() { + $account = \Drupal::currentUser(); + + if ($account->isAnonymous()) { + // Output the user login form. + $output = logintoboggan_get_authentication_form('login'); + drupal_set_page_content($output); + // Return page attributes, hide blocks. + $page = element_info('page'); + $page['#logintoboggan_denied'] = TRUE; + $page['#title'] = t('Access Denied / User log in'); + } + else { + $page = array( + '#title' => t('Access Denied'), + '#markup' => theme('lt_access_denied'), + ); + } + + return $page; + } +} diff --git a/lib/Drupal/logintoboggan/Form/LogintobogganSettingsForm.php b/lib/Drupal/logintoboggan/Form/LogintobogganSettingsForm.php new file mode 100644 index 0000000..4fb0f24 --- /dev/null +++ b/lib/Drupal/logintoboggan/Form/LogintobogganSettingsForm.php @@ -0,0 +1,243 @@ +moduleHandler = $module_handler; +// $this->roleStorage = $role_storage; +// } + + public static function create(ContainerInterface $container) { + return new static( + $container->get('config.factory'), + $container->get('config.context.free') + ); + } + + + /** + * {@inheritdoc} + */ + public function getFormId() { + return 'logintoboggan_main_settings'; + } + + /** + * Gets the roles to display in this form. + * + * @return \Drupal\user\RoleInterface[] + * An array of role objects. + */ + /** + * {@inheritdoc} + */ + public function buildForm(array $form, array &$form_state) { + $config = $this->configFactory->get('logintoboggan.settings'); + + $_disabled = $this->t('Disabled'); + $_enabled = $this->t('Enabled'); + $form['login'] = array( + '#type' => 'fieldset', + '#title' => $this->t('Log in'), + ); + $form['login']['login_with_email'] = array( + '#type' => 'radios', + '#title' => $this->t('Allow users to login using their e-mail address'), + '#default_value' => $config->get('login_with_email'), + '#options' => array($_disabled, $_enabled), + '#description' => $this->t('Users will be able to enter EITHER their username OR their e-mail address to log in.'), + ); + + $form['login']['unified_login'] = array( + '#type' => 'checkbox', + '#title' => $this->t('Present a unified login/registration page'), + '#default_value' => $config->get('unified_login'), + '#description' => $this->t("Use one page for both login and registration instead of Drupal's tabbed login/registration/password pages."), + ); + + $form['registration'] = array( + '#type' => 'fieldset', + '#title' => $this->t('Registration'), + ); + + $form['registration']['confirm_email_at_registration'] = array( + '#type' => 'radios', + '#title' => $this->t('Use two e-mail fields on registration form'), + '#default_value' => $config->get('confirm_email_at_registration'), + '#options' => array($_disabled, $_enabled), + '#description' => $this->t('User will have to type the same e-mail address into both fields. This helps to confirm that they\'ve typed the correct address.'), + ); + + if (module_exists('help')) { + $help_text = $this->t(" More help in writing the e-mail message can be found at LoginToboggan help.", array('!help' => url('admin/help/logintoboggan'))); + } + else { + $help_text = ''; + } + $form['registration']['user_email_verification'] = array( + '#type' => 'checkbox', + '#title' => t('Set password'), + '#default_value' => !$this->configFactory->get('user.settings')->get('verify_mail'), + '#description' => $this->t("This will allow users to choose their initial password when registering (note that this setting is a mirror of the Require e-mail verification when a visitor creates an account setting, and is merely here for convenience). If selected, users will be assigned to the role below. They will not be assigned to the 'authenticated user' role until they confirm their e-mail address by following the link in their registration e-mail. It is HIGHLY recommended that you set up a 'pre-authorized' role with limited permissions for this purpose.
NOTE: If you enable this feature, you should edit the Welcome (no approval required) text.", array('!settings' => url('admin/config/people/accounts'))) . $help_text, + ); + + // Grab the roles that can be used for pre-auth. Remove the anon role, as it's not a valid choice. + $roles = user_role_names(); + $form ['registration']['pre_auth_role'] = array( + '#type' => 'select', + '#title' => $this->t('Non-authenticated role'), + '#options' => $roles, + '#default_value' => $config->get('pre_auth_role'), + '#description' => $this->t('If "Set password" is selected, users will be able to login before their e-mail address has been authenticated. Therefore, you must choose a role for new non-authenticated users -- you may wish to add a new role for this purpose. Users will be removed from this role and assigned to the "authenticated user" role once they follow the link in their welcome e-mail. WARNING: changing this setting after initial site setup can cause undesirable results, including unintended deletion of users -- change with extreme caution!', array('!url' => url('admin/people/permissions/roles'))), + ); + + $purge_options = array( + 0 => $this->t('Never delete'), + 86400 => $this->t('1 Day'), + 172800 => $this->t('2 Days'), + 259200 => $this->t('3 Days'), + 345600 => $this->t('4 Days'), + 432000 => $this->t('5 Days'), + 518400 => $this->t('6 Days'), + 604800 => $this->t('1 Week'), + 1209600 => $this->t('2 Weeks'), + 2592000 => $this->t('1 Month'), + 7776000 => $this->t('3 Months'), + 15379200 => $this->t('6 Months'), + 30758400 => $this->t('1 Year'), + ); + + $form['registration']['purge_unvalidated_user_interval'] = array( + '#type' => 'select', + '#title' => $this->t('Delete unvalidated users after'), + '#options' => $purge_options, + '#default_value' => $config->get('purge_unvalidated_user_interval'), + '#description' => $this->t("If enabled, users that are still in the 'Non-authenticated role' set above will be deleted automatically from the system, if the set time interval since their initial account creation has passed. This can be used to automatically purge spambot registrations. Note: this requires cron, and also requires that the 'Set password' option above is enabled. WARNING: changing this setting after initial site setup can cause undesirable results, including unintended deletion of users -- change with extreme caution! (please read the CAVEATS section of INSTALL.txt for important information on configuring this feature)") + ); + + $form['registration']['immediate_login_on_register'] = array( + '#type' => 'checkbox', + '#title' => $this->t('Immediate login'), + '#default_value' => $config->get('immediate_login_on_register'), + '#description' => $this->t("If set, the user will be logged in immediately after registering. Note this only applies if the 'Set password' option above is enabled."), + ); + + $form['registration']['redirect'] = array( + '#type' => 'details', + '#title' => $this->t('Redirections'), + '#collapsed' => FALSE, + ); + + $form['registration']['redirect']['redirect_on_register'] = array( + '#type' => 'textfield', + '#title' => $this->t('Redirect path on registration'), + '#default_value' => $config->get('redirect_on_register'), + '#description' => $this->t('Normally, after a user registers a new account, they will be taken to the front page, or to their user page if you specify Immediate login above. Leave this setting blank if you wish to keep the default behavior. If you wish the user to go to a page of your choosing, then enter the path for it here. For instance, you may redirect them to a static page such as node/35, or to the <front> page. You may also use %uid as a variable, and the user\'s user ID will be substituted in the path.'), + ); + + $form['registration']['redirect']['redirect_on_confirm'] = array( + '#type' => 'textfield', + '#title' => $this->t('Redirect path on confirmation'), + '#default_value' => $config->get('redirect_on_confirm'), + '#description' => $this->t('Normally, after a user confirms their new account, they will be taken to their user page. Leave this setting blank if you wish to keep the default behavior. If you wish the user to go to a page of your choosing, then enter the path for it here. For instance, you may redirect them to a static page such as node/35, or to the <front> page. You may also use %uid as a variable, and the user\'s user ID will be substituted in the path. In the case where users are not creating their own passwords, it is suggested to use user/%uid/edit here, so the user may set their password immediately after validating their account.'), + ); + $form['registration']['redirect']['override_destination_parameter'] = array( + '#type' => 'checkbox', + '#title' => $this->t('Override destination parameter'), + '#default_value' => $config->get('override_destination_parameter'), + '#description' => $this->t("Normally, when a Drupal redirect is performed, priority is given to the 'destination' parameter from the originating URL. With this setting enabled, LoginToboggan will attempt to override this behavior with any values set above."), + ); + + $form['other'] = array( + '#type' => 'fieldset', + '#title' => $this->t('Other'), + '#tree' => FALSE, + ); + + $site403 = $config->get('site_403'); + if ($site403 == '') { + $disabled = $default = '0'; + } + elseif ($site403 == 'toboggan/denied') { + $disabled = '0'; + $default = 'toboggan/denied'; + } + else { + $disabled = $default = $site403; + } + $options = array($disabled => $_disabled, 'toboggan/denied' => $_enabled); + + $form['other']['site_403'] = array( + '#type' => 'radios', + '#title' => $this->t('Present login form on access denied (403)'), + '#options' => $options, + '#default_value' => $default, + '#description' => $this->t('Anonymous users will be presented with a login form along with an access denied message.') + ); + $form['other']['login_successful_message'] = array( + '#type' => 'radios', + '#title' => $this->t('Display login successful message'), + '#options' => array($_disabled, $_enabled), + '#default_value' => $config->get('login_successful_message'), + '#description' => $this->t('If enabled, users will receive a \'Log in successful\' message upon login.') + ); + $min_pass_options = array($this->t('None')); + for ($i = 2; $i < 30; $i++) { + $min_pass_options[$i] = $i; + } + $form['other']['minimum_password_length'] = array( + '#type' => 'select', + '#title' => $this->t('Minimum password length'), + '#options' => $min_pass_options, + '#default_value' => $config->get('minimum_password_length'), + '#description' => $this->t('LoginToboggan automatically performs basic password validation for illegal characters. If you would additionally like to have a minimum password length requirement, select the length here, or set to \'None\' for no password length validation.') + ); + return parent::buildForm($form, $form_state); + } + + /** + * {@inheritdoc} + */ + public function validateForm(array &$form, array &$form_state) { + parent::validateForm($form, $form_state); + + } + + /** + * {@inheritdoc} + */ + public function submitForm(array &$form, array &$form_state) { + $config = $this->configFactory->get('logintoboggan.settings'); + parent::submitForm($form, $form_state); + foreach ($form_state['values'] as $key => $value) { + if (!in_array($key, array('submit', 'form_build_id', 'form_token', 'form_id', 'op'))) { + if ($key == 'user_email_verification') { + $value = !$value; + } + $config ->set($key, $value); + } + } + $config->save(); + if ($form['login']['unified_login']['#default_value'] != $form['login']['unified_login']['#value']) { + drupal_set_message(t('Unified login setting was changed, menus have been rebuilt.')); + // For some reason, a regular menu_rebuild() still leaves the old callbacks + // cached -- doing it in a shutdown function seems to correct that issue. + drupal_register_shutdown_function('menu_rebuild'); + } + } +} diff --git a/logintoboggan.admin.inc b/logintoboggan.admin.inc deleted file mode 100644 index 0674a4c..0000000 --- a/logintoboggan.admin.inc +++ /dev/null @@ -1,211 +0,0 @@ - 'fieldset', - '#title' => t('Log in'), - ); - - $form['login']['logintoboggan_login_with_email'] = array( - '#type' => 'radios', - '#title' => t('Allow users to login using their e-mail address'), - '#default_value' => variable_get('logintoboggan_login_with_email', 0), - '#options' => array($_disabled, $_enabled), - '#description' => t('Users will be able to enter EITHER their username OR their e-mail address to log in.'), - ); - - $form['login']['logintoboggan_unified_login'] = array( - '#type' => 'checkbox', - '#title' => t('Present a unified login/registration page'), - '#default_value' => variable_get('logintoboggan_unified_login', 0), - '#description' => t("Use one page for both login and registration instead of Drupal's tabbed login/registration/password pages."), - ); - - $form['registration'] = array( - '#type' => 'fieldset', - '#title' => t('Registration'), - ); - - $form['registration']['logintoboggan_confirm_email_at_registration'] = array( - '#type' => 'radios', - '#title' => t('Use two e-mail fields on registration form'), - '#default_value' => variable_get('logintoboggan_confirm_email_at_registration', 0), - '#options' => array($_disabled, $_enabled), - '#description' => t('User will have to type the same e-mail address into both fields. This helps to confirm that they\'ve typed the correct address.'), - ); - - if (module_exists('help')) { - $help_text = t(" More help in writing the e-mail message can be found at LoginToboggan help.", array('!help' => url('admin/help/logintoboggan'))); - } - else { - $help_text = ''; - } - $form['registration']['logintoboggan_user_email_verification'] = array( - '#type' => 'checkbox', - '#title' => t('Set password'), - '#default_value' => !variable_get('user_email_verification', TRUE) ? 1 : 0, - '#description' => t("This will allow users to choose their initial password when registering (note that this setting is a mirror of the Require e-mail verification when a visitor creates an account setting, and is merely here for convenience). If selected, users will be assigned to the role below. They will not be assigned to the 'authenticated user' role until they confirm their e-mail address by following the link in their registration e-mail. It is HIGHLY recommended that you set up a 'pre-authorized' role with limited permissions for this purpose.
NOTE: If you enable this feature, you should edit the Welcome (no approval required) text.", array('!settings' => url('admin/config/people/accounts'))) . $help_text, - ); - - // Grab the roles that can be used for pre-auth. Remove the anon role, as it's not a valid choice. - $roles = user_roles(TRUE); - - $form ['registration']['logintoboggan_pre_auth_role'] = array( - '#type' => 'select', - '#title' => t('Non-authenticated role'), - '#options' => $roles, - '#default_value' => variable_get('logintoboggan_pre_auth_role', DRUPAL_AUTHENTICATED_RID), - '#description' => t('If "Set password" is selected, users will be able to login before their e-mail address has been authenticated. Therefore, you must choose a role for new non-authenticated users -- you may wish to add a new role for this purpose. Users will be removed from this role and assigned to the "authenticated user" role once they follow the link in their welcome e-mail. WARNING: changing this setting after initial site setup can cause undesirable results, including unintended deletion of users -- change with extreme caution!', array('!url' => url('admin/people/permissions/roles'))), - ); - - $purge_options = array( - 0 => t('Never delete'), - 86400 => t('1 Day'), - 172800 => t('2 Days'), - 259200 => t('3 Days'), - 345600 => t('4 Days'), - 432000 => t('5 Days'), - 518400 => t('6 Days'), - 604800 => t('1 Week'), - 1209600 => t('2 Weeks'), - 2592000 => t('1 Month'), - 7776000 => t('3 Months'), - 15379200 => t('6 Months'), - 30758400 => t('1 Year'), - ); - - $form['registration']['logintoboggan_purge_unvalidated_user_interval'] = array( - '#type' => 'select', - '#title' => t('Delete unvalidated users after'), - '#options' => $purge_options, - '#default_value' => variable_get('logintoboggan_purge_unvalidated_user_interval', 0), - '#description' => t("If enabled, users that are still in the 'Non-authenticated role' set above will be deleted automatically from the system, if the set time interval since their initial account creation has passed. This can be used to automatically purge spambot registrations. Note: this requires cron, and also requires that the 'Set password' option above is enabled. WARNING: changing this setting after initial site setup can cause undesirable results, including unintended deletion of users -- change with extreme caution! (please read the CAVEATS section of INSTALL.txt for important information on configuring this feature)") - ); - - $form['registration']['logintoboggan_immediate_login_on_register'] = array( - '#type' => 'checkbox', - '#title' => t('Immediate login'), - '#default_value' => variable_get('logintoboggan_immediate_login_on_register', TRUE), - '#description' => t("If set, the user will be logged in immediately after registering. Note this only applies if the 'Set password' option above is enabled."), - ); - - $form['registration']['redirect'] = array( - '#type' => 'fieldset', - '#title' => t('Redirections'), - '#collapsible' => true, - '#collapsed' => false, - ); - - $form['registration']['redirect']['logintoboggan_redirect_on_register'] = array( - '#type' => 'textfield', - '#title' => t('Redirect path on registration'), - '#default_value' => variable_get('logintoboggan_redirect_on_register', ''), - '#description' => t('Normally, after a user registers a new account, they will be taken to the front page, or to their user page if you specify Immediate login above. Leave this setting blank if you wish to keep the default behavior. If you wish the user to go to a page of your choosing, then enter the path for it here. For instance, you may redirect them to a static page such as node/35, or to the <front> page. You may also use %uid as a variable, and the user\'s user ID will be substituted in the path.'), - ); - - $form['registration']['redirect']['logintoboggan_redirect_on_confirm'] = array( - '#type' => 'textfield', - '#title' => t('Redirect path on confirmation'), - '#default_value' => variable_get('logintoboggan_redirect_on_confirm', ''), - '#description' => t('Normally, after a user confirms their new account, they will be taken to their user page. Leave this setting blank if you wish to keep the default behavior. If you wish the user to go to a page of your choosing, then enter the path for it here. For instance, you may redirect them to a static page such as node/35, or to the <front> page. You may also use %uid as a variable, and the user\'s user ID will be substituted in the path. In the case where users are not creating their own passwords, it is suggested to use user/%uid/edit here, so the user may set their password immediately after validating their account.'), - ); - $form['registration']['redirect']['logintoboggan_override_destination_parameter'] = array( - '#type' => 'checkbox', - '#title' => t('Override destination parameter'), - '#default_value' => variable_get('logintoboggan_override_destination_parameter', 1), - '#description' => t("Normally, when a Drupal redirect is performed, priority is given to the 'destination' parameter from the originating URL. With this setting enabled, LoginToboggan will attempt to override this behavior with any values set above."), - ); - - $form['other'] = array('#type' => 'fieldset', - '#title' => t('Other'), - '#tree' => FALSE, - ); - - $site403 = variable_get('site_403', ''); - if ($site403 == '') { - $disabled = $default = '0'; - } - elseif ($site403 == 'toboggan/denied') { - $disabled = '0'; - $default = 'toboggan/denied'; - } - else { - $disabled = $default = $site403; - } - $options = array($disabled => $_disabled, 'toboggan/denied' => $_enabled); - - $form['other']['logintoboggan_site_403'] = array( - '#type' => 'radios', - '#title' => t('Present login form on access denied (403)'), - '#options' => $options, - '#default_value' => $default, - '#description' => t('Anonymous users will be presented with a login form along with an access denied message.') - ); - $form['other']['logintoboggan_login_successful_message'] = array( - '#type' => 'radios', - '#title' => t('Display login successful message'), - '#options' => array($_disabled, $_enabled), - '#default_value' => variable_get('logintoboggan_login_successful_message', 0), - '#description' => t('If enabled, users will receive a \'Log in successful\' message upon login.') - ); - $min_pass_options = array(t('None')); - for ($i = 2; $i < 30; $i++) { - $min_pass_options[$i] = $i; - } - $form['other']['logintoboggan_minimum_password_length'] = array( - '#type' => 'select', - '#title' => t('Minimum password length'), - '#options' => $min_pass_options, - '#default_value' => variable_get('logintoboggan_minimum_password_length', 0), - '#description' => t('LoginToboggan automatically performs basic password validation for illegal characters. If you would additionally like to have a minimum password length requirement, select the length here, or set to \'None\' for no password length validation.') - ); - - return system_settings_form($form); -} - -/** - * Implement hook_form_logintoboggan_main_settings_alter(). - * - * @ingroup logintoboggan_core - */ -function logintoboggan_form_logintoboggan_main_settings_alter(&$form, &$form_state) { - // Ensure a valid submit array. - $form['#submit'] = is_array($form['#submit']) ? $form['#submit'] : array(); - // Make this submit handler run first. - array_unshift($form['#submit'], 'logintoboggan_main_settings_submit'); -} - -/** - * Takes custom settings on the main settings page, transforms them into the - * appropriate core setting, and cleans up the custom values. - */ -function logintoboggan_main_settings_submit($form, &$form_state) { - // User email verification setting. - $user_email_verification = $form_state['values']['logintoboggan_user_email_verification'] ? FALSE : TRUE; - variable_set('user_email_verification', $user_email_verification); - unset($form_state['values']['logintoboggan_user_email_verification']); - - // Site 403 setting. - $site_403 = $form_state['values']['logintoboggan_site_403'] == '0' ? '' : $form_state['values']['logintoboggan_site_403']; - variable_set('site_403', $site_403); - unset($form_state['values']['logintoboggan_site_403']); - - // If the unified login setting has been changed, we need to rebuild the - // menu cache. - if ($form['login']['logintoboggan_unified_login']['#default_value'] != $form['login']['logintoboggan_unified_login']['#value']) { - drupal_set_message(t('Unified login setting was changed, menus have been rebuilt.')); - // For some reason, a regular menu_rebuild() still leaves the old callbacks - // cached -- doing it in a shutdown function seems to correct that issue. - drupal_register_shutdown_function('menu_rebuild'); - } -} - diff --git a/logintoboggan.info b/logintoboggan.info deleted file mode 100644 index 701b3b6..0000000 --- a/logintoboggan.info +++ /dev/null @@ -1,8 +0,0 @@ - -name = "LoginToboggan" -description = "Improves Drupal's login system." -core = "7.x" -configure = admin/config/system/logintoboggan - -stylesheets[all][] = logintoboggan.css - diff --git a/logintoboggan.info.yml b/logintoboggan.info.yml new file mode 100644 index 0000000..1238b4b --- /dev/null +++ b/logintoboggan.info.yml @@ -0,0 +1,6 @@ +name: LoginToboggan +type: module +description: "Improves Drupal's login system." +version: VERSION +core: 8.x +configure: logintoboggan.config diff --git a/logintoboggan.module b/logintoboggan.module index 3e47dee..ca6f762 100755 --- a/logintoboggan.module +++ b/logintoboggan.module @@ -559,12 +559,6 @@ function logintoboggan_user_login_validate($form, &$form_state) { * @ingroup logintoboggan_form */ function logintoboggan_user_register_validate($form, &$form_state) { - //Check to see whether our username matches any email address currently in the system. - if($mail = db_query("SELECT mail FROM {users} WHERE LOWER(:name) = LOWER(mail)", array( - ':name' => $form_state['values']['name'], - ))->fetchField()) { - form_set_error('name', t('This e-mail has already been taken by another user.')); - } //Check to see whether our e-mail address matches the confirm address if enabled. if (variable_get('logintoboggan_confirm_email_at_registration', 0) && isset($form_state['values']['conf_mail'])) { if ($form_state['values']['mail'] != $form_state['values']['conf_mail']) { @@ -629,10 +623,10 @@ function logintoboggan_user_edit_validate($form, &$form_state) { * access checks have already been performed. */ function logintoboggan_menu_get_item_alter() { - global $user; + $account = \Drupal::currentUser(); // Make sure any user with pre-auth role doesn't have authenticated user role - _logintoboggan_user_roles_alter($user); + _logintoboggan_user_roles_alter($account); } /** @@ -649,14 +643,13 @@ function logintoboggan_menu_get_item_alter() { */ function _logintoboggan_user_roles_alter($account) { $id = logintoboggan_validating_id(); - $in_pre_auth_role = in_array($id, array_keys($account->roles)); - if ($account->uid && $in_pre_auth_role) { - if ($id != DRUPAL_AUTHENTICATED_RID) { - unset($account->roles[DRUPAL_AUTHENTICATED_RID]); - // Reset the permissions cache. - drupal_static_reset('user_access'); - } - } + // if (!$account->isAnonymous() && $account->hasRole($id)) { + // if ($id != DRUPAL_AUTHENTICATED_RID) { + // // unset($account->roles[DRUPAL_AUTHENTICATED_RID]); + // // // Reset the permissions cache. + // // drupal_static_reset('user_access'); + // } + // } } /** @@ -668,44 +661,28 @@ function logintoboggan_menu() { $items = array(); // Settings page. - $items['admin/config/system/logintoboggan'] = array( - 'title' => 'LoginToboggan', + $items['admin/config/system/logintoboggan'] = array( + 'title' => 'LoginToboggan', 'description' => 'Set up custom login options like instant login, login redirects, pre-authorized validation roles, etc.', - 'page callback' => 'drupal_get_form', - 'page arguments' => array('logintoboggan_main_settings'), - 'access callback' => 'user_access', - 'access arguments' => array('administer site configuration'), - 'file' => 'logintoboggan.admin.inc', + 'route_name' => 'logintobogganform.settings', ); // Callback for user validate routine. - $items['user/validate/%user/%/%'] = array( - 'title' => 'Validate e-mail address', - 'page callback' => 'logintoboggan_validate_email', - 'page arguments' => array(2, 3, 4), - 'access callback' => 'logintoboggan_validate_email_access', - 'access arguments' => array(2, 3), - 'type' => MENU_CALLBACK, - 'file' => 'logintoboggan.validation.inc', + $items['user/validate/%user/%/%/%'] = array( + 'title' => 'Validate e-mail address', + 'route_name' => 'logintoboggan.user_validate', ); - // Callback for re-sending validation e-mail + // Callback for re-sending validation e-mail. $items['toboggan/revalidate/%user'] = array( - 'title' => 'Re-send validation e-mail', - 'page callback' => 'logintoboggan_resend_validation', - 'page arguments' => array(2), - 'access callback' => 'logintoboggan_revalidate_access', - 'access arguments' => array(2), - 'type' => MENU_CALLBACK, - 'file' => 'logintoboggan.validation.inc', + 'title' => 'Re-send validation e-mail', + 'route_name' => 'logintoboggan.user_revalidate', ); // Callback for handling access denied redirection. $items['toboggan/denied'] = array( - 'access callback' => TRUE, - 'page callback' => 'logintoboggan_denied', - 'title' => 'Access denied', - 'type' => MENU_CALLBACK, + 'title' => 'Access denied', + 'route_name' => 'logintoboggan.denied', ); return $items; @@ -715,7 +692,7 @@ function logintoboggan_menu() { * Implementation of hook_menu_alter(). */ function logintoboggan_menu_alter(&$callbacks) { - if (variable_get('logintoboggan_unified_login', 0)) { + if (\Drupal::config('logintoboggan.settings')->get('unified_login')) { // Kill the tabs on the login pages. $callbacks['user/login']['type'] = MENU_NORMAL_ITEM; $callbacks['user/login']['page callback'] = 'logintoboggan_unified_login_page'; @@ -728,41 +705,6 @@ function logintoboggan_menu_alter(&$callbacks) { } /** - * Access check for user e-mail validation. - */ -function logintoboggan_validate_email_access($account, $timestamp) { - return $account->uid && $timestamp < REQUEST_TIME; -} - -/** - * Access check for user revalidation. - */ -function logintoboggan_revalidate_access($account) { - return $GLOBALS['user']->uid && ($GLOBALS['user']->uid == $account->uid || user_access('administer users')); -} - -/** - * Menu callback for user/login - * creates a unified login/registration form (without tabs) - * - * @param $active_form - * Which form to display, should be 'login' or 'register'. - */ -function logintoboggan_unified_login_page($active_form = 'login') { - global $user; - if ($user->uid) { - menu_set_active_item('user/' . $user->uid); - return menu_execute_active_handler(NULL, FALSE); - } - else { - // Title just clutters the interface... - drupal_set_title(''); - $output = logintoboggan_get_authentication_form($active_form); - return $output; - } -} - -/** * Implemenation of hook_theme(). * * @ingroup logintoboggan_core @@ -888,7 +830,7 @@ function _logintoboggan_toggleboggan ($form) { * Which form to display, should be 'login' or 'register'. */ function logintoboggan_unified_login_form($active_form = 'login') { - $login_form = drupal_get_form('user_login'); + $login_form = \Drupal::formBuilder()->getForm('Drupal\user\Form\UserLoginForm'); $login_form['#attached']['js'][] = drupal_get_path('module', 'logintoboggan') .'/logintoboggan.unifiedlogin.js'; $login_form['#attached']['js'][] = array( 'data' => array( @@ -898,7 +840,7 @@ function logintoboggan_unified_login_form($active_form = 'login') { ), 'type' => 'setting', ); - $register_form = drupal_get_form('user_register_form'); + $register_form = \Drupal::entityManager()->getForm(entity_create('user', array()), 'register'); $rendered_login_form = drupal_render($login_form); $rendered_register_form = drupal_render($register_form); $variables = array( @@ -919,35 +861,18 @@ function logintoboggan_unified_login_form($active_form = 'login') { */ function logintoboggan_get_authentication_form($active_form = 'login') { $output = ''; - if (variable_get('logintoboggan_unified_login', 0)) { + if (\Drupal::config('logintoboggan.settings')->get('unified_login')) { $output = logintoboggan_unified_login_form($active_form); } elseif ($active_form == 'login') { - $output = drupal_get_form('user_login'); + $output = \Drupal::formBuilder()->getForm('Drupal\user\Form\UserLoginForm'); } elseif ($active_form == 'register') { - $output = drupal_get_form('user_register_form'); + $output = \Drupal::entityManager()->getForm(entity_create('user', array()), 'register'); } return $output; } -function logintoboggan_denied() { - if ($GLOBALS['user']->uid == 0) { - drupal_set_title(t('Access Denied / User log in')); - // Output the user login form. - $output = logintoboggan_get_authentication_form('login'); - drupal_set_page_content($output); - // Return page attributes, hide blocks. - $page = element_info('page'); - $page['#logintoboggan_denied'] = TRUE; - } - else { - drupal_set_title(t('Access Denied')); - $page = theme('lt_access_denied'); - } - return $page; -} - /** * Modified version of user_validate_name * - validates user submitted passwords have a certain length and only contain letters, numbers or punctuation (graph character class in regex) @@ -966,8 +891,12 @@ function logintoboggan_validate_pass($pass) { $pass)) { return t('The password contains an illegal character.'); } - $min_pass_length = variable_get('logintoboggan_minimum_password_length', 0); - if ($min_pass_length && strlen($pass) < $min_pass_length) return t("The password is too short: it must be at least %min_length characters.", array('%min_length' => $min_pass_length)); + $min_pass_length = \Drupal::config('user.settings')->get('minimum_password_length'); + if ($min_pass_length && strlen($pass) < $min_pass_length) { + return t("The password is too short: it must be at least %min_length characters.", array( + '%min_length' => $min_pass_length, + )); + } } @@ -976,34 +905,33 @@ function logintoboggan_validate_pass($pass) { * - gets the role id for the "validating" user role. */ function logintoboggan_validating_id() { - return variable_get('logintoboggan_pre_auth_role', DRUPAL_AUTHENTICATED_RID); + return \Drupal::config('logintoboggan.settings')->get('pre_auth_role'); } function _logintoboggan_process_validation($account) { // Test here for a valid pre-auth -- if the pre-auth is set to the auth user, we // handle things a bit differently. $validating_id = logintoboggan_validating_id(); - $pre_auth = !variable_get('user_email_verification', TRUE) && $validating_id != DRUPAL_AUTHENTICATED_RID; - + $pre_auth = !\Drupal::config('user.settings')->get('verify_mail') && $validating_id != DRUPAL_AUTHENTICATED_RID; // Remove the pre-auth role from the user, unless they haven't been approved yet. - if ($account->status) { + if (!$account->isBlocked()) { if ($pre_auth) { db_delete('users_roles') - ->condition('uid', $account->uid) + ->condition('uid', $account->id()) ->condition('rid', $validating_id) ->execute(); } } // Reload the user object freshly, since the cached value may have stale // roles, and to prepare for the possible user_save() below. - $account = user_load($account->uid, TRUE); + $account = user_load($account->id(), TRUE); // Allow other modules to react to email validation by invoking the user update hook. // This should only be triggered if LT's custom validation is active. - if (!variable_get('user_email_verification', TRUE)) { - $edit = array(); - $account->logintoboggan_email_validated = TRUE; - user_module_invoke('update', $edit, $account); + if (!\Drupal::config('user.settings')->get('verify_mail')) { + // $edit = array(); + // $account->logintoboggan_email_validated = TRUE; + // user_module_invoke('update', $edit, $account); } } @@ -1022,35 +950,25 @@ function _logintoboggan_process_validation($account) { * 'fragment' => NULL */ -function logintoboggan_process_login($account, &$edit, $redirect = array()){ - global $user; - - $user = user_load($account->uid); - user_login_finalize($edit); +function logintoboggan_process_login($account, $redirect = FALSE) { + user_login_finalize($account); // In the special case where a user is validating but they did not create their // own password, show a user message letting them know to change their password. - if (variable_get('user_email_verification', TRUE)) { - watchdog('user', 'User %name used one-time login link at time %timestamp.', array('%name' => $user->name, '%timestamp' => REQUEST_TIME)); + if (\Drupal::config('user.settings')->get('verify_mail')) { + watchdog('user', 'User %name used one-time login link at time %timestamp.', array('%name' => $account->getUsername(), '%timestamp' => REQUEST_TIME)); drupal_set_message(t('You have just used your one-time login link. It is no longer possible to use this link to login. Please change your password.')); } - if (isset($redirect[0]) && $redirect[0] != '') { + if (!empty($redirect)) { return $redirect; } - return array( - 'user/'. $user->uid, - array( - 'query' => array(), - 'fragment' => '', - ), - ); + return \Drupal::url('user.edit', array('user' => $account->id())); } function logintoboggan_eml_validate_url($account, $url_options){ - $timestamp = REQUEST_TIME; - return url("user/validate/$account->uid/$timestamp/". logintoboggan_eml_rehash($account->pass, $timestamp, $account->mail), $url_options); + return url('user/validate/' . $account->id() . '/' . REQUEST_TIME . '/' . logintoboggan_eml_rehash($account->getPassword(), REQUEST_TIME, $account->getEmail()), $url_options); } function logintoboggan_eml_rehash($password, $timestamp, $mail) { @@ -1061,8 +979,8 @@ function logintoboggan_eml_rehash($password, $timestamp, $mail) { * Implement hook_user_login(). */ function logintoboggan_user_login(&$edit, $account) { - if (variable_get('logintoboggan_login_successful_message', 0)) { - drupal_set_message(theme('lt_login_successful_message', array('account' => $account))); + if (\Drupal::config('logintoboggan.settings')->get('redirect_on_confirm')) { + drupal_set_message(theme('login_successful_message', array('account' => $account))); } } @@ -1079,7 +997,7 @@ function logintoboggan_user_load($users) { /** * Implement hook_user_update(). */ -function logintoboggan_user_update(&$edit, $account, $category) { +function logintoboggan_user_update($account) { // Only perform this check if an admin is editing the account. if (user_access('administer users') && isset($edit['roles'])) { // Check to see if roles present, and the pre-auth role was present when @@ -1114,11 +1032,11 @@ function _logintoboggan_protocol() { * The user account object associated with the redirect. */ function _logintoboggan_process_redirect($redirect, $account) { - $variables = array('%uid' => $account->uid); + $variables = array('%uid' => $account->id()); $redirect = parse_url(urldecode(strtr($redirect, $variables))); // If there's a path set, override the destination parameter if necessary. - if ($redirect['path'] && variable_get('logintoboggan_override_destination_parameter', 1)) { + if ($redirect['path'] && \Drupal::config('logintoboggan.settings')->get('override_destination_parameter')) { unset($_GET['destination']); } @@ -1126,13 +1044,7 @@ function _logintoboggan_process_redirect($redirect, $account) { $query = isset($redirect['query']) ? $redirect['query'] : array(); $fragment = isset($redirect['fragment']) ? $redirect['fragment'] : ''; - return array( - $redirect['path'], - array( - 'query' => $query, - 'fragment' => $fragment, - ), - ); + return url($redirect['path'], array('query' => $query, 'fragment' => $fragment)); } /** @@ -1140,7 +1052,7 @@ function _logintoboggan_process_redirect($redirect, $account) { */ function logintoboggan_mail_alter(&$message) { if ($message['id'] == 'user_register_pending_approval_admin') { - $reg_pass_set = !variable_get('user_email_verification', TRUE); + $reg_pass_set = !\Drupal::config('user.settings')->get('verify_mail'); if ($reg_pass_set) { $account = $message['params']['account']; $url_options = array('absolute' => TRUE); @@ -1242,7 +1154,7 @@ function theme_lt_loggedinblock($variables){ */ function theme_lt_login_link($variables) { // Only display register text if registration is allowed. - if (variable_get('user_register', USER_REGISTER_VISITORS_ADMINISTRATIVE_APPROVAL)) { + if (\Drupal::config('user.settings')->get('admin_role')) { return t('Log in/Register'); } else { @@ -1291,4 +1203,3 @@ function theme_lt_unified_login_page($variables) { return $output; } - diff --git a/logintoboggan.routing.yml b/logintoboggan.routing.yml new file mode 100644 index 0000000..8d03c81 --- /dev/null +++ b/logintoboggan.routing.yml @@ -0,0 +1,31 @@ +logintoboggan.user_validate: + path: '/user/validate/{user}/{timestamp}/{hashed_pass}/{operation}' + defaults: + _title: 'Validate e-mail address' + _content: '\Drupal\logintoboggan\Controller\LogintobogganController::logintobogganValidateEmail' + operation: 'login' + requirements: + _logintoboggan_validate_email_access: 'TRUE' + +logintoboggan.user_revalidate: + path: '/toboggan/revalidate/{user}' + defaults: + _title: 'Re-send validation e-mail' + _content: '\Drupal\logintoboggan\Controller\LogintobogganController::logintobogganResendValidation' + requirements: + _logintoboggan_revalidate_access: 'TRUE' + +logintoboggan.denied: + path: '/toboggan/denied' + defaults: + _title: 'Access denied' + _content: '\Drupal\logintoboggan\Controller\LogintobogganController::logintobogganDenied' + requirements: + _access: 'TRUE' + +logintobogganform.settings: + path: '/admin/config/system/logintoboggan' + defaults: + _form: '\Drupal\logintoboggan\Form\LogintobogganSettingsForm' + requirements: + _permission: 'administer site configuration' diff --git a/logintoboggan.services.yml b/logintoboggan.services.yml new file mode 100644 index 0000000..077410a --- /dev/null +++ b/logintoboggan.services.yml @@ -0,0 +1,9 @@ +services: + access_check.logintoboggan.validate_access: + class: Drupal\logintoboggan\Access\LogintobogganValidateAccess + tags: + - { name: access_check } + access_check.logintoboggan.revalidate_access: + class: Drupal\logintoboggan\Access\LogintobogganReValidateAccess + tags: + - { name: access_check }