'Email verifications', 'description' => 'Form to submit for email verification', 'page callback' => 'drupal_get_form', 'page arguments' => array('email_required_verification_form'), 'access callback' => 'email_required_verification_form_access', 'file' => 'email_required.pages.inc', 'type' => MENU_CALLBACK, ); $items['email/verify/%user/%'] = array( 'title' => 'Email verification', 'description' => 'Path to verify your email address', 'page callback' => 'email_required_verification_page', 'page arguments' => array(2, 3), 'access callback' => 'email_required_verification_page_access', 'access arguments' => array(2, 3), 'file' => 'email_required.pages.inc', 'type' => MENU_CALLBACK, ); $items['admin/config/people/email-required'] = array( 'title' => 'Email verification', 'description' => 'Configure when users will need to verify their email address', 'page callback' => 'drupal_get_form', 'page arguments' => array('email_required_admin_settings'), 'access arguments' => array('administer email required'), 'file' => 'email_required.admin.inc', 'type' => MENU_NORMAL_ITEM, ); return $items; } /** * Implements hook_init(). */ function email_required_init() { // If authenticated if (user_is_logged_in()) { // See if we have paths to enforce a required email if ($paths = variable_get('email_required_paths', NULL)) { // Determine the current path $path = drupal_strtolower(drupal_get_path_alias($_GET['q'])); // See if we have a match if (drupal_match_path($path, $paths)) { // See if the user has to validate their email if (!email_required_user_is_validated()) { // See if we can capture the referred page $options = array(); if (isset($_SERVER['HTTP_REFERER']) && $referer = $_SERVER['HTTP_REFERER']) { $referer = str_replace(url('', array('absolute' => TRUE)), '', $referer); $options['query'] = array('destination' => $referer); } // Redirect to the verify form drupal_goto('email/verify', $options); } } } } } /** * Implements hook_mail(). */ function email_required_mail($key, &$message, $params) { global $user; // Get email text $text = _email_required_email_text(); // Inject a verification link $text['body'] = str_replace('!link', email_required_generate_link($user, TRUE), $text['body']); // Attach to the message, and replace tokens $message['subject'] = token_replace($text['subject'], array('user' => $user)); $message['body'] = array(token_replace($text['body'], array('user' => $user))); } /** * Implements hook_permission(). */ function email_required_permission() { return array( 'administer email required' => array( 'title' => t('Administer email required'), 'description' => t('Configure the settings for this module.'), ), 'bypass email required' => array( 'title' => t('Bypass email verification'), 'description' => t('These users will not have to verify their email. Anonyous users will never have to.'), ), ); } /** * Implements hook_user_presave(). */ function email_required_user_presave(&$edit, $account, $category) { // See if the user exists yet if ($account->uid) { // See if the email address has changed if (strtolower($edit['mail']) != strtolower($account->mail)) { // Mark this user as no longer having a verified email address email_required_delete_hash($account); } } } /** * Generate a URL to verify an email address * * @param $user * A user object or uid * @param $absolute * TRUE if the link should be absolute. * @return * A link to verify an email address, or FALSE if one cannot be made. */ function email_required_generate_link($user = NULL, $absolute = FALSE) { // Get the uid $uid = _email_required_get_uid($user); // Get the hash string if ($hash = email_required_load_hash($user, TRUE)) { // Provide the link return url("email/verify/{$uid}/{$hash->hash}", array('absolute' => $absolute)); } return FALSE; } /** * Delete a hash entry for a given user * * @param $user * A user object or uid */ function email_required_delete_hash($user = NULL) { // Delete the hash entry db_delete('email_required') ->condition('uid', _email_required_get_uid($user)) ->execute(); } /** * Create a hash entry for a given user * * This will delete any existing entries so call with caution * * @param $user * A user object * @return * A hash object */ function email_required_create_hash($user = NULL) { if (!$user) { global $user; } // Create the hash object $hash = array( 'uid' => _email_required_get_uid($user), 'hash' => _email_required_hash($user), 'created' => REQUEST_TIME, 'verified' => 0, ); // Delete any existing hashes for this user email_required_delete_hash($user); // Insert it db_insert('email_required') ->fields($hash) ->execute(); return $hash; } /** * Validate a user's hash * * @param $user * A user object or uid */ function email_required_validate_hash($user = NULL) { // Delete the hash entry db_update('email_required') ->fields(array('verified' => REQUEST_TIME)) ->condition('uid', _email_required_get_uid($user)) ->execute(); } /** * Get a user's hash object * * @param $user * A user object or uid * @param $reset * TRUE if the static cache should be skipped, otherwise FALSE. * @return * A hash object */ function email_required_load_hash($user = NULL, $reset = FALSE) { static $hashes = array(); // Determine the uid $uid = _email_required_get_uid($user); // Check the cache if ($reset || !isset($hashes[$uid])) { // Fetch the hash entry $hashes[$uid] = db_select('email_required', 'e') ->fields('e', array('uid', 'hash', 'created', 'verified')) ->condition('uid', $uid) ->execute() ->fetchObject(); } return $hashes[$uid]; } /** * Determine if a given user has validated their email * * @param $user * A user object or uid * @param $admin * TRUE if users with adequate permissions will be considered * validated regardless * @return * TRUE if the user is validated, otherwise FALSE. */ function email_required_user_is_validated($user = NULL, $admin = TRUE) { static $validated = array(); // Determine the uid $uid = _email_required_get_uid($user); // Generate a cache key $key = $uid . ($admin ? ':admins' : ''); // Check the cache if (!isset($validated[$key])) { // Assume false $validated[$key] = FALSE; // Check higher permissions first, if desired if ($admin && (user_access('administer email required') || user_access('bypass email required'))) { $validated[$key] = TRUE; } // Load the hash to check else if ($hash = email_required_load_hash($user)) { if ($hash->verified) { $validated[$key] = TRUE; } } // Allow modules to alter this drupal_alter('email_required_validated', $user, $admin, $validated[$key]); } return $validated[$key]; } /** * Access callback for the verification form */ function email_required_verification_form_access() { global $user; // No anonymous allowed if (!$user->uid) { return FALSE; } return email_required_user_is_validated($user) ? FALSE : TRUE; } /** * Access callback for the verification page * * @param $account * A user object specified in the link * @param $string * The hash string on the URL */ function email_required_verification_page_access($account, $string) { global $user; // If the user is logged in, they can only verify themselves if ($user->uid && ($account->uid != $user->uid)) { return FALSE; } // We'll check for the existance of a hash, and the validity of it // in the page callback, because we don't want a 403 if the hash // has been purged return TRUE; } /** * Wrapper for user_pass_rehash() * * Generate a a hash that will be used in the verification URL * * @param $user * A user object or ID * @return * A hash */ function _email_required_hash($user) { return user_pass_rehash($user->pass, REQUEST_TIME, $user->name); } /** * Helper function to determine the user id to use * * @param $user * A user object, or user ID, or NULL if you want to use * the current user. * @return * A user ID */ function _email_required_get_uid($user = NULL) { if (!$user) { global $user; } return is_object($user) ? $user->uid : $user; } /** * Helper function to return email strings * * @return * An array containing the 'subject' and 'body for the * verification email */ function _email_required_email_text() { // Load the defaults $text = array( 'subject' => variable_get('email_required_subject', NULL), 'body' => variable_get('email_required_body', NULL), ); // See if we need a subject if (!$text['subject']) { $text['subject'] = '[[site:name]] ' . t('Verify your email address'); } // See if we need a body if (!$text['body']) { $text['body'] = '[user:name],' . "\n\n"; $text['body'] .= t('You have requested to have your email address validated at !sitename.', array('!sitename' => '[site:name]')) . "\n\n"; $text['body'] .= t('Click the link below to validate your email address:') . "\n\n"; $text['body'] .= '!link' . "\n\n"; $text['body'] .= t('You will never have to validate your email address again, unless you change it.') . "\n\n"; $text['body'] .= '- [site:name]'; } return $text; }