You are here

autologout.module in Automated Logout 7.2

Used to automagically log out a user after a preset time, AjK May 2006

Updated for core 7.x This version uses Javascript Timer 7.x-1.x for the timer. It will still work without Javascript Timer, but will not display the timer.

  • jrglasgow Feb 2008
  • mradcliffe May 2011

File

autologout.module
View source
<?php

/**
 * @file
 * Used to automagically log out a user after a preset time, AjK May 2006
 *
 * Updated for core 7.x
 * This version uses Javascript Timer 7.x-1.x for the timer. It will still
 * work without Javascript Timer, but will not display the timer.
 *      - jrglasgow Feb 2008
 *      - mradcliffe May 2011
 */

/**
 * Provide Drupal site administrators with a method to ensure site users
 * are automatically logged out after a preset time.
 */
if (!defined("AUTOLOGOUT_DEBUG")) {
  define("AUTOLOGOUT_DEBUG", 0);

  // 0 - disabled, 1 - enabled
}

/**
 * Note, these defaults are for the "out of the box" settings. Most
 * of these can be set by the site admin and once the site admin does
 * change them, these are no longer used (i.e. if variable_get() returns
 * a valid value these are ignored.
 *
 * @see _autologout_local_settings()
 */
class autologout_default_settings {
  var $enabled = FALSE;

  // Initially disabled
  var $timeout = 3600;

  // default 1 hour
  var $refresh_delta = 0;

  // force browser refresh (timeout+refresh_delta)
  var $use_watchdog = 1;

  // log auto-logouts to watchdog
  var $block_title = 'automatic logout in';

}

/**
 * Implements hook_permission().
 */
function autologout_permission() {
  _autologout_debug("autologout_permission()");
  return array(
    'administer autologout' => array(
      'title' => t('Administer Automated Logout'),
    ),
  );
}

/**
 * Implements hook_block_info().
 */
function autologout_block_info() {
  $blocks = array();
  _autologout_debug("autologout_block_info()");
  $blocks['info'] = array(
    'info' => t('Automated Logout info'),
  );
  return $blocks;
}

/**
 * Implements hook_block_view().
 */
function autologout_block_view($delta = '') {
  global $user;
  $block = array();
  _autologout_debug("autologout_block_view()");
  if (!_autologout_local_settings('enabled')) {
    return;
  }
  if ($user->uid < 1 || _autologout_by_role()) {
    _autologout_debug("  block doesn't apply");
    return;
  }
  $block['subject'] = filter_xss_admin(_autologout_local_settings('block_title'));
  $refresh = (int) _autologout_local_settings('refresh_delta');
  $timeout = _autologout_local_settings('timeout');
  $redirect_url = filter_xss_admin(_autologout_local_settings('redirect_url'));
  if (module_exists('jstimer') && module_exists('jst_timer')) {
    $block['content'] = theme('jstimer', array(
      'widget_name' => 'jst_timer',
      'widget_args' => array(
        'interval' => $timeout,
        'tc_redir' => $redirect_url,
        'threshold' => 5,
        'format_txt' => filter_xss_admin(variable_get('autologout_jstimer_format', '%hours%:%mins%:%secs%')),
        'complete' => t('You have been automatically logged out due to inactivity.') . ' ' . ($refresh ? ' ' . t('This page will refresh in %refresh seconds.', array(
          '%refresh' => $refresh,
        )) : ''),
        'no_js_txt' => t('You will be logged out in !time if this page is not refreshed before then.', array(
          '!time' => format_interval($timeout),
        )),
      ),
    ));
  }
  else {
    $block['content'] = t('You will be logged out in !time if this page is not refreshed before then.', array(
      '!time' => format_interval($timeout),
    ));
  }
  $logout_message = filter_xss_admin(_autologout_local_settings('logout_message'));
  if ($logout_message != '') {
    $block['content'] .= '<span style="display:none" class="tc_msg">' . $logout_message . '</span>';
  }
  $block['content'] .= '</span>';
  return $block;
}

/**
 * Implements hook_block_configure().
 */
function autologout_block_configure($delta = '') {
  $block = array();
  _autologout_debug("autologout_block_configure()");
  if (!_autologout_local_settings('enabled')) {
    return;
  }
  if (module_exists('jstimer')) {
    if (!module_exists('jst_timer')) {
      drupal_set_message(t('The "Widget: timer" module must also be enabled for the dynamic countdown to work in the automated logout block.'), 'error');
    }
    if (variable_get('jstimer_js_load_option', 0) != 1) {
      drupal_set_message(t('The Javascript timer module\'s "Javascript load options" setting should be set to "Every page" for the dynamic countdown to work in the automated logout block.'), 'error');
    }
  }
  return $block;
}

/**
 * Implements hook_help().
 */
function autologout_help($path, $arg) {
  switch ($path) {
    case 'admin/help#autologout':
      $output = '<p>' . t('The <em>Automated Logout</em> module allows you to force users to log out after a given amount of time. You can configure this in the <a href="@alo_settings">Automated logout settings</a> page.', array(
        '@alo_settings' => url('admin/config/people/autologout'),
      )) . '</p>';
      $output .= '<p>' . t('If you have the <a href="@jstimer">Javascript timer module</a> enabled, the <a href="@automatedlogoutblock">Automated Logout block</a> will have a live countdown timer.', array(
        '@automatedlogoutblock' => url('admin/structure/block'),
        '@jstimer' => 'http://drupal.org/project/jstimer',
      )) . '</p>';
      return $output;
  }
}

/**
 * Implements hook_boot().
 * NOTE: Do as little as possible here. A lot of modules are not loaded at this point.
 */
function autologout_boot() {
  global $user;
  if ($user->uid < 1) {
    return;
  }
  if (!isset($_SESSION['autologout_hits'])) {
    $_SESSION['autologout_hits'] = array();
  }
  $_SESSION['autologout_hits'][] = REQUEST_TIME;
}

/**
 * Implements hook_init().
 */
function autologout_init() {
  global $user;
  if ($user->uid < 1 || _autologout_by_role()) {
    _autologout_debug("block doesn't apply");
    return;
  }
  if (_autologout_local_settings('enabled')) {
    $timeout = (int) _autologout_local_settings('timeout');
    $nowtime = REQUEST_TIME;
    if (!isset($_SESSION['lastaccess'])) {
      $_SESSION['lastaccess'] = $nowtime;
    }

    // update lastaccess from any cache hits which wouldn't have been processed by hook_init()
    if (isset($_SESSION['autologout_hits'])) {
      foreach ($_SESSION['autologout_hits'] as $hit) {
        if ((int) $hit - (int) $_SESSION['lastaccess'] > 0) {

          // if hit wouldn't have timedout, update lastaccess
          if ((int) $hit - (int) $_SESSION['lastaccess'] < $timeout) {

            // rebase lastaccess
            $_SESSION['lastaccess'] = $hit;
          }
        }
      }
    }

    // now normal processing because the cache hits have been accounted for.
    if ($nowtime - (int) $_SESSION['lastaccess'] < $timeout) {

      // the timeout has not yet occurred.
      $_SESSION['lastaccess'] = $nowtime;
      $_SESSION['autologout_hits'] = array(
        $nowtime,
      );
      $refresh = (int) _autologout_local_settings('refresh_delta');
      if ($refresh >= 0) {
        $force_refresh = $timeout + $refresh;
        $this_header = "<meta http-equiv=\"refresh\" content=\"{$force_refresh};\" />";
        $element = array(
          '#type' => 'markup',
          '#markup' => $this_header,
        );
        $this_head = drupal_add_html_head($element, 'refresh-header');
      }
    }
    else {

      // timeout occured, logout and end session
      unset($_SESSION['lastaccess']);

      // code from core(user.pages.inc), can't use it directly because we need need a custom goto
      watchdog('user', 'Session closed for %name.', array(
        '%name' => $user->name,
      ));
      module_invoke_all('user_logout', $user);

      // Destroy the current session, and reset $user to the anonymous user.
      session_destroy();
      $redirect_url = filter_xss_admin(_autologout_local_settings('redirect_url'));
      if ($redirect_url != '') {
        drupal_goto($redirect_url);
      }
      else {
        drupal_goto('autologout/logout', drupal_get_destination());
      }
      return;
    }
  }
}

/**
 * Implements hook_user_login().
 */
function autologout_user_login(&$edit, $account) {
  _autologout_debug("autologout_user_login()");
  if (empty($account->uid)) {
    return;

    // UID 0 not appliciable
  }

  // one session checking
  $return = _autologout_check_one_session();
  if ($return) {
    _autologout_invalidate_other_sessions($account);
  }
  $_SESSION['lastaccess'] = REQUEST_TIME;
}

/**
 * Implements hook_user_load().
 */
function autologout_user_load($users) {
  _autologout_debug("autologout_user_load()");
  foreach ($users as $uid => $account) {
    $uids[] = $uid;
  }
  $query = db_select('autologout');
  $query
    ->fields('autologout')
    ->condition('uid', $uids, 'IN');
  $result = $query
    ->execute();
  foreach ($result as $row) {
    $users[$row->uid]->autologout = (int) $row->setting;
  }
}

/**
 * Implements hook_form_FORM_ID_alter().
 */
function autologout_form_user_profile_form_alter(&$form, &$form_state) {
  _autologout_debug("autologout_form_user_profile_form_alter()");
  $account = $form['#user'];
  if (empty($account->uid)) {
    return;

    // UID 0 not applicable.
  }
  if (_autologout_user_in_by_user_role() && $form['#user_category'] == 'account') {
    $form['autologout'] = array(
      '#type' => 'fieldset',
      '#title' => t('Automated Logout'),
      '#weight' => 10,
      '#collapsible' => TRUE,
    );
    $form['autologout']['autologout'] = array(
      '#type' => 'checkbox',
      '#title' => t('Disable inactivity Automated Logout'),
      '#default_value' => isset($account->autologout) ? $account->autologout : 0,
    );
  }
}

/**
 * Implements hook_user_update().
 */
function autologout_user_update(&$edit, $account, $category) {
  _autologout_debug("autologout_user_update()");
  if (empty($account->uid)) {
    return;

    // UID 0 not appliciable
  }
  if (_autologout_user_in_by_user_role($account)) {
    if (isset($edit['autologout'])) {
      db_delete('autologout')
        ->condition('uid', $account->uid)
        ->execute();
      db_insert('autologout')
        ->fields(array(
        'uid' => $account->uid,
        'setting' => $edit['autologout'],
      ))
        ->execute();
      unset($edit['autologout']);
    }
  }
}

/**
 * Implements hook_user_delete().
 */
function autologout_user_delete($account) {
  _autologout_debug("autologout_user_delete()");
  if (empty($account->uid)) {
    return;

    // UID 0 not appliciable
  }
  db_delete('autologout')
    ->condition('uid', $account->uid)
    ->execute();
}

// if a user is entering data then don't log them out or
// they will loose their work. There's nothing more
// frustrating than entering alot of info only for a
// website to throw it away!

/**
 * Implements hook_node_update().
 */
function autologout_node_update($node) {
  _autologout_debug("autologout_node_update()");
  $_SESSION['lastaccess'] = REQUEST_TIME;
}

/**
 * Implements hook_node_insert().
 */
function autologout_node_insert($node) {
  _autologout_debug("autologout_node_insert()");
  $_SESSION['lastaccess'] = REQUEST_TIME;
}

/**
 * Implements hook_node_delete().
 */
function autologout_node_delete($node) {
  _autologout_debug("autologout_node_delete()");
  $_SESSION['lastaccess'] = REQUEST_TIME;
}

/**
 * Implements hook_node_validate().
 */
function autologout_node_validate($node) {
  _autologout_debug("autologout_node_validate()");
  $_SESSION['lastaccess'] = REQUEST_TIME;
}

/**
 * Implements hook_node_prepare().
 */
function autologout_node_prepare($node) {
  _autologout_debug("autologout_node_prepare()");
  $_SESSION['lastaccess'] = REQUEST_TIME;
}

/**
 * Implements hook_menu().
 */
function autologout_menu() {
  $items['admin/config/people/autologout'] = array(
    'title' => 'Automated logout',
    'description' => 'Manage the Automated Logout features',
    'page callback' => 'drupal_get_form',
    'page arguments' => array(
      'autologout_admin_settings',
    ),
    'access arguments' => array(
      'administer autologout',
    ),
    'file' => 'autologout.admin.inc',
    'type' => MENU_NORMAL_ITEM,
  );
  $items['autologout/logout'] = array(
    'title' => 'Automated logout',
    'page callback' => 'autologout_logout',
    'access callback' => TRUE,
    'type' => MENU_CALLBACK,
  );
  return $items;
}

/* ========================= */

/*  Helper functions follow  */

/* ========================= */

/**
 * _autologout_by_role()
 *
 * Is the user in a role that we exclude from features defined by this module
 *
 * @param $passed_user
 *   A drupal "user" object or default FALSE (use global $user)
 *
 * @return bool
 *  TRUE if user s to be excluded, FALSE otherwise
 */
function _autologout_by_role($passed_user = NULL) {
  global $user;
  if ($passed_user === NULL) {
    $local_user = $user;
  }
  else {
    $local_user = $passed_user;
  }
  foreach (user_roles(TRUE) as $role) {
    switch (_autologout_local_settings($role)) {
      case 0:

        // Enforce for all in this role
        break;
      case 1:

        // Exclude all users in this role
        if (in_array($role, array_values($local_user->roles))) {
          return TRUE;
        }
        break;
      case 2:

        // Exclude user if user set to disable
        if (in_array($role, array_values($local_user->roles))) {
          if (isset($local_user->autologout) && $local_user->autologout != 0) {
            return TRUE;
          }
        }
        break;
    }
  }
  return FALSE;
}
function _autologout_user_in_by_user_role($passed_user = NULL) {
  global $user;
  if ($passed_user === NULL) {
    $local_user = $user;
  }
  else {
    $local_user = $passed_user;
  }

  // some modules invoke hook_user without fully loading the $account object
  // we may be able to remove this if those modules are fixed.
  if (!is_array($local_user->roles)) {
    return FALSE;
  }
  foreach (user_roles(TRUE) as $role) {
    if (_autologout_local_settings($role) == 2 && in_array($role, array_values($local_user->roles))) {
      return TRUE;
    }
  }
  return FALSE;
}

/**
 * _autologout_local_settings($name = FALSE)
 *
 * Used to get a modules "settings" value. Note, the "out of box"
 * settings are defined by the place holder class
 *
 * @see autologout_default_settings()
 *
 * @param $name
 *  A string of the variable name to get or FALSE return all variables as array
 *
 * @return mixed
 *   array of all variables (if param was false)
 *   string the named variabled value
 */
function _autologout_local_settings($name = FALSE) {
  $defaults = (array) new autologout_default_settings();
  if (!($settings = variable_get('autologout', FALSE))) {
    $settings = $defaults;
  }
  if (FALSE != $name) {
    if (!isset($settings[$name]) && isset($defaults[$name])) {
      return $defaults[$name];
    }
    elseif (!isset($settings[$name]) && !isset($defaults[$name])) {
      return NULL;
    }
    else {
      return $settings[$name];
    }
  }
  else {
    return $settings;

    // return the entire array
  }
}
function _autologout_debug($s) {
  if (defined("AUTOLOGOUT_DEBUG") && AUTOLOGOUT_DEBUG) {
    error_log($s);
  }
}
function _autologout_check_one_session() {
  global $user;
  $return = 1;
  $values = variable_get('autologout_one_session', 0);
  foreach ($user->roles as $role) {
    $return = $return && $values[$role];
  }
  return $return;
}
function _autologout_invalidate_other_sessions($account) {

  // Do nothing if anonymous.
  if ($account->uid == 0) {
    return;
  }

  // check to see if the user is already logged in somewhere else
  // if so deactivate that login and let the user know that the
  // other session has been deactivated
  $result = db_select('sessions', 'ss')
    ->fields('ss')
    ->condition('ss.uid', $account->uid)
    ->condition('ss.sid', session_id(), '<>')
    ->countQuery()
    ->execute()
    ->fetchField();
  if ($result != 0) {

    // Send the message to the current session
    drupal_set_message(t('You are only allowed 1 open session at a time. Your other session has been terminated.'), 'error');

    // Logout OTHER sessions, and send them their message
    $other_session_msg = t('You have been automatically logged out.  You are only allowed 1 open session at a time, and another open session was detected.');
    $other_session_msg = 'messages|' . serialize(array(
      'error' => array(
        $other_session_msg,
      ),
    ));
    db_update('sessions')
      ->fields(array(
      'uid' => '0',
      'session' => $other_session_msg,
    ))
      ->condition('uid', $account->uid, '=')
      ->condition('sid', session_id(), '<>')
      ->execute();

    // Write a watchdog message for the site admin.
    watchdog('Automated Logout', 'One Session automatically logged out user.', array(), WATCHDOG_WARNING);
  }
}
function autologout_logout() {
  $xtra = filter_xss_admin(_autologout_local_settings('logout_message'));
  if ($xtra == '') {
    drupal_set_message(t('You have been automatically logged out due to inactivity.'));
    drupal_goto();
  }
  else {
    drupal_set_message(t($xtra));
    drupal_goto();
  }
}

Functions

Classes

Namesort descending Description
autologout_default_settings Note, these defaults are for the "out of the box" settings. Most of these can be set by the site admin and once the site admin does change them, these are no longer used (i.e. if variable_get() returns a valid value these are ignored.