You are here

user_external_invite.module in User External Invite 1.0.x

Same filename and directory in other branches
  1. 7.2 user_external_invite.module
  2. 7 user_external_invite.module

Invites a user to site when connecting via external protocol e.g. LDAP.

File

user_external_invite.module
View source
<?php

/**
 * @file
 * Invites a user to site when connecting via external protocol e.g. LDAP.
 */

/**
 * Implements hook_entity_info().
 */
function user_external_invite_entity_info() {
  $info = array();
  $info['ext-invite'] = array(
    'label' => t('Invite'),
    'base table' => 'user_external_invite',
    'entity class' => 'Entity',
    'controller class' => 'EntityAPIController',
    'entity keys' => array(
      'id' => 'id',
      'label' => 'mail',
    ),
    'module' => 'user_external_invite',
  );
  return $info;
}

/**
 * Implements hook_permission().
 */
function user_external_invite_permission() {
  return array(
    'invite new user' => array(
      'title' => t('Invite new user'),
      'restrict access' => TRUE,
      'description' => t('Allow access to send invitation email'),
    ),
  );
}

/**
 * Implements hook_menu().
 */
function user_external_invite_menu() {
  $items = array();
  $items['admin/config/people/invite'] = array(
    'title' => 'User external invite settings',
    'description' => 'Configure roles, email addresses, message templates, etc',
    'page callback' => 'drupal_get_form',
    'page arguments' => array(
      'user_external_invite_settings_form',
    ),
    'access arguments' => array(
      'administer users',
    ),
    'file' => 'user_external_invite.admin.inc',
    'type' => MENU_NORMAL_ITEM,
  );
  $items['admin/people/invite'] = array(
    'title' => 'Invite users',
    'page callback' => 'user_external_invite_page',
    'page arguments' => array(
      'user_external_invite_form',
    ),
    'access arguments' => array(
      'invite new user',
    ),
    'file' => 'user_external_invite.admin.inc',
    'type' => MENU_LOCAL_TASK,
    'weight' => 3,
  );
  $items['admin/people/invite/invite'] = array(
    'title' => 'Invite users',
    'page callback' => 'user_external_invite_page',
    'page arguments' => array(
      'user_external_invite_form',
    ),
    'access arguments' => array(
      'invite new user',
    ),
    'file' => 'user_external_invite.admin.inc',
    'type' => MENU_DEFAULT_LOCAL_TASK,
  );
  $items['admin/people/invite/operations'] = array(
    'title' => 'Manage invites',
    'page callback' => 'user_external_invite_operations_page',
    'page arguments' => array(
      'user_external_invite_pending_invites_form',
    ),
    'access arguments' => array(
      'invite new user',
    ),
    'type' => MENU_LOCAL_TASK,
  );
  $items['user-external/accept-invite'] = array(
    'title' => 'Accept an invite',
    'page callback' => 'user_external_invite_accept_invite',
    'access arguments' => array(
      'access content',
    ),
    'type' => MENU_CALLBACK,
  );
  return $items;
}

/**
 * Page callback for admin/people/invite.
 */
function user_external_invite_page() {
  $build['form'] = drupal_get_form('user_external_invite_form');
  return $build;
}

/**
 * Page callback for admin/people/invite/operations.
 */
function user_external_invite_operations_page() {
  $build['invites'] = drupal_get_form('user_external_invite_pending_invites_form');
  return $build;
}

/**
 * Build table showing pending invites.
 */
function user_external_invite_pending_invites_form($form, &$form_state) {
  $form = array();
  $headers = array(
    'id' => t('ID'),
    'mail' => t('Email'),
    'role' => t('Role'),
    'expire' => t('Expires'),
    'inviter' => t('Inviter'),
    'status' => t('Status'),
  );
  $rids = variable_get('user_external_invite_roles', NULL);
  if (!empty($rids)) {
    foreach ($rids as $rid) {
      $roles[$rid] = _user_external_invite_role_name_from_rid($rid);
    }
  }
  $pending = _user_external_invite_pending_invites(NULL);
  $rows = array();
  while ($r = $pending
    ->fetchObject()) {
    $inviter = user_load($r->uid);
    $rows[] = array(
      'id' => $r->id,
      'mail' => $r->mail,
      'role' => $roles[$r->rid],
      'expire' => format_date($r->expire, 'short'),
      'inviter' => $inviter->mail,
      'status' => $r->status,
    );
  }

  // Determine whether or not to display confirm form message.
  if (!isset($form_state['storage']['confirm'])) {

    // Add operations only if rows are present.
    if (!empty($rows)) {

      // Add container for operations functionality.
      $form['operations'] = array(
        '#type' => 'fieldset',
        '#title' => 'Operations',
      );

      // Add select list for choosing operations.
      $select_options = array(
        'cancel' => 'Cancel Invites',
        'resend' => 'Resend Invites',
      );
      $form['operations']['select'] = array(
        '#type' => 'select',
        '#options' => $select_options,
        '#default_value' => $select_options['cancel'],
      );
      $form['operations']['resend_message'] = array(
        '#type' => 'textarea',
        '#title' => t('Custom Message'),
        '#cols' => 40,
        '#rows' => 5,
        '#description' => t('Message sent to users being re-invited. The rest of the email will include the "Invitation Email Template" text.'),
        '#states' => array(
          'visible' => array(
            ':input[name="select"]' => array(
              'value' => 'resend',
            ),
          ),
        ),
      );

      // Add submit button for operations.
      $form['operations']['submit'] = array(
        '#type' => 'submit',
        '#value' => t('Submit'),
      );
      $form['table'] = array(
        '#type' => 'tableselect',
        '#header' => $headers,
        '#options' => $rows,
        '#suffix' => empty($rows) ? 'No pending invites.' : '',
        '#weight' => 10,
      );
      $form['pager'] = array(
        '#theme' => 'pager',
        '#weight' => 11,
      );
    }
    else {
      drupal_set_message(t('No invites available.'), 'status');
    }
    return $form;
  }
  else {

    // Get operation being performed for message to user.
    $operation = $form_state['values']['select'];

    // Count the number of invites for user message.
    // @todo It would be nice to do this in a more concise way.
    $num_invites = 0;
    foreach ($form_state['input']['table'] as $value) {
      if ($value != NULL) {
        $num_invites += 1;
      }
    }

    // Determine if there are multiple invites for message display.
    $plural = $num_invites > 1 ? 'invites' : 'invite';

    // Need to pass current path and message variables to
    // confirm_form() function.
    $path = current_path();
    return confirm_form($form, 'Do you really want to ' . $operation . ' invites?', $path, $description = 'By proceeding you will ' . $operation . ' ' . $num_invites . ' ' . $plural . '. This action can not be undone.', 'Proceed', 'Cancel');
  }
}

/**
 * Submit callback for user_external_invite_pending_invites_form().
 *
 * @param array $form
 *   The form being used to edit the node.
 * @param array $form_state
 *   The form state array.
 */
function user_external_invite_pending_invites_form_submit(&$form, &$form_state) {

  // If the user has confirmed the form operation, proceed.
  if (isset($form_state['storage']['confirm'])) {

    // Define variable.
    $table_options = $form_state['storage']['form']['table']['#options'];
    $selected_options = $form_state['storage']['form_state']['input']['table'];
    $operation = $form_state['storage']['form_state']['values']['select'];
    $message = $form_state['storage']['form_state']['values']['resend_message'];
    $ids = array();

    // Loop through selected rows and match with invite IDs.
    foreach ($selected_options as $key => $option) {
      if ($option != NULL) {
        $ids[] = $table_options[$key]['id'];
      }
    }

    // If no IDs passed then return with error message.
    if (empty($ids)) {
      drupal_set_message(t('No invites selected for operation. Please select desired invites and try again.'), 'error');
      return;
    }

    // Pass IDs into corresponding callback function.
    switch ($operation) {
      case 'cancel':
        user_external_invite_cancel_invites($ids);
        break;
      case 'resend':

        // Adding additional message to send to invitees.
        user_external_invite_resend_invites($ids, $message);

        // Set message to user about successful resent invites.
        // @todo Handle this better for possible errors. We're assuming invites were sent.
        $num_invites = count($ids);
        $plural_invites = $num_invites > 1 ? 'invites' : 'invite';
        drupal_set_message(t('Successfully resent :number user :invites.', array(
          ':number' => $num_invites,
          ':invites' => $plural_invites,
        )));
        break;
      default:
        break;
    }
  }
  else {
    $form_state['storage']['form_state'] = $form_state;
    $form_state['storage']['form'] = $form;

    // This will cause the form to be rebuilt
    // returning to the confirm part of the form.
    $form_state['storage']['confirm'] = TRUE;
    $form_state['rebuild'] = TRUE;
  }
}

/**
 * Callback to resend invites.
 *
 * @param array $ids
 *   The user external invite Ids.
 */
function user_external_invite_resend_invites($ids = array(), $message = '') {

  // Grab row from db and pass to add invite function.
  // This is done to avoid duplication, but if enough needs added to resending,
  // then a separate resend function should be developed.
  $results = db_query('SELECT * FROM {user_external_invite} WHERE id IN (:ids)', array(
    ':ids' => $ids,
  ))
    ->fetchall();
  $resend = TRUE;
  foreach ($results as $result) {
    $message = !empty($message) ? $message : $result->message;
    _user_external_invite_add_invite($result->rid, array(
      $result->mail,
    ), $result->uid, $message, $resend);
  }
}

/**
 * Callback to cancel/remove pending invites.
 *
 * @param array $ids
 *   Ids to be deleted.
 */
function user_external_invite_cancel_invites($ids = array()) {

  // Delete pending invites from database and set message.
  db_delete('user_external_invite')
    ->condition('id', $ids, 'IN')
    ->execute();
  drupal_set_message(t('Deleted :ids user :invites.', array(
    ':ids' => count($ids),
    ':invites' => count($ids) > 1 ? 'invites' : 'invite',
  )));
}

/**
 * Implements hook_user_external_invite_excluded_roles().
 *
 * It doesn't make sense to invite someone as an anonymous user, so we take out
 * that role here.
 */
function user_external_invite_user_external_invite_excluded_roles($roles) {
  $anonymous_role = user_role_load_by_name('anonymous user');
  $roles[] = $anonymous_role->rid;
  return $roles;
}

/**
 * Get and return pending invites.
 *
 * @param null|string $mail
 *   The email to search for pending invites.
 *
 * @return $record
 *   Record of pending invite.
 */
function _user_external_invite_pending_invites($mail = NULL) {
  $query = db_select('user_external_invite', 'i')
    ->fields('i');
  $query = $query
    ->extend('PagerDefault')
    ->limit(10)
    ->extend('TableSort');
  if ($mail) {
    $query
      ->condition('mail', $mail);
  }
  $record = $query
    ->execute();
  return $record;
}

/**
 * Adds invite to database and sends email.
 */
function _user_external_invite_add_invite($rid, $emails, $uid, $message = NULL, $resend = FALSE) {
  $now = REQUEST_TIME;
  $days = variable_get('user_external_invite_days_valid_for', 5);

  // Expire in X days.
  $expire = $now + $days * 24 * 60 * 60;

  // All invites start out as pending.
  $status = 'Pending';

  // Add or update invite in db.
  foreach ($emails as $mail) {

    // Check and see if invite is supposed to be resent rather than added.
    if ($resend == TRUE) {
      db_update('user_external_invite')
        ->fields(array(
        'expire' => $expire,
        'status' => $status,
        'message' => $message,
      ))
        ->condition('mail', $mail, '=')
        ->execute();
    }
    else {
      db_insert('user_external_invite')
        ->fields(array(
        'mail' => $mail,
        'rid' => $rid,
        'expire' => $expire,
        'status' => $status,
        'uid' => $uid,
        'message' => $message,
      ))
        ->execute();
    }

    // Now send the email.
    _user_external_invite_send_invite($rid, $mail, $expire, $uid);
  }

  // After all emails are sent, send confirmation to submitter.
  global $user;
  $site_name = _user_external_invite_site_name();
  $from = _user_external_invite_from_email();
  $params = array(
    'subject' => t('Access request for the !site_name website', array(
      '!site_name' => $site_name,
    )),
    'rid' => $rid,
    'role_name' => _user_external_invite_role_name_from_rid($rid),
    'body' => token_replace(variable_get('user_external_invite_confirmation_template'), array(
      'ext-invite' => _user_external_invite_load_entity($emails[0]),
    )),
    'site_name' => $site_name,
  );

  // @TODO: use universal email?
  drupal_mail('user_external_invite', 'user_external_invite_sent', $user->mail, language_default(), $params, $from, TRUE);
}

/**
 * Sends email with special token-login link.
 */
function _user_external_invite_send_invite($rid, $email, $expire, $uid) {
  $hash = _user_external_invite_calculate_hash($rid, $email, $expire);
  _user_external_invite_send_invite_mail($rid, $email, $expire, $hash, $uid);
}

/**
 * Calculates the token based on $rid, $email, and $expire.
 */
function _user_external_invite_calculate_hash($rid, $email, $expire) {
  $hash = drupal_hmac_base64('user_rid:' . $rid . ',user_mail:' . $email, drupal_get_hash_salt() . $email . $expire);
  return $hash;
}

/**
 * Returns from address used to send mailing.
 *
 * @param int $uid
 *   User ID being invited.
 */
function _user_external_invite_from_email($uid = NULL) {

  // Needs to be configurable.
  if (variable_get('user_external_invite_use_universal_from_email', NULL)) {
    return variable_get('user_external_invite_universal_from_email', NULL);
  }
  else {

    // Get logged in user if none passed.
    if ($uid === NULL) {
      global $user;
    }
    else {
      $user = user_load($uid);
    }
    return $user->mail;
  }
}

/**
 * Get role name from role ID.
 */
function _user_external_invite_role_name_from_rid($rid) {
  $user_role = user_role_load($rid);

  // Format role name to be  more readable.
  $role_name = check_plain($user_role->name);
  $role_name = ucwords(str_replace('_', ' ', $role_name));
  return $role_name;
}

/**
 * Return site name.
 */
function _user_external_invite_site_name() {
  return variable_get('site_name', '');
}

/**
 * Sends invitation email with token login link.
 */
function _user_external_invite_send_invite_mail($rid, $mail, $expire, $hash, $uid) {
  $from = _user_external_invite_from_email();
  $link = url('user-external/accept-invite', array(
    'query' => array(
      'key' => $hash,
      'mail' => $mail,
    ),
    'absolute' => TRUE,
  ));
  $role_name = _user_external_invite_role_name_from_rid($rid);
  $site_name = _user_external_invite_site_name();
  $params = array(
    'token' => $hash,
    'rid' => $rid,
    'role_name' => $role_name,
    'expire' => $expire,
    'link' => $link,
    'uid' => $uid,
    'site_name' => $site_name,
    'subject' => t('Invitation to access the !site_name website', array(
      '!site_name' => $site_name,
    )),
    'body' => token_replace(variable_get('user_external_invite_invite_template'), array(
      'ext-invite' => _user_external_invite_load_entity($mail),
    )),
  );

  // Send mail to user who was invited.
  drupal_mail('user_external_invite', 'user_external_invite_token', $mail, language_default(), $params, $from, TRUE);
}

/**
 * Sends accepted invitation email with login link and extra help.
 */
function _user_external_invite_send_invite_accepted_mail($rid, $mail) {
  $from = _user_external_invite_from_email();
  $link = url('user/login', array(
    'absolute' => TRUE,
  ));
  $role_name = _user_external_invite_role_name_from_rid($rid);
  $site_name = _user_external_invite_site_name();
  $params = array(
    'rid' => $rid,
    'role_name' => $role_name,
    'link' => $link,
    'site_name' => $site_name,
    'subject' => t('Access confirmation for the !site_name website', array(
      '!site_name' => $site_name,
    )),
    'body' => token_replace(variable_get('user_external_invite_accepted_confirmation_template'), array(
      'ext-invite' => _user_external_invite_load_entity($mail),
    )),
  );
  drupal_mail('user_external_invite', 'user_external_invite_accepted', $mail, language_default(), $params, $from, TRUE);
}

/**
 * Sends confirmation to inviter that the invite was sent out.
 *
 * @param int $uid
 *   Uid of user who sent the invite.
 * @param string $mail
 *   Email address of user who was invited.
 * @param int $rid
 *   Role id that was granted.
 */
function _user_external_invite_send_inviter_confirmation($uid, $mail, $rid) {
  $account = user_load($uid);
  $from = _user_external_invite_from_email();
  $role_name = _user_external_invite_role_name_from_rid($rid);
  $site_name = _user_external_invite_site_name();
  $params = array(
    'rid' => $rid,
    'role_name' => $role_name,
    'site_name' => $site_name,
    'invite' => $mail,
    'subject' => t('Access request confirmation'),
    'body' => token_replace(variable_get('user_external_invite_accepted_template'), array(
      'ext-invite' => _user_external_invite_load_entity($mail),
    )),
  );
  drupal_mail('user_external_invite', 'user_external_invite_confirmation', $account->mail, language_default(), $params, $from, TRUE);
}

/**
 * Implements hook_mail().
 */
function user_external_invite_mail($key, &$message, $params) {
  switch ($key) {

    // Invite email.
    case 'user_external_invite_token':
      $message['body'] = array();
      $message['body'][] = $params['body'];
      $message['subject'] = $params['subject'];
      break;
    case 'user_external_invite_sent':
      $message['body'] = array();
      $message['body'][] = $params['body'];
      $message['subject'] = $params['subject'];
      break;
    case 'user_external_invite_accepted':
      $message['body'] = array();
      $message['body'][] = $params['body'];
      $message['subject'] = $params['subject'];
      break;
    case 'user_external_invite_confirmation':
      $message['body'] = array();
      $message['body'][] = $params['body'];
      $message['subject'] = $params['subject'];
      break;
  }
}

/**
 * Page callback for accepting an invite.
 *
 * If logged in, checks invite token and grants role, sends to user page.
 * If not logged in, sends to user/login with correct params to grant role on
 * successful login.
 */
function user_external_invite_accept_invite() {
  if (isset($_GET['key']) && isset($_GET['mail'])) {
    if (user_is_logged_in()) {
      global $user;
      user_external_invite_grant_invite($_GET['key'], $_GET['mail'], $user);
      drupal_goto('user');
    }
    else {
      drupal_goto('user/login', array(
        'query' => array(
          'key' => $_GET['key'],
          'mail' => $_GET['mail'],
        ),
      ));
    }
  }
}

/**
 * Grants an invite given a token and mail.
 *
 * Checks invite key+mail token is valid,
 * Grants role, sends emails, and removes invite from db.
 */
function user_external_invite_grant_invite($key, $mail, $account) {
  $grant_rid = _user_external_invite_dehash($key, $mail);

  // Allow for other actions and checks before a role is granted to a user.
  // If any hook returns a message, then the role will not be granted.
  $error_messages = module_invoke_all('user_external_invite_pre_grant_invite', $account, $grant_rid);
  if ($grant_rid && !$error_messages) {

    // Set message to user that role was granted.
    drupal_set_message(t('Invite accepted!'));

    // Check to see if the user already has the role.  Because the email
    // used in the invite is not always = to LDAP, they could already be
    // in the role.
    global $user;
    $role = user_role_load($grant_rid);
    if (in_array($role->name, $user->roles)) {
      return;
    }
    db_insert('users_roles')
      ->fields(array(
      'uid' => $account->uid,
      'rid' => $grant_rid,
    ))
      ->execute();

    // Send acceptance email.
    _user_external_invite_send_invite_accepted_mail($grant_rid, $account->mail);

    // Load the invite to send email to inviter.
    $invite = _user_external_invite_load_invite($mail);

    // Send email to inviter.
    _user_external_invite_send_inviter_confirmation($invite['uid'], $invite['mail'], $invite['rid']);

    // Once granted, change status of invite in database.
    _user_external_invite_change_invite_status($mail, 'Granted');
  }
  elseif ($error_messages) {
    foreach ($error_messages as $message) {
      drupal_set_message($message, 'error');
    }
  }
  else {
    drupal_set_message(t('Invite invalid or has expired! If you feel you have received this in error, please contact a site owner.'), 'error');
  }
}

/**
 * Loads external invite from entity from email.
 */
function _user_external_invite_load_entity($mail) {
  $query = new EntityFieldQuery();
  $query
    ->entityCondition('entity_type', 'ext-invite')
    ->propertyCondition('mail', $mail);
  $result = $query
    ->execute();
  if ($result['ext-invite']) {
    $id = array_pop($result['ext-invite']);
    return entity_load_single('ext-invite', $id->id);
  }
}

/**
 * Implements hook_user_login().
 *
 * Upon successful login, if key and mail are set, grant role.
 */
function user_external_invite_user_login(&$edit, $account) {
  if (isset($_GET['key']) && isset($_GET['mail'])) {
    user_external_invite_grant_invite($_GET['key'], $_GET['mail'], $account);
    drupal_goto('user');
  }
}

/**
 * Loads an invite.
 *
 * @param string $mail
 *   Email address used for loading invite.
 *
 * @return mixed $query
 *   Full invite being loaded.
 */
function _user_external_invite_load_invite($mail) {
  return db_select('user_external_invite', 'i')
    ->fields('i')
    ->condition('mail', $mail)
    ->execute()
    ->fetchAssoc();
}

/**
 * Change status of invite in {user_external_invite} table.
 *
 * Called to change the status of invite from pending to accepted or expired.
 */
function _user_external_invite_change_invite_status($mail, $status) {
  if ($status === 'canceled') {
    db_delete('user_external_invite')
      ->condition('mail', $mail)
      ->execute();
  }
  else {
    db_update('user_external_invite')
      ->fields(array(
      'status' => $status,
    ))
      ->condition('mail', $mail)
      ->execute();
  }
}

/**
 * Determine whether invite is accepted based on hash.
 *
 * Given a key-token and mail, calculate expected hash, if same as key,
 * return $rid of role to be granted.
 */
function _user_external_invite_dehash($key, $mail) {
  $result = db_select('user_external_invite', 'i')
    ->fields('i')
    ->condition('mail', $mail)
    ->execute()
    ->fetchAssoc();
  if ($result['expire'] < REQUEST_TIME) {

    // Token has expired.
    // @todo: need a better error here.
    return FALSE;
  }
  $expected_hash = _user_external_invite_calculate_hash($result['rid'], $result['mail'], $result['expire']);
  if ($key == $expected_hash) {
    return $result['rid'];
  }
  return FALSE;
}

/**
 * Implements hook_cron().
 *
 * Set invites to expired status if too much time has passed.
 */
function user_external_invite_cron() {

  // Set status of expired invites.
  db_update('user_external_invite')
    ->fields(array(
    'status' => 'Expired',
  ))
    ->condition('expire', REQUEST_TIME, '<')
    ->execute();

  // Delete old invites after 30 days.
  $expire_time = variable_get('user_external_invite_delete_old_invites', 60 * 60 * 24 * 30);
  db_delete('user_external_invite')
    ->condition('expire', REQUEST_TIME - $expire_time, '<')
    ->execute();
}

/**
 * Implements hook_token_info().
 */
function user_external_invite_token_info() {
  $types['user_external_invite'] = array(
    'name' => t("User External Invite"),
    'description' => t("Tokens for User External Invite."),
  );
  $info['invite_link'] = array(
    'name' => t('Invitation Link'),
    'description' => t('Returns the link with query string for this invite'),
  );
  $info['invite_role'] = array(
    'name' => t('Invitation Role'),
    'description' => t('Returns the Role for the invite'),
  );
  $info['invited_email'] = array(
    'name' => t('Email of Invite'),
    'description' => t('Returns the email for the invite'),
  );
  $info['invited_emails'] = array(
    'name' => t('Emails of Invitees'),
    'description' => t('Returns the emails of users who where invited at the same time'),
  );
  $info['invite_custom'] = array(
    'name' => t('Custom Message'),
    'description' => t('Returns custom message for the invite'),
  );
  $info['invite_expiration'] = array(
    'name' => t('Invite Expiration'),
    'description' => t('Returns expiration time of invite.'),
  );
  return array(
    'types' => $types,
    'tokens' => array(
      'user_external_invite' => $info,
    ),
  );
}

/**
 * Implements hook_tokens().
 */
function user_external_invite_tokens($type, $tokens, array $data = array(), array $options = array()) {
  $replacements = array();
  if ($type == 'user_external_invite') {
    foreach ($tokens as $name => $original) {
      switch ($name) {
        case 'invite_role':
          $name = _user_external_invite_role_name_from_rid($data['ext-invite']->rid);
          $replacements[$original] = $name;
          break;
      }
      switch ($name) {
        case 'invite_link':
          $hash = _user_external_invite_calculate_hash($data['ext-invite']->rid, $data['ext-invite']->mail, $data['ext-invite']->expire);
          $link = url('user-external/accept-invite', array(
            'query' => array(
              'key' => $hash,
              'mail' => $data['ext-invite']->mail,
            ),
            'absolute' => TRUE,
          ));
          $replacements[$original] = $link;
          break;
        case 'invited_email':
          $replacements[$original] = $data['ext-invite']->mail;
          break;
        case 'invited_emails':
          $replacements[$original] = _user_external_invite_load_related_invites($data['ext-invite']);
          break;
        case 'invite_custom':
          $replacements[$original] = $data['ext-invite']->message;
          break;
        case 'invite_expiration':
          $replacements[$original] = date('F j, Y g:i', $data['ext-invite']->expire) . ' ' . date_default_timezone_get();
          break;
      }
    }
  }
  return $replacements;
}

/**
 * Load invites that were invited at the same time.
 */
function _user_external_invite_load_related_invites($entity) {
  $query = new EntityFieldQuery();
  $query
    ->entityCondition('entity_type', 'ext-invite')
    ->propertyCondition('expire', $entity->expire);
  $result = $query
    ->execute();
  $entities = entity_load('ext-invite', array_keys($result['ext-invite']));
  $emails = '';
  foreach ($entities as $entity) {
    $emails .= $entity->mail . "\r\n";
  }
  return $emails;
}

/**
 * Implements hook_user_role_delete().
 *
 * Removes role from invited roles upon deletion.
 */
function user_external_invite_user_role_delete($role) {
  $rids = variable_get('user_external_invite_roles', NULL);
  $removed_id = $role->rid;
  if (in_array($removed_id, $rids)) {
    unset($rids[$removed_id]);
    variable_set('user_external_invite_roles', $rids);
  }
}

/**
 * Implements hook_system_info_alter().
 *
 * Prevent disabling of module if pending invites are present.
 */
function user_external_invite_system_info_alter(&$info, $file, $type) {
  if ($type == 'module' && $file->name == 'user_external_invite') {
    if (db_field_exists('user_external_invite', 'status') && db_query('SELECT * FROM {user_external_invite} WHERE status = :status', array(
      ':status' => 'Pending',
    ))
      ->fetchAll()) {
      $info['required'] = TRUE;
      $explanation = t('This module can\'t be disabled when there are invites pending. See a list of pending invites here - <a href="@invites">Invites list</a>', array(
        '@invites' => url('admin/people/invite/operations'),
      ));
      $info['explanation'] = $explanation;
    }
  }
}

/**
 * Implements hook_form_alter().
 *
 * If there are still invites pending, then changing anything related to
 * roles could interfere with the invite process.
 * Users should get a warning message to proceed with caution.
 *
 * @param array $form
 *   The form array passed to form_alter().
 *
 * @param array $form_state
 *   The values of the current form's state.
 */
function user_external_invite_form_alter(&$form, &$form_state) {

  // Only alter the roles and permissions forms.
  if ($form['#form_id'] == 'user_admin_permissions' || $form['#form_id'] == 'user_admin_roles') {

    // Check to see if any invites are pending and
    // what role ids are associated with them.
    if ($role_ids = db_query('SELECT rid FROM {user_external_invite} WHERE status = :status', array(
      ':status' => 'Pending',
    ))
      ->fetchCol()) {

      // Print out role names to attach to warning message.
      $role_names = array_map(function ($a) {
        return _user_external_invite_role_name_from_rid($a);
      }, $role_ids);
      drupal_set_message(t('User Invites are still pending with roles of: :roles. Altering roles or permissions could interfere with the user invite process. Pending invites can be viewed at: :urls', array(
        ':roles' => implode(', ', $role_names),
        ':urls' => '<a href="' . base_path() . 'admin/people/invite">admin/people/invite</a>',
      )), 'warning');
    }
  }
}

Functions

Namesort descending Description
user_external_invite_accept_invite Page callback for accepting an invite.
user_external_invite_cancel_invites Callback to cancel/remove pending invites.
user_external_invite_cron Implements hook_cron().
user_external_invite_entity_info Implements hook_entity_info().
user_external_invite_form_alter Implements hook_form_alter().
user_external_invite_grant_invite Grants an invite given a token and mail.
user_external_invite_mail Implements hook_mail().
user_external_invite_menu Implements hook_menu().
user_external_invite_operations_page Page callback for admin/people/invite/operations.
user_external_invite_page Page callback for admin/people/invite.
user_external_invite_pending_invites_form Build table showing pending invites.
user_external_invite_pending_invites_form_submit Submit callback for user_external_invite_pending_invites_form().
user_external_invite_permission Implements hook_permission().
user_external_invite_resend_invites Callback to resend invites.
user_external_invite_system_info_alter Implements hook_system_info_alter().
user_external_invite_tokens Implements hook_tokens().
user_external_invite_token_info Implements hook_token_info().
user_external_invite_user_external_invite_excluded_roles Implements hook_user_external_invite_excluded_roles().
user_external_invite_user_login Implements hook_user_login().
user_external_invite_user_role_delete Implements hook_user_role_delete().
_user_external_invite_add_invite Adds invite to database and sends email.
_user_external_invite_calculate_hash Calculates the token based on $rid, $email, and $expire.
_user_external_invite_change_invite_status Change status of invite in {user_external_invite} table.
_user_external_invite_dehash Determine whether invite is accepted based on hash.
_user_external_invite_from_email Returns from address used to send mailing.
_user_external_invite_load_entity Loads external invite from entity from email.
_user_external_invite_load_invite Loads an invite.
_user_external_invite_load_related_invites Load invites that were invited at the same time.
_user_external_invite_pending_invites Get and return pending invites.
_user_external_invite_role_name_from_rid Get role name from role ID.
_user_external_invite_send_invite Sends email with special token-login link.
_user_external_invite_send_inviter_confirmation Sends confirmation to inviter that the invite was sent out.
_user_external_invite_send_invite_accepted_mail Sends accepted invitation email with login link and extra help.
_user_external_invite_send_invite_mail Sends invitation email with token login link.
_user_external_invite_site_name Return site name.