You are here

user_registrationpassword.module in User registration password 8

Same filename and directory in other branches
  1. 6 user_registrationpassword.module
  2. 7 user_registrationpassword.module

Enables password creation on registration form.

File

user_registrationpassword.module
View source
<?php

/**
 * @file
 * Enables password creation on registration form.
 */
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Messenger\MessengerInterface;
use Drupal\user\Form\UserPasswordForm;
use Drupal\Core\Session\AccountInterface;
use Drupal\user\UserInterface;
use Drupal\Component\Render\PlainTextOutput;
use Drupal\user_registrationpassword\UserRegistrationPassword;

/**
 * No verification email is sent.
 *
 * @deprecated in 1.0-beta1 and will be removed before 1.0.
 *   Use \Drupal\user_registrationpassword\UserRegistrationPassword::NO_VERIFICATION
 *   instead.
 */
define('USER_REGISTRATIONPASSWORD_NO_VERIFICATION', 'none');

/**
 * Verification email is sent before password is set.
 *
 * @deprecated in 1.0-beta1 and will be removed before 1.0.
 *   Use \Drupal\user_registrationpassword\UserRegistrationPassword::VERIFICATION_DEFAULT
 *   instead.
 */
define('USER_REGISTRATIONPASSWORD_VERIFICATION_DEFAULT', 'default');

/**
 * Verification email is sent after password is set.
 *
 * @deprecated in 1.0-beta1 and will be removed before 1.0.
 *   Use \Drupal\user_registrationpassword\UserRegistrationPassword::VERIFICATION_PASS
 *   instead.
 */
define('USER_REGISTRATIONPASSWORD_VERIFICATION_PASS', 'with-pass');

/**
 * Implements hook_form_FORM_ID_alter().
 */
function user_registrationpassword_form_user_admin_settings_alter(&$form, FormStateInterface $form_state, $form_id) {
  $config = \Drupal::config('user_registrationpassword.settings');
  $mail_config = \Drupal::config('user_registrationpassword.mail');

  // Render our configuration options.
  $form['registration_cancellation']['user_registrationpassword_registration'] = [
    '#type' => 'radios',
    '#title' => t('Require email verification when a visitor creates an account'),
    '#description' => t('Choose whether new users can set their password directly on the registration form and login during registration (without email confirmation), or if they will be required to validate their email address prior to logging into the site, and will be assigned a system-generated password, or they can set a password during registration, but first have to confirm their account via the activation email sent after registration is complete, before they can log in to the site.'),
    '#options' => [
      UserRegistrationPassword::NO_VERIFICATION => t('Do not require a verification email, and let users set their password on the registration form.'),
      UserRegistrationPassword::VERIFICATION_DEFAULT => t('Require a verification email, but wait for the approval email to let users set their password.'),
      UserRegistrationPassword::VERIFICATION_PASS => t('Require a verification email, but let users set their password directly on the registration form.'),
    ],
    '#default_value' => $config
      ->get('registration'),
  ];

  // Render an option to change first time login link behaviour.
  $form['registration_cancellation']['user_registrationpassword_registration_ftll_expire'] = [
    '#type' => 'checkbox',
    '#title' => t('Enable Account activation link expiration'),
    '#description' => t('This option enables site admins to expire activation links after the expiration time has passed. People are still able to request a new activation email via the password reset form after this time expires to activate their account via a new activation email.'),
    '#default_value' => $config
      ->get('registration_ftll_expire'),
    '#weight' => 7,
  ];

  // Hide this setting by default.
  $form['registration_cancellation']['ftll_timeout'] = [
    '#type' => 'container',
    '#weight' => 8,
    '#states' => [
      'invisible' => [
        'input[name="user_registrationpassword_registration_ftll_expire"]' => [
          'checked' => FALSE,
        ],
      ],
    ],
  ];
  $form['registration_cancellation']['ftll_timeout']['user_registrationpassword_registration_ftll_timeout'] = [
    '#type' => 'textfield',
    '#title' => t('Account activation link expiration'),
    '#description' => t('Enter the expiration time for the activation link (aka first time login link) sent in the activation email. The default is set to 86400 seconds (= 24 hours).'),
    '#default_value' => $config
      ->get('registration_ftll_timeout'),
    '#maxlength' => 10,
    '#weight' => 8,
  ];

  // Hide the default option.
  $form['registration_cancellation']['user_email_verification']['#access'] = FALSE;

  // Set up available tokens.
  if (\Drupal::moduleHandler()
    ->moduleExists('rpt')) {

    // Hide the option to generate passwords, because we
    // only use the token option the rtp module provides.
    $form['registration_cancellation']['rpt_password_generate']['#access'] = FALSE;

    // We support the Registration Password Tokens module.
    // Add a password to a template with [user:password].
    // See http://drupal.org/project/rpt for more information.
    $email_token_help = t('Available variables are: [site:name], [site:url], [user:display-name], [user:account-name], [user:mail], [user:password], [site:login-url], [site:url-brief], [user:edit-url], [user:one-time-login-url], [user:cancel-url], [user:registrationpassword-url].');
  }
  else {
    $email_token_help = t('Available variables are: [site:name], [site:url], [user:display-name], [user:account-name], [user:mail], [site:login-url], [site:url-brief], [user:edit-url], [user:one-time-login-url], [user:cancel-url], [user:registrationpassword-url].');
  }

  // Render email template settings.
  $form['email_user_registrationpassword'] = [
    '#type' => 'details',
    '#title' => t('Welcome (no approval required, password is set)'),
    '#collapsible' => TRUE,
    '#collapsed' => TRUE,
    '#description' => t('Edit the welcome email messages sent to new members upon registering, when no administrator approval is required and password has already been set.') . ' ' . $email_token_help,
    '#group' => 'email',
  ];
  $form['email_user_registrationpassword']['user_registrationpassword_register_confirmation_with_pass_subject'] = [
    '#type' => 'textfield',
    '#title' => t('Subject'),
    '#default_value' => $mail_config
      ->get('register_confirmation_with_pass.subject'),
    '#maxlength' => 180,
  ];
  $form['email_user_registrationpassword']['user_registrationpassword_register_confirmation_with_pass_body'] = [
    '#type' => 'textarea',
    '#title' => t('Body'),
    '#default_value' => $mail_config
      ->get('register_confirmation_with_pass.body'),
    '#rows' => 15,
  ];

  // Reorder the form items back to their original order.
  $form['anonymous_settings']['#weight'] = -2;
  $form['admin_role']['#weight'] = -1;
  $form['registration_cancellation']['#weight'] = 0;
  $form['registration_cancellation']['user_cancel_method']['#weight'] = 3;
  $form['registration_cancellation']['user_registrationpassword_registration']['#weight'] = 2;
  $form['personalization']['#weight'] = 4;
  $form['email_title']['#weight'] = 5;
  $form['email']['#weight'] = 6;
  $form['email_admin_created']['#weight'] = 6;

  // Register our extra submit function.
  $form['#submit'][] = 'user_registrationpassword_admin_settings_submit';
}

/**
 * Submit handler for the user admin form.
 */
function user_registrationpassword_admin_settings_submit(&$form, FormStateInterface $form_state) {
  $config = \Drupal::configFactory()
    ->getEditable('user_registrationpassword.settings');
  $mail_config = \Drupal::configFactory()
    ->getEditable('user_registrationpassword.mail');
  $user_config = \Drupal::configFactory()
    ->getEditable('user.settings');

  // The option we implement.
  $user_registrationpassword_type = $form_state
    ->getValue('user_registrationpassword_registration');
  $config
    ->set('registration', $user_registrationpassword_type);

  // Core option.
  $user_register = $form_state
    ->getValue('user_register');

  // Test if we are enabled. For this, both options have to be set correctly
  // because then and only then we can function.
  // @see _user_mail_notify()
  if ($user_registrationpassword_type == UserRegistrationPassword::VERIFICATION_PASS && $user_register == UserInterface::REGISTER_VISITORS) {
    $user_config
      ->set('verify_mail', 0)
      ->set('notify.register_pending_approval', 0)
      ->set('notify.register_no_approval_required', 0);
  }
  else {

    // Our option.
    switch ($user_registrationpassword_type) {
      case UserRegistrationPassword::NO_VERIFICATION:
        $user_config
          ->set('verify_mail', 0);
        break;
      case UserRegistrationPassword::VERIFICATION_DEFAULT:
      case UserRegistrationPassword::VERIFICATION_PASS:
        $user_config
          ->set('verify_mail', 1);
        break;
    }

    // Core option.
    switch ($user_register) {
      case UserInterface::REGISTER_ADMINISTRATORS_ONLY:
        $user_config
          ->set('notify.register_pending_approval', 0)
          ->set('notify.register_no_approval_required', 0);
        break;
      case UserInterface::REGISTER_VISITORS:
      case UserInterface::REGISTER_VISITORS_ADMINISTRATIVE_APPROVAL:
        $user_config
          ->set('notify.register_pending_approval', 1)
          ->set('notify.register_no_approval_required', 1);
        break;
    }
  }

  // Save mail data.
  $mail_config
    ->set('register_confirmation_with_pass.subject', $form_state
    ->getValue('user_registrationpassword_register_confirmation_with_pass_subject'))
    ->set('register_confirmation_with_pass.body', $form_state
    ->getValue('user_registrationpassword_register_confirmation_with_pass_body'));

  // Save or revert expiration limits variables.
  $config
    ->set('registration_ftll_expire', $form_state
    ->getValue('user_registrationpassword_registration_ftll_expire'))
    ->set('registration_ftll_timeout', $form_state
    ->getValue('user_registrationpassword_registration_ftll_timeout'));

  // Save configs.
  $config
    ->save();
  $mail_config
    ->save();
  $user_config
    ->save();
}

/**
 * Implements hook_form_FORM_ID_alter().
 *
 * @see user_register_form()
 * @see user_registrationpassword_form_user_register_submit()
 */
function user_registrationpassword_form_user_register_form_alter(&$form, FormStateInterface $form_state, $form_id) {

  /** @var \Drupal\user\UserInterface $account */
  $account = $form_state
    ->getFormObject()
    ->getEntity();
  $user_config = \Drupal::config('user.settings');
  $config = \Drupal::configFactory()
    ->get('user_registrationpassword.settings');

  // Add the password field from the user_account_form when visitors can
  // register, but require admin approval.
  if ($user_config
    ->get('register') == UserInterface::REGISTER_VISITORS_ADMINISTRATIVE_APPROVAL && $config
    ->get('registration') == UserRegistrationPassword::VERIFICATION_PASS && $account
    ->isNew()) {
    $form['account']['pass'] = [
      '#type' => 'password_confirm',
      '#size' => 25,
      '#description' => t('Provide a password for the new account in both fields.'),
      '#required' => TRUE,
    ];
  }

  // Prevent this from being run if approval with password on registration
  // form is set and the user is an anonymous user registering to the site.
  // When admin users create a user, this does not need to be executed.
  // And when this also does not need to be executed 'user_register' is not set
  // as 'Visitors can create accounts and no administrator approval is
  // required.' User registers, receives user_registrationpass email, would
  // not make sense. Cause that will unblock the user Without
  // the admin 'approving'.
  if ($user_config
    ->get('register') == UserInterface::REGISTER_VISITORS && $config
    ->get('registration') == UserRegistrationPassword::VERIFICATION_PASS && $account
    ->isNew()) {

    // Set the user account to blocked.
    $form['account']['status']['#default_value'] = 0;

    // Suppress any notification.
    $form['account']['notify']['#default_value'] = 0;

    // Register our validate and submit handlers.
    $form['actions']['submit']['#submit'][] = 'user_registrationpassword_form_user_register_submit';
  }
}

/**
 * Implements submission handler for the user registration form.
 *
 * @see user_register_form()
 * @see user_registrationpassword_form_user_register_form_alter()
 */
function user_registrationpassword_form_user_register_submit(&$form, FormStateInterface $form_state) {

  // Define the message we need to remove.
  // Yes, this throws a code style error, but this is in core.
  // See user.module, that string contains a <br />.
  $message = t('Thank you for applying for an account. Your account is currently pending approval by the site administrator.<br />In the meantime, a welcome message with further instructions has been sent to your email address.');

  // Unset all messages that contain the $message.
  $messenger = \Drupal::messenger();

  // Cannot delete a single message, getting all and re-setting but the
  // one from the core user register form.
  foreach ($messenger
    ->deleteByType(MessengerInterface::TYPE_STATUS) as $statusmessage) {
    if (!((string) $message == (string) $statusmessage)) {
      $messenger
        ->addStatus($statusmessage);
    }
  }

  // Notify the user.

  /** @var \Drupal\user\UserInterface $account */
  $account = $form_state
    ->getFormObject()
    ->getEntity();

  // Send the confirmation email.
  _user_registrationpassword_mail_notify('register_confirmation_with_pass', $account);

  // Set success message and redirect to the front page.
  \Drupal::messenger()
    ->addMessage(t('A welcome message with further instructions has been sent to your email address.'));
}

/**
 * Implements hook_mail().
 *
 * @see drupal_mail()
 * @see user_mail()
 */
function user_registrationpassword_mail($key, &$message, $params) {
  $token_service = \Drupal::token();
  $language_manager = \Drupal::languageManager();
  $langcode = $message['langcode'];
  $variables = [
    'user' => $params['account'],
  ];
  $language = $language_manager
    ->getLanguage($params['account']
    ->getPreferredLangcode());
  $original_language = $language_manager
    ->getConfigOverrideLanguage();
  $language_manager
    ->setConfigOverrideLanguage($language);
  $mail_config = \Drupal::config('user_registrationpassword.mail');
  $token_options = [
    'langcode' => $langcode,
    'callback' => 'user_registrationpassword_mail_tokens',
    'clear' => TRUE,
  ];
  $message['subject'] .= PlainTextOutput::renderFromHtml($token_service
    ->replace($mail_config
    ->get($key . '.subject'), $variables, $token_options));
  $message['body'][] = $token_service
    ->replace($mail_config
    ->get($key . '.body'), $variables, $token_options);
  $language_manager
    ->setConfigOverrideLanguage($original_language);
}

/**
 * Token callback to add unsafe tokens for user mails.
 *
 * @see user_mail_tokens()
 * @see user_registrationpassword_mail()
 * @see user_registrationpassword_confirmation_url()
 */
function user_registrationpassword_mail_tokens(&$replacements, $data, $options) {
  user_mail_tokens($replacements, $data, $options);
  if (isset($data['user'])) {
    $replacements['[user:registrationpassword-url]'] = user_registrationpassword_confirmation_url($data['user'], $options);
  }

  // For D7 we use the rpt module to enable emails to contain
  // the user's password, so no need to add the password
  // token here, people just need to install rpt.
}

/**
 * Generates a unique URL for a user to login with their password already set.
 *
 * @param \Drupal\user\UserInterface $account
 *   The user account.
 * @param array $options
 *   (optional) A keyed array of settings. Supported options are:
 *   - langcode: A language code to be used when generating locale-sensitive
 *    URLs. If langcode is NULL the users preferred language is used.
 *
 * @return string
 *   A unique URL that provides a one-time log in for the user, from which
 *   they can change their password.
 *
 * @see user_registrationpassword_mail_tokens()
 * @see user_pass_rehash()
 */
function user_registrationpassword_confirmation_url(UserInterface $account, array $options = []) {
  $timestamp = \Drupal::time()
    ->getRequestTime();
  $langcode = isset($options['langcode']) ? $options['langcode'] : $account
    ->getPreferredLangcode();
  return \Drupal::service('url_generator')
    ->generateFromRoute('user_registrationpassword.confirm', [
    'uid' => $account
      ->id(),
    'timestamp' => $timestamp,
    'hash' => user_pass_rehash($account, $timestamp),
  ], [
    'absolute' => TRUE,
    'language' => \Drupal::languageManager()
      ->getLanguage($langcode),
  ]);
}

/**
 * Simple message and redirect.
 *
 * @deprecated from 8.x-1.0-beta1 and dropped in 8.x-1.0.
 *   Use the drupal core messenger service instead.
 *
 * @param string $type
 *   The type of message.
 * @param string $redirect
 *   The redirect route.
 *
 * @return string
 *   The redirect route name.
 */
function user_registrationpassword_set_message($type = 'welcome', $redirect = '') {
  @trigger_error('user_registrationpassword_set_message() is deprecated in 8.x-1.0-beta1 and is removed in version 8.x-1.0 - Use the Drupal core messenger service instead.', E_USER_DEPRECATED);
  $route_name = '';

  // Select what message to display.
  switch ($type) {
    case 'linkerror':
      \Drupal::messenger()
        ->addStatus(t('You have tried to use a one-time login link that has either been used or is no longer valid. Please request a new one using the form below.'));

      // Redirect to user/pass.
      if (!empty($redirect)) {
        $route_name = 'user.pass';
      }
      break;
    case 'welcome':
      \Drupal::messenger()
        ->addStatus(t('Further instructions have been sent to your email address.'));

      // Redirect to front.
      if (!empty($redirect)) {
        $route_name = '<front>';
      }
      break;
  }
  return $route_name;
}

/**
 * Implements hook_form_FORM_ID_alter().
 */
function user_registrationpassword_form_user_pass_alter(&$form, FormStateInterface $form_state, $form_id) {
  $form['#validate'][0] = '_user_registrationpassword_user_pass_validate';
  $form['#submit'][0] = '_user_registrationpassword_user_pass_submit';
}

/**
 * Form validation function.
 */
function _user_registrationpassword_user_pass_validate(&$form, FormStateInterface $form_state) {

  // We try and load a blocked user that never ever logged in.
  // This should only return 'brand new' user accounts.
  $name = trim($form_state
    ->getValue('name'));

  // Try to load by email.
  $users = \Drupal::entityQuery('user')
    ->condition('mail', $name)
    ->condition('status', 0)
    ->condition('access', 0)
    ->condition('login', 0)
    ->execute();
  if (empty($users)) {

    // No success, try to load by name.
    $users = \Drupal::entityQuery('user')
      ->condition('name', $name)
      ->condition('status', 0)
      ->condition('access', 0)
      ->condition('login', 0)
      ->execute();
  }
  if (!empty($users)) {
    $uid = reset($users);

    /** @var \Drupal\user\UserInterface $account */
    $account = \Drupal::entityTypeManager()
      ->getStorage('user')
      ->load($uid);
  }

  // If the account has never ever been used, load
  // the $account into the form for processing.
  if (!empty($account) && $account
    ->id()) {
    $form_state
      ->setValueForElement([
      '#parents' => [
        'account',
      ],
    ], $account);
  }
  else {
    $core_validate = new UserPasswordForm(\Drupal::entityTypeManager()
      ->getStorage('user'), \Drupal::languageManager(), \Drupal::configFactory(), \Drupal::flood());
    $core_validate
      ->validateForm($form, $form_state);
  }
}

/**
 * Implements submit function.
 */
function _user_registrationpassword_user_pass_submit(&$form, FormStateInterface $form_state) {

  /** @var \Drupal\user\UserInterface $account */
  $account = $form_state
    ->getValue('account');

  // Then, if we have a user account
  // and it has never ever been used.
  if (!empty($account) && $account
    ->id() && !$account
    ->getLastLoginTime() && !$account
    ->getLastAccessedTime() && !$account
    ->isActive()) {

    // Try to load the account in disabled state.
    $users = \Drupal::entityQuery('user')
      ->condition('uid', $account
      ->id())
      ->condition('login', 0)
      ->condition('access', 0)
      ->condition('status', 0)
      ->execute();
    $uid = reset($users);

    // If the user never ever logged in, resend the activation mail.
    if (!empty($uid)) {

      /** @var \Drupal\user\UserInterface $account */
      $account = \Drupal::entityTypeManager()
        ->getStorage('user')
        ->load($uid);

      // Mail one time login URL and instructions using current language.
      $mail = _user_registrationpassword_mail_notify('register_confirmation_with_pass', $account);
      if (!empty($mail)) {

        // And on success, log the email & throw a message.
        \Drupal::logger('user')
          ->notice('Account confirmation mail sent via password reset form to %name at %email.', [
          '%name' => $account
            ->getAccountName(),
          '%email' => $account
            ->getEmail(),
        ]);
        \Drupal::messenger()
          ->addMessage(t('Further instructions have been sent to your email address.'));
      }
      else {

        // Or log the error.
        \Drupal::logger('user')
          ->notice('Unable to send email. Contact the site administrator if the problem persists.');
      }
    }
  }
  else {

    // If we found no errors execute core submission handler.
    // Obviously cache is disabled on this pages, so this should work.
    if (empty($_SESSION['messages']['error'])) {
      $core_submit = new UserPasswordForm(\Drupal::entityTypeManager()
        ->getStorage('user'), \Drupal::languageManager(), \Drupal::configFactory(), \Drupal::flood());
      $core_submit
        ->submitForm($form, $form_state);
    }
  }
}

/**
 * Implements hook_config_translation_info_alter().
 */
function user_registrationpassword_config_translation_info_alter(&$info) {
  $info['entity.user.admin_form']['names'][] = 'user_registrationpassword.mail';
}

/**
 * Conditionally create and send a notification email when a certain
 * operation happens on the given user account.
 *
 * @param $op
 *   The operation being performed on the account. Possible values:
 *   confirmation_with_pass / confirmation_admin_created.
 * @param \Drupal\Core\Session\AccountInterface $account
 *   The user object of the account being notified. Must contain at
 *   least the fields 'uid', 'name', and 'mail'.
 * @param string $langcode
 *   (optional) Language code to use for the notification, overriding account
 *   language.
 *
 * @return array
 *   An array containing various information about the message.
 *   See \Drupal\Core\Mail\MailManagerInterface::mail() for details.
 *
 * @see user_registrationpassword_mail_tokens()
 */
function _user_registrationpassword_mail_notify($op, AccountInterface $account, $langcode = NULL) {
  if (\Drupal::config('user_registrationpassword.settings')
    ->get('notify.' . $op)) {
    $params['account'] = $account;
    $langcode = $langcode ? $langcode : $account
      ->getPreferredLangcode();

    // Get the custom site notification email to use as the from email address
    // if it has been set.
    $site_mail = \Drupal::config('system.site')
      ->get('mail_notification');

    // If the custom site notification email has not been set, we use the site
    // default for this.
    if (empty($site_mail)) {
      $site_mail = \Drupal::config('system.site')
        ->get('mail');
    }
    if (empty($site_mail)) {
      $site_mail = ini_get('sendmail_from');
    }

    // Notify the user.
    $mail = \Drupal::service('plugin.manager.mail')
      ->mail('user_registrationpassword', $op, $account
      ->getEmail(), $langcode, $params, $site_mail);
    return empty($mail) ? NULL : $mail['result'];
  }
}

Functions

Namesort descending Description
user_registrationpassword_admin_settings_submit Submit handler for the user admin form.
user_registrationpassword_config_translation_info_alter Implements hook_config_translation_info_alter().
user_registrationpassword_confirmation_url Generates a unique URL for a user to login with their password already set.
user_registrationpassword_form_user_admin_settings_alter Implements hook_form_FORM_ID_alter().
user_registrationpassword_form_user_pass_alter Implements hook_form_FORM_ID_alter().
user_registrationpassword_form_user_register_form_alter Implements hook_form_FORM_ID_alter().
user_registrationpassword_form_user_register_submit Implements submission handler for the user registration form.
user_registrationpassword_mail Implements hook_mail().
user_registrationpassword_mail_tokens Token callback to add unsafe tokens for user mails.
user_registrationpassword_set_message Deprecated Simple message and redirect.
_user_registrationpassword_mail_notify Conditionally create and send a notification email when a certain operation happens on the given user account.
_user_registrationpassword_user_pass_submit Implements submit function.
_user_registrationpassword_user_pass_validate Form validation function.

Constants

Namesort descending Description
USER_REGISTRATIONPASSWORD_NO_VERIFICATION Deprecated No verification email is sent.
USER_REGISTRATIONPASSWORD_VERIFICATION_DEFAULT Deprecated Verification email is sent before password is set.
USER_REGISTRATIONPASSWORD_VERIFICATION_PASS Deprecated Verification email is sent after password is set.