You are here

autologout.module in Automated Logout 6.2

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

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

File

autologout.module
View source
<?php

/**
 * @file
 * Used to automagically log out a user after a preset time, AjK May 2006
 *
 * Updated for core 6.x
 * This version uses Javascript timer 6.x for the timer. It will
 * still work without Javascript timer, but will not display the timer.
 */

/**
 * 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
}

/* {{{ autologout_info() */

/**
 * Implementation of hook_info().
 */
function autologout_info() {
  return array(
    'autologout' => array(
      'name' => t('Automated logout'),
      'module' => 'autologout',
      'description' => t('Manage the automated logout features'),
    ),
  );
}

/* }}} */

/* {{{ autologout_perm() */

/**
 * Implementation of hook_perm().
 */
function autologout_perm() {
  _autologout_debug("autologout_perm()");
  return array(
    'administer autologout',
  );
}

/* }}} */

/* {{{ autologout_block() */

/**
 * Implementation of hook_block().
 */
function autologout_block($op = 'list', $delta = 0, $edit = array()) {
  global $user;

  // Turn block off if module turned off, anonymous user, or exluded by role.
  if (!_autologout_local_settings('enabled') || $user->uid == 0 || _autologout_exclude_by_role()) {
    return;
  }
  switch ($op) {
    case 'list':
      $block[0]['info'] = t('Automated Logout info');
      return $block;
      break;
    case 'configure':
      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');
        }
      }
    case 'save':
      break;
    case 'view':
      $refresh = (int) _autologout_local_settings('refresh_delta');
      $timeout = _autologout_local_settings('timeout');
      $block['subject'] = filter_xss_admin(_autologout_local_settings('block_title'));
      if (module_exists('jstimer') && module_exists('jst_timer')) {
        $logout_message = filter_xss_admin(_autologout_local_settings('logout_message'));
        $block['content'] = theme('jstimer', 'jst_timer', array(
          'interval' => $timeout,
          'threshold' => 5,
          'format_txt' => filter_xss_admin(variable_get('autologout_jstimer_format', '%hours%:%mins%:%secs%')),
          'complete' => $logout_message . ' ' . ($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>';
      }
      break;
  }
  return $block;
}

/* }}} */

/* {{{ autologout_help() */

/**
 * Implementation of 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/settings/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/build/block'),
        '@jstimer' => 'http://drupal.org/project/jstimer',
      )) . '</p>';
      return $output;
  }
}

/* }}} */

/**
 * Implementation of hook_boot().
 * NOTE: Do as little as possible here. A lot of modules are not loaded at this point.
 */
function autologout_boot() {

  // Do not start session for anonymous users,
  // as it would break Pressflow's lazy sessions.
  // (Copy of user_is_anonymous(), which is undefined
  // when called from hook_boot() by drush.)
  if (!$GLOBALS['user']->uid || !empty($GLOBALS['menu_admin'])) {
    return;
  }
  if (!isset($_SESSION['autologout_hits'])) {
    $_SESSION['autologout_hits'] = array();
  }
  $_SESSION['autologout_hits'][] = time();
}

/* {{{ autologout_init() */

/**
 * Implementation of hook_init().
 */
function autologout_init() {

  // We have to perform a load in order to assure that the $user->autologout bits are present.
  global $user;
  if ($user->uid) {

    // $user gets modified by reference.
    autologout_user('load', array(), $user);
  }
  else {
    $user->autologout = 0;
    return;
  }

  // Check for enabled autologout and excluded users.
  if (_autologout_local_settings('enabled') && !_autologout_exclude_by_role()) {
    $timeout = (int) _autologout_local_settings('timeout');
    $nowtime = time();
    if (!isset($_SESSION['lastaccess'])) {
      $_SESSION['lastaccess'] = $nowtime;
    }

    // update lastaccess from any cache hits which wouldn't have been processes 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};\" />";
        $this_head = drupal_set_html_head($this_header);
      }
    }
    else {

      // timeout occured, logout and end session
      unset($_SESSION['lastaccess']);
      if (_autologout_local_settings('use_watchdog')) {
        watchdog('autologout', 'User %name automatically logged out.', array(
          '%name' => $user->name,
        ));
      }

      // 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,
      ));

      // Destroy the current session:
      session_destroy();

      // Only variables can be passed by reference workaround.
      $null = NULL;
      user_module_invoke('logout', $null, $user);

      // Load the anonymous user
      $user = drupal_anonymous_user();
      $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;
    }
  }

  // Clear session variable if you are not using autologout function and for anonymous users.
  if (isset($_SESSION['autologout_hits']) && (!_autologout_local_settings('enabled') || $user->uid == 0)) {
    unset($_SESSION['autologout_hits']);
  }
}

/* }}} */

/* {{{ autologout_user() */

/**
 * Implementation of hook_user().
 */
function autologout_user($op, $edit, &$account, $category = NULL) {

  // Do nothing for anonymous users.
  if ($account->uid == 0) {
    return;
  }
  if ($op == 'form' && $category == 'account') {
    if (_autologout_user_in_by_user_role($account)) {
      $form = array();
      $form[$category]['autologout'] = array(
        '#type' => 'checkbox',
        '#title' => t('Disable inactivity Automated Logout'),
        '#default_value' => $account->autologout,
        '#weight' => 10,
      );
      return $form;
    }
  }
  else {
    if ($op == 'login') {

      // one session checking
      $return = _autologout_check_one_session();
      if ($return) {
        _autologout_invalidate_other_sessions($account);
      }
      $_SESSION['lastaccess'] = time();
    }
    else {
      if ($op == 'load') {
        if (_autologout_user_in_by_user_role($account)) {
          $account->autologout = 0;
          $result = db_query("SELECT setting FROM {autologout} WHERE uid = %d", $account->uid);
          while ($row = db_fetch_object($result)) {
            $account->autologout = (int) $row->setting;
          }
        }
      }
      else {
        if ($op == 'update') {
          if (_autologout_user_in_by_user_role($account)) {
            if (isset($edit['autologout'])) {
              db_query("DELETE FROM {autologout} WHERE uid = %d", $account->uid);
              db_query("INSERT INTO {autologout} SET uid = %d, setting = %d", $account->uid, $edit['autologout']);

              // If they are turning off the disable setting, set lastaccess so they are not immediately logged out.
              if ($edit['autologout'] == 0) {
                $_SESSION['lastaccess'] = $nowtime;
              }
            }
          }
        }
        else {
          if ($op == 'delete') {
            db_query("DELETE FROM {autologout} WHERE uid = %d", $account->uid);
          }
        }
      }
    }
  }
  return;
}

/* {{{ autologout_nodeapi() */

/**
 * Implementation of hook_nodeapi().
 */
function autologout_nodeapi(&$node, $op, $teaser = NULL, $page = NULL) {
  _autologout_debug("autologout_nodeapi({$op})");

  // 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!
  switch ($op) {
    case "insert":
    case "update":
    case "execute":
    case "validate":
    case "prepare":
    case "delete":
      $_SESSION['lastaccess'] = time();
      break;
  }
}

/* }}} */

/* {{{ autologout_menu() */

/**
 * Implementation of hook_menu().
 */
function autologout_menu() {
  $items['admin/settings/autologout'] = array(
    'title' => t('Automated logout'),
    'description' => t('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' => t('Automated logout'),
    'page callback' => 'autologout_logout',
    'access callback' => TRUE,
    'type' => MENU_CALLBACK,
  );
  return $items;
}

/* }}} */

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

/*  Helper functions follow  */

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

/* {{{ _autologout_exclude_by_role() */

/**
 * _autologout_exclude_by_role()
 *
 * Is the user in a role that we exclude from features defined by this module
 *
 * @param $account
 *   A drupal "user" object or default FALSE (use global $user)
 *
 * @return bool
 *  TRUE if user to be excluded, FALSE otherwise
 */
function _autologout_exclude_by_role($account = NULL) {
  global $user;
  if ($account == NULL) {
    $account = $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($account->roles))) {
          return TRUE;
        }
        break;
      case 2:

        // Exclude user if user set to disable
        if (in_array($role, array_values($account->roles))) {
          if ($account->autologout == 1) {
            return TRUE;
          }
        }
        break;
    }
  }
  return FALSE;
}

/* }}} */

/* {{{ _autologout_user_in_by_user_role() */
function _autologout_user_in_by_user_role($account = NULL) {
  global $user;
  if ($account == NULL) {
    $account = $user;
  }
  foreach (user_roles(TRUE) as $role) {
    if (_autologout_local_settings($role) == 2 && in_array($role, array_values($account->roles))) {
      return TRUE;
    }
  }
  return FALSE;
}

/* }}} */

/* {{{ _autologout_local_settings() */

/**
 * _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) {

  // Default settings.
  $defaults = array(
    'enabled' => FALSE,
    'timeout' => 3600,
    'refresh_delta' => 0,
    'use_watchdog' => 1,
    'block_title' => t('automatic logout in'),
    'logout_message' => t('You have been automatically logged out due to inactivity.'),
  );
  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
  }
}

/* }}} */

/**
 * theme_autologout_generic()
 *
 * A general theme function used when HTML is being inserted into
 * a text string. Allows themers the oppertunity to alter html the
 * module may want to output to suit their site/theme.
 *
 * @param integer $arg
 *  A constant defining what string to return.
 * @return string
 *   The actual string.
 */
function theme_autologout_generic($arg = 0) {
  switch ($arg) {
    case 0:
      return '<br/><b>' . t('Set-up your site policy by role:-') . '</b>';
    default:
      return '';
  }
}

/* {{{ _autologout_debug() */
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[t($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
  $sql = "SELECT COUNT(*) as count FROM {sessions} WHERE uid = '%s' AND sid <> '%s'";
  $result = db_result(db_query($sql, $account->uid, session_id()));
  if ($result != 0) {

    // Send the message to the current session
    $current_session_msg = t('You are only allowed 1 open session at a time. Your other session has been terminated.');
    drupal_set_message($current_session_msg, '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_query("UPDATE {sessions} SET uid='0', session = '%s' WHERE uid = '%s' AND sid <> '%s'", $other_session_msg, $account->uid, session_id());

    // Write a watchdog message for the site admin.
    watchdog('Automated Logout', t('One Session automatically logged out user.'), array(), WATCHDOG_WARNING);
  }
}
function autologout_logout() {
  global $user;
  if ($user->uid == 0) {
    print theme('page', filter_xss_admin(_autologout_local_settings('logout_message')));
  }
  else {
    drupal_goto();
  }
}

Functions

Namesort descending Description
autologout_block Implementation of hook_block().
autologout_boot Implementation of hook_boot(). NOTE: Do as little as possible here. A lot of modules are not loaded at this point.
autologout_help Implementation of hook_help().
autologout_info Implementation of hook_info().
autologout_init Implementation of hook_init().
autologout_logout
autologout_menu Implementation of hook_menu().
autologout_nodeapi Implementation of hook_nodeapi().
autologout_perm Implementation of hook_perm().
autologout_user Implementation of hook_user().
theme_autologout_generic theme_autologout_generic()
_autologout_check_one_session
_autologout_debug
_autologout_exclude_by_role _autologout_exclude_by_role()
_autologout_invalidate_other_sessions
_autologout_local_settings _autologout_local_settings($name = FALSE)
_autologout_user_in_by_user_role