You are here

user_email_verification.module in User email verification 7

Same filename and directory in other branches
  1. 8 user_email_verification.module

This module allows you to have e-mail verification and in meanwhile allowing the users to type their own passwords. If they do not verify their accounts in a certain time interval the user will be blocked.

File

user_email_verification.module
View source
<?php

/**
 * @file
 * This module allows you to have e-mail verification and in meanwhile allowing the users to type their own passwords.
 * If they do not verify their accounts in a certain time interval the user will be blocked.
 */

// TODO: Build action for rules for e-mail verification e-mail send!

/**
 * Implements hook_cron_queue_info().
 */
function user_email_verification_cron_queue_info() {
  $queues['user_email_verification_tasks'] = array(
    'worker callback' => 'user_email_verification_task',
    'time' => 15,
  );
  $queues['user_email_verification_reminders'] = array(
    'worker callback' => 'user_email_verification_reminder',
    'time' => 15,
  );
  $queues['user_email_verification_extended'] = array(
    'worker callback' => 'user_email_verification_delete_account',
    'time' => 15,
  );
  return $queues;
}

/**
 * Queue worker callback for running a single task.
 *
 * @param array $task
 *   The task to process.
 */
function user_email_verification_task($uid) {

  // Block account if active
  user_email_verification_block_account($uid);
}

/**
 * Block user account if active and email user if extended verification is
 * enabled
 */
function user_email_verification_block_account($uid) {
  $account = user_load($uid);

  // If the account is active, it shold be blocked
  if ($account->status == 1) {
    $account->status = 0;
    user_save($account);
    if (module_exists('rules')) {

      // Invoke rules event
      rules_invoke_event('user_email_verification_account_blocked', $account);
    }

    // If extended verification period is enabled, then send e-mail to user with
    // a link which lets user to activate and verify the account within defined
    // time period.
    if (variable_get('user_email_verification_extended_enable', 0)) {
      $params['account'] = $account;
      $mail = drupal_mail('user_email_verification', 'verify_extended', $account->mail, $account->language, $params);
    }
  }
}

/**
 * Queue worker callback for sending a single reminder.
 *
 * @param int $uid
 *   The user ID to process.
 */
function user_email_verification_reminder($uid) {

  // Only send the reminder if the user is not verified yet and the number of
  // reminders has not been reached yet.
  $interval = variable_get('user_email_verification_validate_interval', 86400);
  $num_reminders = variable_get('user_email_verification_num_reminders', 0);
  $reminder_interval = $interval / ($num_reminders + 1);
  $result = db_query("SELECT reminders FROM {user_email_verification} WHERE uid = :uid\n    AND verified = 0 AND reminders < :num_reminders AND last_reminder < :reminder", array(
    ':uid' => $uid,
    ':num_reminders' => $num_reminders,
    ':reminder' => REQUEST_TIME - $reminder_interval,
  ))
    ->fetchField();
  if ($result === FALSE) {
    return;
  }
  $account = user_load($uid);
  $params['account'] = $account;
  $language = user_preferred_language($account);
  drupal_mail('user_email_verification', 'verify', $account->mail, $language, $params);
  if (module_exists('rules')) {

    // Invoke rules event
    rules_invoke_event('user_email_verification_account_reminded', $account);
  }

  // Always increase the reminder mail counter by one even if sending the mail
  // failed. Some mail systems like Mandrill return FALSE if they cannot deliver
  // the mail to an invalid address. We need to increase the counter to make
  // sure the users get blocked at some point.
  db_update('user_email_verification')
    ->condition('uid', $account->uid)
    ->expression('reminders', 'reminders + 1')
    ->fields(array(
    'last_reminder' => REQUEST_TIME,
  ))
    ->execute();
}

/**
 * Queue worker callback for running a single task. Delete user account when
 * e-mail address has not been verified after extended verification period
 * has passed.
 *
 * @param $uid
 *   User ID to be deleted
 */
function user_email_verification_delete_account($uid) {

  // Load the account before canceling it
  $account = user_load($uid);
  if ($account) {
    if (module_exists('rules')) {

      // Invoke rules event
      rules_invoke_event('user_email_verification_account_deleted', $account);
    }

    // Notify account about cancellation
    $op = 'status_canceled';
    _user_mail_notify($op, $account);

    // Get the cancel method
    $user_cancel_method = variable_get('user_cancel_method');
    user_cancel(array(), $uid, $user_cancel_method);

    // user_cancel() initiates a batch process. Run it manually.
    $batch =& batch_get();
    $batch['progressive'] = FALSE;
    batch_process();
    watchdog('user', 'User with uid %uid has been deleted due to e-mail address not being verified within extended verification period.', array(
      '%uid' => $uid,
    ));
  }
  else {
    watchdog('user', 'User with uid %uid could not be deleted because the uid does not exist anymore.', array(
      '%uid' => $uid,
    ));
  }
}

/**
 * Implements hook_cron().
 */
function user_email_verification_cron() {
  $interval = variable_get('user_email_verification_validate_interval', 86400);
  $num_reminders = variable_get('user_email_verification_num_reminders', 0);
  $reminder_interval = $interval / ($num_reminders + 1);
  $skip_roles = variable_get('user_email_verification_roles', array());
  $skip_roles = array_filter($skip_roles);

  // Select those that need to be blocked.
  $query = db_select('user_email_verification', 'uev');
  $query
    ->join('users', 'u', 'uev.uid = u.uid');
  if (!empty($skip_roles)) {
    $query
      ->leftJoin('users_roles', 'ur', 'ur.uid = uev.uid');
    $query
      ->distinct('uev.uid');
    $or = db_or()
      ->condition('ur.rid', array_keys($skip_roles), 'NOT IN')
      ->isNull('ur.rid');

    // normal registered users don't have an entry in the users_roles table.
    $query
      ->condition($or);
  }
  $result = $query
    ->fields('u', array(
    'uid',
  ))
    ->condition('uev.verified', 0, '=')
    ->condition('u.uid', 1, '>')
    ->condition('uev.last_reminder', REQUEST_TIME - $reminder_interval, '<')
    ->condition('uev.reminders', $num_reminders, '>=')
    ->execute();
  $queue = DrupalQueue::get('user_email_verification_tasks');
  foreach ($result as $account) {
    $queue
      ->createItem($account->uid);
  }

  // Select those that need to be sent a reminder.
  $query = db_select('user_email_verification', 'uev');
  $query
    ->join('users', 'u', 'uev.uid = u.uid');
  if (!empty($skip_roles)) {
    $query
      ->leftJoin('users_roles', 'ur', 'ur.uid = uev.uid');
    $query
      ->distinct('uev.uid');
    $or = db_or()
      ->condition('ur.rid', array_keys($skip_roles), 'NOT IN')
      ->isNull('ur.rid');

    // normal registered users don't have an entry in the users_roles table.
    $query
      ->condition($or);
  }
  $result = $query
    ->fields('u', array(
    'uid',
  ))
    ->condition('uev.verified', 0, '=')
    ->condition('u.uid', 1, '>')
    ->condition('uev.last_reminder', REQUEST_TIME - $reminder_interval, '<')
    ->condition('uev.reminders', $num_reminders, '<')
    ->execute();
  $queue = DrupalQueue::get('user_email_verification_reminders');
  foreach ($result as $account) {
    $queue
      ->createItem($account->uid);
  }
  if (variable_get('user_email_verification_extended_enable', 0)) {

    // Delete accounts which have not verified their e-mail addresses
    // within extended time period. Similar to blocking users, but doesn't
    // care about reminder settings.
    $extended_interval = variable_get('user_email_verification_extended_validate_interval', 86400);
    $skip_roles = variable_get('user_email_verification_roles', array());
    $skip_roles = array_filter($skip_roles);

    // Select those that need to be blocked.
    $query = db_select('user_email_verification', 'uev');
    $query
      ->join('users', 'u', 'uev.uid = u.uid');
    if (!empty($skip_roles)) {
      $query
        ->leftJoin('users_roles', 'ur', 'ur.uid = uev.uid');
      $query
        ->distinct('uev.uid');
      $or = db_or()
        ->condition('ur.rid', array_keys($skip_roles), 'NOT IN')
        ->isNull('ur.rid');

      // normal registered users don't have an entry in the users_roles table.
      $query
        ->condition($or);
    }
    $result = $query
      ->fields('u', array(
      'uid',
    ))
      ->condition('uev.verified', 0, '=')
      ->condition('u.uid', 1, '>')
      ->condition('uev.last_reminder', REQUEST_TIME - $extended_interval, '<')
      ->execute();
    $queue = DrupalQueue::get('user_email_verification_extended');
    foreach ($result as $account) {
      $queue
        ->createItem($account->uid);
    }
  }
}

/**
 * Implements hook_menu().
 */
function user_email_verification_menu() {
  $items['user/verify'] = array(
    'title' => 'Request new e-mail verification',
    'page callback' => 'drupal_get_form',
    'page arguments' => array(
      'user_email_verification_request',
    ),
    'access callback' => TRUE,
    'type' => MENU_LOCAL_TASK,
    'file' => 'user_email_verification.admin.inc',
  );
  $items['user/email-verify/%/%/%'] = array(
    'title' => 'Verify user e-mail',
    'page callback' => 'drupal_get_form',
    'page arguments' => array(
      'user_email_verification_verify',
      2,
      3,
      4,
    ),
    'access callback' => TRUE,
    'type' => MENU_CALLBACK,
    'file' => 'user_email_verification.admin.inc',
  );
  $items['user/email-verify-extended/%/%/%'] = array(
    'title' => 'Verify user e-mail',
    'page callback' => 'drupal_get_form',
    'page arguments' => array(
      'user_email_verification_verify_extended',
      2,
      3,
      4,
    ),
    'access callback' => TRUE,
    'type' => MENU_CALLBACK,
    'file' => 'user_email_verification.admin.inc',
  );
  return $items;
}

/**
 * Implements of hook_form_FORM_ID_alter().
 */
function user_email_verification_form_user_admin_settings_alter(&$form, &$form_state, $form_id) {
  $form['user_email_verification'] = array(
    '#type' => 'fieldset',
    '#title' => t('User e-mail verification'),
  );
  $roles = array_map('check_plain', user_roles(TRUE));
  unset($roles[DRUPAL_AUTHENTICATED_RID]);
  $form['user_email_verification']['user_email_verification_roles'] = array(
    '#type' => 'checkboxes',
    '#title' => t('Roles'),
    '#default_value' => variable_get('user_email_verification_roles', array()),
    '#options' => $roles,
    '#access' => !empty($roles) && user_access('administer permissions'),
    '#description' => t('Select the roles for which we should not verify the e-mail address!'),
  );
  $form['user_email_verification']['user_email_verification_validate_interval'] = array(
    '#type' => 'textfield',
    '#title' => t('Verification time interval'),
    '#required' => TRUE,
    '#default_value' => variable_get('user_email_verification_validate_interval', 86400),
    '#description' => t('Enter the time interval in seconds in which the user must validate his/her e-mail.'),
    '#element_validate' => array(
      'element_validate_number',
    ),
  );
  $form['user_email_verification']['user_email_verification_num_reminders'] = array(
    '#type' => 'select',
    '#title' => t('Send reminder'),
    '#options' => array(
      0 => '- never -',
      1 => 'Once',
      2 => 'Twice',
      3 => 'Three times',
    ),
    '#required' => TRUE,
    '#default_value' => variable_get('user_email_verification_num_reminders', 0),
    '#description' => t('Select the number of reminders to be sent spread equally through the time interval in which the user must validate his/her e-mail.'),
  );
  $form['user_email_verification']['user_email_verification_subject'] = array(
    '#type' => 'textfield',
    '#title' => t('Verification mail subject'),
    '#default_value' => variable_get('user_email_verification_subject', t('Verification e-mail')),
    '#maxlength' => 180,
    '#description' => t('Subject for e-mail when user is requesting a new verification link at /user/verify. <strong>On registration user will receive the core\'s "Welcome (no approval required)" message.</strong>'),
  );
  $form['user_email_verification']['user_email_verification_body'] = array(
    '#type' => 'textarea',
    '#title' => t('Verification mail body'),
    '#default_value' => variable_get('user_email_verification_body', t('Please verify your e-mail by following the link: [user:verify-email]')),
    '#rows' => 10,
    '#description' => t('Among other tokens, [user:verify-email] can be used to display the link to e-mail verification.'),
  );
  $form['user_email_verification']['user_email_verification_extended_enable'] = array(
    '#type' => 'checkbox',
    '#title' => t('Enable extended verification period'),
    '#default_value' => variable_get('user_email_verification_extended_enable', 0),
    '#description' => t('Extended verification period allows you to define another time period when the account can be still verified even after being blocked.'),
  );
  $form['user_email_verification']['extended'] = array(
    '#type' => 'fieldset',
    '#title' => t('Extended verification period'),
    // Hide the extended settings when disabled.
    '#states' => array(
      'invisible' => array(
        'input[name="user_email_verification_extended_enable"]' => array(
          'checked' => FALSE,
        ),
      ),
    ),
  );
  $form['user_email_verification']['extended']['user_email_verification_extended_validate_interval'] = array(
    '#type' => 'textfield',
    '#title' => t('Extended verification time interval'),
    '#required' => FALSE,
    '#default_value' => variable_get('user_email_verification_extended_validate_interval', 604800),
    '#description' => t('Enter the extended time interval in seconds in which the user must validate his/her e-mail before the account gets deleted completely.'),
    '#element_validate' => array(
      'element_validate_number',
    ),
  );
  $form['user_email_verification']['extended']['user_email_verification_extended_subject'] = array(
    '#type' => 'textfield',
    '#title' => t('Mail subject'),
    '#default_value' => variable_get('user_email_verification_extended_subject', t('Account blocked, please verify e-mail address')),
    '#maxlength' => 180,
    '#description' => t('Subject for e-mail when an account is blocked after not being verified.'),
  );
  $form['user_email_verification']['extended']['user_email_verification_extended_body'] = array(
    '#type' => 'textarea',
    '#title' => t('Mail body'),
    '#default_value' => variable_get('user_email_verification_extended_body', t('Your account is now blocked. Your e-mail may still be verified by following the link: [user:verify-email-extended]')),
    '#rows' => 10,
    '#description' => t('Among other tokens, [user:verify-email-extended] can be used to display the link to e-mail verification.'),
  );
}

/**
 * Implements hook_user_delete().
 */
function user_email_verification_user_delete($account) {
  db_delete('user_email_verification')
    ->condition('uid', $account->uid)
    ->execute();
}

/**
 * Implements hook_user_insert().
 */
function user_email_verification_user_insert(&$edit, $account, $category) {
  db_insert('user_email_verification')
    ->fields(array(
    'uid' => $account->uid,
    'verified' => 0,
    'last_reminder' => REQUEST_TIME,
    'reminders' => 0,
  ))
    ->execute();
}

/**
 * Implements hook_mail_alter().
 */
function user_email_verification_mail_alter(&$message) {
  if ($message['module'] == 'user' || $message['module'] == 'user_email_verification') {
    $language = $message['language'];
    $params = $message['params'];
    $account = $params['account'];
    if (!empty($account)) {
      if (!empty($message['subject'])) {
        $message['subject'] = token_replace($message['subject'], array(
          'user' => $account,
        ), array(
          'language' => $language,
          'sanitize' => FALSE,
          'clear' => TRUE,
        ));
      }
      if (!empty($message['body'])) {
        foreach ($message['body'] as &$body) {
          $body = token_replace($body, array(
            'user' => $account,
          ), array(
            'language' => $language,
            'sanitize' => FALSE,
            'clear' => TRUE,
          ));
        }
      }
    }
  }
}

/**
 * Implements hook_mail().
 */
function user_email_verification_mail($key, &$message, $params) {
  $language = $message['language'];
  if ($key == 'verify') {
    $subject = t(variable_get('user_email_verification_subject', 'Verification e-mail'), [], [
      'langcode' => $language->language,
    ]);
    $body = t(variable_get('user_email_verification_body', 'Please verify your e-mail by following the link: [user:verify-email]'), [], [
      'langcode' => $language->language,
    ]);
    if (module_exists('i18n_variable')) {

      // If i18n_variable module is used: get the text variables directly and
      // not from the cache, because the language might not match the current
      // $language here.
      $subject_translated = i18n_variable_get('user_email_verification_subject', $language->language, NULL);

      // i18n_variable_get returns nothing if the realm is not set.
      if (!is_null($subject_translated)) {

        // Use translated value.
        $subject = $subject_translated;
      }
      $body_translated = i18n_variable_get('user_email_verification_body', $language->language, NULL);

      // i18n_variable_get returns nothing if the realm is not set.
      if (!is_null($body_translated)) {

        // Use translated value.
        $body = $body_translated;
      }
    }
    $message['subject'] .= $subject;
    $message['body'][] = $body;
  }
  if ($key == 'verify_blocked') {
    $account = $params['account'];
    $message['subject'] .= t('A blocked account verified his e-mail.');
    $message['body'][] = t('A blocked account ID: @AID verified his e-mail. If the account is not blocked for other reason, please unblock the account.');
  }
  if ($key == 'verify_extended') {
    $account = $params['account'];
    $message['subject'] .= variable_get('user_email_verification_extended_subject', t('Account blocked, please verify e-mail address'));
    $message['body'][] = variable_get('user_email_verification_extended_body', t('Your account is now blocked. Your e-mail may still be verified by following the link: [user:verify-email-extended]'));
  }
}

/**
 * Implements hook_variable_info().
 *
 * Makes the user_email_verification mail translatable.
 */
function user_email_verification_variable_info($options) {
  $variables['user_email_verification_[mail_part]'] = [
    'type' => 'user_mail',
    'title' => t('User email verification', [], $options),
    'description' => t('Customize verification e-mail messages to verify email address.', [], $options),
    'group' => 'user_mails',
  ];
  return $variables;
}

/**
 * Send e-mail to user when they request a new verification link
 */
function user_email_verification_mail_notify($op, $account, $language = NULL) {
  $params['account'] = $account;
  $language = $language ? $language : user_preferred_language($account);
  $mail = drupal_mail('user_email_verification', 'verify', $account->mail, $language, $params);
  return empty($mail) ? NULL : $mail['result'];
}

/**
 * Generate the
 * @param unknown $account
 * @return string
 */
function user_email_verification_url($account) {
  $timestamp = REQUEST_TIME;
  $hmac = user_email_verification_hmac($account->uid, $timestamp);
  $language = user_preferred_language($account);
  return url("user/email-verify/{$account->uid}/{$timestamp}/" . $hmac, array(
    'absolute' => TRUE,
    'language' => $language,
  ));
}

/**
 * Generate the
 * @param unknown $account
 * @return string
 */
function user_email_verification_extended_url($account) {
  $timestamp = REQUEST_TIME;
  $hmac = user_email_verification_hmac($account->uid, $timestamp);
  return url("user/email-verify-extended/{$account->uid}/{$timestamp}/" . $hmac, array(
    'query' => array(
      'language' => $account->language,
    ),
    'absolute' => TRUE,
  ));
}

/**
 * Generate HMAC.
 * @param unknown $uid
 * @param unknown $timestamp
 * @return string
 */
function user_email_verification_hmac($uid, $timestamp) {
  $string = drupal_get_hash_salt() . $uid . variable_get('user_email_verification_salt', 'salt');
  return drupal_hmac_base64($timestamp . $uid, $string);
}

/**
 * Return the verified flag
 * @param int $uid
 * @return bool
 */
function user_email_verification_load_verify_flag($uid) {
  $result = db_select('user_email_verification')
    ->fields('user_email_verification', array(
    'verified',
  ))
    ->condition('uid', $uid, '=')
    ->execute()
    ->fetchAssoc();
  if (!empty($result)) {
    return (bool) $result['verified'];
  }
  return FALSE;
}

/**
 * Implements hook_user_view().
 */
function user_email_verification_user_view($account, $view_mode, $langcode) {

  // Present the information if a user has a verified account.
  if ($view_mode == "full" && user_access('administer users')) {
    $account->content['user_email_verification'] = array(
      '#type' => 'user_profile_category',
      '#attributes' => array(
        'class' => array(
          'user-email-verified',
        ),
      ),
      '#title' => t('User email verification'),
    );
    $account->content['user_email_verification']['verified'] = array(
      '#type' => 'user_profile_item',
      '#title' => t('Verified account'),
      '#markup' => user_email_verification_load_verify_flag($account->uid) ? t("Verified") : t("Not verified"),
    );
  }
}

/**
 * Implements hook_views_api().
 */
function user_email_verification_views_api() {
  return array(
    'api' => 3,
    'path' => drupal_get_path('module', 'user_email_verification') . '/views',
  );
}

/**
 * Implements hook_user_presave().
 */
function user_email_verification_user_presave(&$edit, $account, $category) {

  // An admin user activated a user account
  if (user_access('administer users') && (isset($edit['status']) && $edit['status'] == 1) && (isset($account->original, $account->original->status) && $account->original->status == 0)) {

    // Mark user as verified
    db_update('user_email_verification')
      ->fields(array(
      'verified' => 1,
    ))
      ->condition('uid', $account->uid, '=')
      ->execute();
  }
}

Functions

Namesort descending Description
user_email_verification_block_account Block user account if active and email user if extended verification is enabled
user_email_verification_cron Implements hook_cron().
user_email_verification_cron_queue_info Implements hook_cron_queue_info().
user_email_verification_delete_account Queue worker callback for running a single task. Delete user account when e-mail address has not been verified after extended verification period has passed.
user_email_verification_extended_url Generate the
user_email_verification_form_user_admin_settings_alter Implements of hook_form_FORM_ID_alter().
user_email_verification_hmac Generate HMAC.
user_email_verification_load_verify_flag Return the verified flag
user_email_verification_mail Implements hook_mail().
user_email_verification_mail_alter Implements hook_mail_alter().
user_email_verification_mail_notify Send e-mail to user when they request a new verification link
user_email_verification_menu Implements hook_menu().
user_email_verification_reminder Queue worker callback for sending a single reminder.
user_email_verification_task Queue worker callback for running a single task.
user_email_verification_url Generate the
user_email_verification_user_delete Implements hook_user_delete().
user_email_verification_user_insert Implements hook_user_insert().
user_email_verification_user_presave Implements hook_user_presave().
user_email_verification_user_view Implements hook_user_view().
user_email_verification_variable_info Implements hook_variable_info().
user_email_verification_views_api Implements hook_views_api().