You are here

sms_user.module in SMS Framework 5

Provides integration between the SMS Framework and Drupal users.

File

modules/sms_user/sms_user.module
View source
<?php

/**
 * @file
 * Provides integration between the SMS Framework and Drupal users.
 */
define('SMS_USER_PENDING', 1);
define('SMS_USER_CONFIRMED', 2);

/**
 * Send a message to a user.
 */
function sms_user_send($uid, $message) {
  $account = user_load($uid);
  if ($account->sms_user[0]['status'] == 2) {
    return sms_send($account->sms_user[0]['number'], $message, $account->sms_user[0]['gateway']);
  }
  else {
    return FALSE;
  }
}

/**
 * Returns the uid of the owner of a number.
 */
function sms_user_get_uid($number, $status = NULL) {
  $sql = "SELECT uid FROM {sms_user} WHERE number = '%s'";
  $arguments = array(
    $number,
  );
  if (isset($status)) {
    $sql .= " AND status = %d";
    $arguments[] = $status;
  }
  $data = db_fetch_array(db_query($sql, $arguments));
  return $data['uid'];
}

/**
 * Implementation of hook_menu().
 */
function sms_user_menu($may_cache) {
  global $user;
  $items = array();
  if ($may_cache) {
    $items[] = array(
      'path' => 'admin/smsframework/sms_user',
      'title' => t('SMS User'),
      'description' => t('Edit options for SMS and user integration.'),
      'callback' => 'drupal_get_form',
      'callback arguments' => array(
        'sms_user_admin_settings',
      ),
      'access' => user_access('administer sms_user'),
      'type' => MENU_NORMAL_ITEM,
    );
  }
  else {
    if (arg(0) == 'user' && is_numeric(arg(1))) {
      $items[] = array(
        'path' => 'user/' . arg(1) . '/mobile',
        'title' => t('Mobile settings'),
        'callback' => 'sms_user_settings',
        'callback arguments' => array(
          'sms_user_form',
        ),
        'access' => user_access('administer users') || $user->uid == arg(1),
        'type' => MENU_LOCAL_TASK,
      );
    }
  }
  return $items;
}

/**
 * Implementation of hook_perm()
 */
function sms_user_perm() {
  return array(
    'administer sms_user',
  );
}

/**
 * Implementation of hook_sms_send().
 */
function sms_user_sms_send(&$number, &$message, &$options, &$gateway) {
  if (variable_get('sms_user_sleep', 1) && ($uid = sms_user_get_uid($number))) {
    $account = user_load(array(
      'uid' => $uid,
      'status' => 1,
    ));
    if (!empty($account->sms_user['sleep_enabled']) && _sms_user_sleep_active($account)) {
      unset($gateway['send']);
      watchdog('sms', t('Message was not sent to @user due to sleep settings.', array(
        '@user' => $account->name,
      )));
    }
  }
}
function _sms_user_sleep_active($account) {
  $current_hour = date('G');
  if ($account->sms_user['sleep_start_time'] <= $current_hour && $account->sms_user['sleep_end_time'] > $current_hour) {
    return TRUE;
  }
  return FALSE;
}

/**
 * Menu callback; provides the forms for adding and confirming a user's mobile number.
 */
function sms_user_settings($register = FALSE) {
  $account = user_load(array(
    'uid' => arg(1),
  ));
  switch (isset($account->sms_user) ? $account->sms_user[0]['status'] : 0) {
    case 0:
      $output = drupal_get_form('sms_user_settings_add_form', $account);
      break;
    case SMS_USER_PENDING:
      $output = drupal_get_form('sms_user_settings_confirm_form', $account);
      break;
    case SMS_USER_CONFIRMED:
      $output = drupal_get_form('sms_user_settings_reset_form', $account);
      break;
  }
  if (variable_get('sms_user_sleep', 1)) {
    $output .= drupal_get_form('sms_user_settings_sleep_form', $account);
  }
  return $output;
}
function sms_user_settings_add_form($account) {
  $form = sms_send_form();
  $form['submit'] = array(
    '#type' => 'submit',
    '#value' => t('Confirm number'),
  );
  return $form;
}
function sms_user_settings_add_form_validate($form_id, $form_values) {
  if ($error = sms_user_validate_number($form_values['number'])) {
    form_set_error('number', $error);
  }
}
function sms_user_settings_add_form_submit($form_id, $form_values, $account = NULL) {
  if (!$account) {
    $account = user_load(array(
      'uid' => arg(1),
    ));
  }
  sms_user_send_confirmation($account, $form_values['number'], $form_values['gateway']);
}
function sms_user_settings_confirm_form($account) {
  $form['number'] = array(
    '#type' => 'item',
    '#title' => t('Number'),
    '#value' => $account->sms_user[0]['number'],
  );
  $form['confirm_code'] = array(
    '#type' => 'textfield',
    '#title' => t('Confirmation code'),
    '#description' => t('Enter the confirmation code that was sent to your phone.'),
    '#size' => 4,
    '#maxlength' => 4,
  );
  $form['submit'] = array(
    '#type' => 'submit',
    '#value' => t('Confirm number'),
  );
  $form['reset'] = array(
    '#type' => 'submit',
    '#value' => t('Delete & start over'),
  );
  return $form;
}
function sms_user_settings_confirm_form_validate($form_id, $form_values) {
  if ($form_values['op'] == t('Confirm number')) {
    $account = user_load(array(
      'uid' => arg(1),
    ));
    if ($form_values['confirm_code'] != $account->sms_user[0]['code']) {
      form_set_error('confirm_code', t('The confirmation code is invalid.'));
    }
  }
}
function sms_user_settings_confirm_form_submit($form_id, $form_values) {
  $account = user_load(array(
    'uid' => arg(1),
  ));
  if ($form_values['op'] == t('Delete & start over')) {
    sms_user_delete($account->uid, 0);
  }
  else {
    $data[0] = array(
      'number' => $account->sms_user[0]['number'],
      'status' => SMS_USER_CONFIRMED,
      'gateway' => $account->sms_user[0]['gateway'],
    );
    user_save($account, array(
      'sms_user' => $data,
    ), 'mobile');
  }
}
function sms_user_settings_reset_form($account) {
  $form['sms_user']['number'] = array(
    '#type' => 'item',
    '#title' => t('Your number'),
    '#value' => $account->sms_user[0]['number'],
    '#description' => t('Your number has been confirmed.'),
  );
  $form['reset'] = array(
    '#type' => 'submit',
    '#value' => t('Delete & start over'),
  );
  return $form;
}
function sms_user_settings_reset_form_submit($form_id, $form_values) {
  $account = user_load(array(
    'uid' => arg(1),
  ));
  sms_user_delete($account->uid, 0);
  drupal_set_message(t('Your mobile information has been removed'), 'status');
}
function sms_user_settings_sleep_form($account) {
  $form['sleep'] = array(
    '#type' => 'fieldset',
    '#title' => t('Sleep Time'),
    '#collapsible' => TRUE,
  );
  $form['sleep']['sleep_enabled'] = array(
    '#type' => 'checkbox',
    '#title' => t('Disable messages between these hours'),
    '#description' => t('If enabled, you will not receive messages between the specified hours.'),
    '#default_value' => isset($account->sms_user['sleep_enabled']) ? $account->sms_user['sleep_enabled'] : NULL,
  );

  // Determine whether to use the 24-hour or 12-hour clock based on site settings
  if (strpos(variable_get('date_format_short', 'm/d/Y - H:i'), 'g')) {
    $format = 'g A';
  }
  else {
    $format = 'H:00';
  }

  // Build the list of options based on format
  $hour = 0;
  while ($hour < 24) {
    $options[$hour] = date($format, mktime($hour));
    $hour++;
  }
  $form['sleep']['sleep_start_time'] = array(
    '#type' => 'select',
    '#multiple' => FALSE,
    '#options' => $options,
    '#default_value' => isset($account->sms_user['sleep_start_time']) ? $account->sms_user['sleep_start_time'] : NULL,
  );
  $form['sleep']['sleep_end_time'] = array(
    '#type' => 'select',
    '#multiple' => FALSE,
    '#options' => $options,
    '#default_value' => isset($account->sms_user['sleep_end_time']) ? $account->sms_user['sleep_end_time'] : NULL,
  );
  $form['sleep']['save'] = array(
    '#type' => 'submit',
    '#value' => t('Save'),
  );
  return $form;
}
function sms_user_settings_sleep_form_submit($form_id, $form_values) {
  $account = user_load(array(
    'uid' => arg(1),
  ));
  $data = $account->sms_user;
  $data['sleep_enabled'] = $form_values['sleep_enabled'];
  $data['sleep_start_time'] = $form_values['sleep_start_time'];
  $data['sleep_end_time'] = $form_values['sleep_end_time'];
  user_save($account, array(
    'sms_user' => $data,
  ), 'mobile');
  drupal_set_message(t('The changes have been saved.'), 'status');
}
function sms_user_send_confirmation($account, $number, $options) {
  $code = rand(1000, 9999);
  $number = sms_formatter($number);
  $data[0] = array(
    'number' => $number,
    'status' => SMS_USER_PENDING,
    'code' => $code,
    'gateway' => $options,
  );
  user_save($account, array(
    'sms_user' => $data,
  ), 'mobile');
  sms_send($number, _sms_user_confirm_message($code), $options);
}
function sms_user_validate_number(&$number) {
  if ($error = sms_validate_number($number)) {
    return $error;
  }
  elseif (sms_user_get_uid($number, SMS_USER_CONFIRMED)) {
    return t('This phone number is already registered to another user.');
  }
}

/**
 * Deletes a user's mobile information from the database
 * 
 * @param $uid
 *   The uid of the user who's data is to be removed.
 * 
 * @param $delta
 *   The delta value of the number. Defaults to 'all' which will delete all numbers.
 */
function sms_user_delete($uid, $delta = 'all') {
  $db_args = array(
    $uid,
  );
  if (is_numeric($delta)) {
    $delta_where = 'AND delta = %d';
    $db_args[] = $delta;
  }
  db_query("DELETE FROM {sms_user} WHERE uid = %d {$delta_where}", $db_args);
}
function sms_user_user($op, &$edit, &$account, $category = NULL) {
  switch ($op) {
    case 'load':
      return sms_user_load($edit, $account, $category);
    case 'update':
    case 'insert':
      return sms_user_save($edit, $account, $category);
    case 'register':
      return sms_user_register();
    case 'delete':
      return sms_user_delete($account->uid);
    case 'validate':
      if (!empty($edit['sms_user']) && (variable_get('sms_user_registration_form', 0) == 2 || strlen($edit['sms_user'][0]['number']))) {
        if ($error = sms_user_validate_number($edit['sms_user'][0]['number'])) {
          form_set_error('sms_user][0][number', $error);
        }
      }
      break;
    case 'login':

      // Check if first it's the user's first time logging in.
      if (!$account->access && $account->sms_user[0]['number']) {
        sms_user_send_confirmation($account, $account->sms_user[0]['number'], $account->sms_user[0]['gateway']);
        drupal_set_message(t('A confirmation message has been sent to your mobile phone. Please !link.', array(
          '!link' => l(t('confirm your number'), 'user/' . $account->uid . '/mobile'),
        )), 'status');
      }
      break;
  }
}
function sms_user_load(&$edit, &$account, $category) {
  $result = db_query("SELECT number, delta, status, code, gateway FROM {sms_user} WHERE uid = %d", $account->uid);
  while ($data = db_fetch_array($result)) {
    if ($data) {
      $account->sms_user[$data['delta']] = array(
        'number' => $data['number'],
        'status' => $data['status'],
        'code' => $data['code'],
        'gateway' => unserialize($data['gateway']),
      );
    }
  }
}
function sms_user_save(&$edit, &$account, $category) {
  if (($category == 'mobile' || $category == 'account') && $edit['sms_user'][0]['number']) {
    foreach ($edit['sms_user'] as $delta => $number) {
      if (is_numeric($delta)) {
        $db_values = array(
          $number['number'],
          $number['status'],
          isset($number['code']) ? $number['code'] : NULL,
          serialize($number['gateway']),
        );
        if (isset($account->sms_user[$delta])) {
          db_query("UPDATE {sms_user} SET number = '%s', status = %d, code = '%s', gateway = '%s'\n            WHERE uid = %d AND delta = %d", array_merge($db_values, array(
            $account->uid,
            $delta,
          )));
        }
        else {
          db_query("INSERT INTO {sms_user} (number, status, code, gateway, uid, delta)\n            VALUES ('%s', %d, '%s', '%s', %d, %d)", array_merge($db_values, array(
            $account->uid,
            $delta,
          )));
        }
      }
    }
    $edit['sms_user'][0] = NULL;
  }
}
function _sms_user_confirm_message($code) {
  $text_format = variable_get('sms_user_confirmation_message', '[site-name] confirmation code: [confirm-code]');
  $text = token_replace_multiple($text_format, array(
    'sms_user' => array(
      'confirm-code' => $code,
    ),
  ));
  return $text;
}
function sms_user_register() {
  if (variable_get('sms_user_registration_form', 0)) {
    $form['sms_user'] = array(
      '#type' => 'fieldset',
      '#title' => t('Mobile settings'),
      '#description' => t('You will receive a message to confirm your mobile information upon login.'),
      '#collapsible' => TRUE,
      '#tree' => TRUE,
    );
    $required = FALSE;
    if (variable_get('sms_user_registration_form', 0) == 2) {
      $required = TRUE;
    }
    $form['sms_user'][0] = sms_send_form($required);
    $form['sms_user'][0]['status'] = array(
      '#type' => 'value',
      '#value' => 1,
    );
    return $form;
  }
}
function sms_user_admin_settings() {
  $form['sms_user_registration_form'] = array(
    '#type' => 'radios',
    '#title' => t('Show mobile fields during user registration'),
    '#description' => t('Specify if the site should collect mobile information during registration.'),
    '#options' => array(
      t('Disabled'),
      t('Optional'),
      t('Required'),
    ),
    '#default_value' => variable_get('sms_user_registration_form', 0),
  );
  $form['sms_user_confirmation_message'] = array(
    '#type' => 'textfield',
    '#title' => t('Confirmation message format'),
    '#default_value' => variable_get('sms_user_confirmation_message', '[site-name] confirmation code: [confirm-code]'),
    '#description' => t('Specify the format for confirmation messages. Keep this as short as possible.'),
    '#size' => 40,
    '#maxlength' => 255,
  );
  $form['tokens'] = array(
    '#type' => 'fieldset',
    '#title' => t('Available replacement patterns'),
    '#collapsible' => TRUE,
    '#collapsed' => TRUE,
  );
  $form['tokens']['content']['#value'] = theme('token_help', 'sms_user');
  $form['sms_user_sleep'] = array(
    '#type' => 'checkbox',
    '#title' => t('Enable sleep hours'),
    '#description' => t('If checked, users will be able to specifiy hours during which they will not receive messages from the site.'),
    '#default_value' => variable_get('sms_user_sleep', 1),
  );
  return system_settings_form($form);
}

/**
 * Implementation of hook_token_values()
 */
function sms_user_token_values($type, $object = NULL, $options = array()) {
  global $user;
  $values = array();
  switch ($type) {
    case 'sms_user':
      $values['confirm-code'] = $object['confirm-code'];
      $values['mobile-url'] = url("user/{$user->uid}/mobile", NULL, NULL, TRUE);
      break;
  }
  return $values;
}

/**
 * Implementation of hook_token_list()
 */
function sms_user_token_list($type = 'all') {
  if ($type = 'sms_user') {
    $tokens['sms_user']['confirm-code'] = t('The mobile confirmation code for the user.');
    $tokens['sms_user']['mobile-url'] = t('The URL for the user\'s mobile settings page.');
  }
  return $tokens;
}

/**
 * Implementation of hook_sms_incoming().
 */
function sms_user_sms_incoming($op, $number, $message) {
  switch ($op) {
    case 'pre process':
      sms_user_authenticate($number);
      break;
    case 'post process':
      sms_user_logout();
      break;
  }
}

/**
 * Authenticate a user based on mobile number.
 * 
 * @param $number
 *   The number to authenticate against. For security, this should only be
 *   provided by incoming messages, not through user input.
 */
function sms_user_authenticate($number) {
  global $user;
  $uid = sms_user_get_uid($number);
  if ($account = user_load(array(
    'uid' => $uid,
    'status' => 1,
  ))) {
    $user = $account;
    watchdog('sms', t('%name was authenticated using SMS.', array(
      '%name' => $user->name,
    )));
    return $user;
  }
}
function sms_user_logout() {
  global $user;

  // Destroy the current session:
  session_destroy();
  $user = drupal_anonymous_user();
}

/*
 * Implementation of hook_sms_receive()
 * Match an incoming message to a user
 */
function sms_user_sms_receive(&$node, $sms_message) {

  // is $sms_message['from'] a general case?
  $number = $sms_message['from'];

  // how is this be handled with international numbers? do we need to remove +44 for UK?
  // should be able to test when we get Chinese SIM card
  if (substr($number, 0, 1) == '1') {

    // remove leading '1', sms_user doesn't store it...
    $number = substr($number, 1);
  }
  $uid = sms_user_get_uid($number);
  $account = user_load(array(
    'uid' => $uid,
  ));
  $node->uid = $account->uid;
  $node->name = $account->name;
}

Functions

Constants

Namesort descending Description
SMS_USER_CONFIRMED
SMS_USER_PENDING @file Provides integration between the SMS Framework and Drupal users.