You are here

role_watchdog.module in Role Watchdog 6

Logs changes to user roles.

File

role_watchdog.module
View source
<?php

/**
 * @file
 * Logs changes to user roles.
 */
define('ROLE_WATCHDOG_SUPERUSER_RID', 999999);
define('ROLE_WATCHDOG_ROLE_ADD', 1);
define('ROLE_WATCHDOG_ROLE_REMOVE', 0);
define('ROLE_WATCHDOG_ROLE_CHANGE', 1);
define('ROLE_WATCHDOG_ROLE_NOCHANGE', 0);

/**
 * Implementation of hook_help().
 */
function role_watchdog_help($path, $arg) {
  switch ($path) {
    case 'admin/help#role_watchdog':
      return '<p>' . t('Role watchdog will automatically start recording all role changes. No further configuration is necessary for this functionality, the module will do this "out of the box". A record of these changes is shown in a Role history tab on each user\'s page and optionally in the Watchdog log if enabled. Users will need  either "View role history" or "View own role history" access permissions to  view the tab.') . '</p>' . '<p>' . t('Role watchdog can optionally email members of selected Notify roles when selected Monitor roles are added or removed. This was specifically added to keep a closer eye on certain role changes, such as an Administrator role. At least one Monitor role and one Notify role must be selected for this functionality.') . '</p>';
  }
}

/**
 * Implementation of hook_perm().
 */
function role_watchdog_perm() {
  return array(
    'view role history',
    'view own role history',
  );
}

/**
 * Implementation of hook_menu().
 */
function role_watchdog_menu() {
  $items = array();
  $items['admin/user/roles/role_watchdog'] = array(
    'title' => 'Role watchdog',
    'description' => 'Logs changes to user roles.',
    'page callback' => 'drupal_get_form',
    'page arguments' => array(
      'role_watchdog_admin_settings',
    ),
    'type' => MENU_NORMAL_ITEM,
    'access arguments' => array(
      'administer site configuration',
    ),
    'file' => 'role_watchdog.admin.inc',
  );
  $items['admin/reports/role_watchdog'] = array(
    'title' => 'Role grants report',
    'description' => 'View changes to role assignments for users.',
    'page callback' => 'role_watchdog_report',
    'access arguments' => array(
      'view role history',
    ),
    'file' => 'role_watchdog.pages.inc',
  );
  $items['user/%user/track/role_history'] = array(
    'title' => 'Track role history',
    'page callback' => 'role_watchdog_history',
    'page arguments' => array(
      1,
    ),
    'access callback' => '_role_watchdog_history_access',
    'access arguments' => array(
      1,
    ),
    'type' => MENU_LOCAL_TASK,
    'file' => 'role_watchdog.pages.inc',
    'weight' => '20',
  );
  $items['user/%user/track/role_grants'] = array(
    'title' => 'Track role grants',
    'page callback' => 'role_watchdog_grants',
    'page arguments' => array(
      1,
    ),
    'access callback' => '_role_watchdog_history_access',
    'access arguments' => array(
      1,
    ),
    'type' => MENU_LOCAL_TASK,
    'file' => 'role_watchdog.pages.inc',
    'weight' => '21',
  );
  return $items;
}

/**
 * Implementation of hook_user().
 */
function role_watchdog_user($type, &$edit, &$account, $category = NULL) {
  switch ($type) {
    case 'load':
      _role_watchdog_user_roles_cache($account->uid, $account->roles);
      break;
    case 'insert':
      if (isset($edit['roles'])) {
        role_watchdog_user_save(isset($account->uid) ? $account->uid : FALSE, $edit, $account);
      }
      break;
    case 'update':
      global $user;
      if (isset($edit['roles'])) {
        role_watchdog_user_save(isset($account->uid) ? $account->uid : FALSE, $edit, $account);
      }
      if (isset($account->uid) && array_key_exists('status', $edit)) {
        role_watchdog_user_block($account, $edit['status']);
      }
      break;
    case 'delete':
      db_query('DELETE FROM {role_watchdog} WHERE aid=%d', $account->uid);
      db_query('UPDATE {role_watchdog} SET uid=0 WHERE uid=%d', $account->uid);
      break;
  }
}

/**
 * Cache the user roles on the first call to hook_user('load') to
 * guard against modules that may incorrectly edit the $account object
 * before calling user_save().
 */
function _role_watchdog_user_roles_cache($uid, $roles = FALSE) {
  static $user_roles = array();
  if (!isset($user_roles[$uid]) && is_array($roles)) {
    $user_roles[$uid] = $roles;
  }
  if (!isset($user_roles[$uid])) {
    return array();
  }
  else {
    return $user_roles[$uid];
  }
}

/**
 * Process user block / unblock
 */
function role_watchdog_user_block($account, $status) {
  if ($account->status != $status) {
    if ($status) {
      return _role_watchdog_process_role_changes($account, array(
        DRUPAL_AUTHENTICATED_RID,
      ), array());
    }
    else {
      return _role_watchdog_process_role_changes($account, array(), array(
        DRUPAL_AUTHENTICATED_RID,
      ));
    }
  }
}

/**
 * Process role add/remove.
 */
function role_watchdog_user_save($uid, &$form, $account) {

  // Incoming format: array([2] => authenticated user, [3] => test role,)
  $old_roles = _role_watchdog_user_roles_cache($account->uid);
  unset($old_roles[DRUPAL_AUTHENTICATED_RID]);

  // No built-in roles.
  if (!empty($old_roles)) {
    $old_roles = array_flip($old_roles);
  }

  // Outgoing format: array([test role] => 3,)
  // Incoming format: array([3] => 3, [2] => 1,)
  $new_roles = $form['roles'];
  unset($new_roles[DRUPAL_AUTHENTICATED_RID]);

  // No built-in roles.
  if (!empty($new_roles)) {
    $new_roles = array_flip($new_roles);
    unset($new_roles[0]);
  }

  // Outgoing format: array([3] => 3,)
  return _role_watchdog_process_role_changes($account, $new_roles, $old_roles);
}
function _role_watchdog_process_role_changes($account, $new_roles, $old_roles, $vars = array()) {
  $result = array();
  $count = 0;

  // Is role added?
  foreach ($new_roles as $rid) {
    $record = _role_watchdog_add_role($rid, $old_roles, $account, ROLE_WATCHDOG_ROLE_ADD, $vars);
    if (is_array($record)) {
      $result[] = $record;
      $count = $count + 1;
    }
  }

  // Is role removed?
  foreach ($old_roles as $rid) {
    $record = _role_watchdog_remove_role($rid, $new_roles, $account, FALSE, $vars);
    if (is_array($record)) {
      $result[] = $record;
      $count = $count + 1;
    }
  }
  if ($count) {
    drupal_set_message(format_plural($count, t('Role change has been logged.'), t('Role changes have been logged.')));
  }
  return $result;
}

/**
 * Implementation of hook_mail().
 */
function role_watchdog_mail($key, &$message, $params) {
  global $base_url;
  $language = $message['language'];
  $variables = array(
    '!site' => variable_get('site_name', 'Drupal'),
    '!uri' => $base_url,
  );
  $variables += $params;
  $variables['!user'] .= ' (' . $variables['!uri'] . '/user/' . $params['!user_id'] . ')';
  $variables['!account'] .= ' (' . $variables['!uri'] . '/user/' . $params['!account_id'] . ')';
  switch ($key) {
    case 'notification':
      $message['subject'] = str_replace(array(
        "\r",
        "\n",
      ), '', t('Role watchdog notification on !site', $variables, $language->language));
      $message['body'][] = strtr($params['body'], $variables);
      break;
  }
}

/**
 * Internal function
 *
 * Handles addition of roles.
 */
function _role_watchdog_add_role($rid, $old_roles = array(), $account, $action = ROLE_WATCHDOG_ROLE_ADD, $vars = array()) {
  if (!in_array($rid, $old_roles)) {
    global $user;
    $roles = user_roles();
    $record = array(
      'aid' => $account->uid,
      'rid' => $rid,
      'action' => $action,
      'uid' => $user->uid,
      'stamp' => $_SERVER['REQUEST_TIME'],
    );
    if (drupal_write_record('role_watchdog', $record)) {
      $vars += array(
        'body' => 'Role !role!role_modifier added to !account by !user',
        '!role' => check_plain($roles[$rid]),
        '!role_modifier' => '',
        '!user' => check_plain($user->name),
        '!user_id' => $user->uid,
        '!account' => check_plain($account->name),
        '!account_id' => $account->uid,
      );
      watchdog('role_watchdog', $vars['body'], $vars, WATCHDOG_NOTICE, l(t('view'), 'user/' . $account->uid . '/track/role_history'));
      _role_watchdog_notification($rid, $vars);
      return $record;
    }
    else {
      watchdog('role_watchdog', 'Unable to save record in _role_watchdog_add_role()', array(), WATCHDOG_ERROR, l(t('view'), 'user/' . $account->uid . '/track/role_history'));
      return ROLE_WATCHDOG_ROLE_NOCHANGE;
    }
  }
  return ROLE_WATCHDOG_ROLE_NOCHANGE;
}

/**
 * Internal function
 *
 * Handles removal of roles.
 */
function _role_watchdog_remove_role($rid, $old_roles = array(), $account, $in_array = TRUE, $vars = array()) {
  if (in_array($rid, $old_roles) xor !$in_array) {
    global $user;
    $roles = user_roles();
    $record = array(
      'aid' => $account->uid,
      'rid' => $rid,
      'action' => 0,
      'uid' => $user->uid,
      'stamp' => $_SERVER['REQUEST_TIME'],
    );
    if (drupal_write_record('role_watchdog', $record)) {
      $vars += array(
        'body' => 'Role !role!role_modifier removed from !account by !user',
        '!role' => check_plain($roles[$rid]),
        '!role_modifier' => '',
        '!user' => check_plain($user->name),
        '!user_id' => $user->uid,
        '!account' => check_plain($account->name),
        '!account_id' => $account->uid,
      );
      watchdog('role_watchdog', $vars['body'], $vars, WATCHDOG_NOTICE, l(t('view'), 'user/' . $account->uid . '/track/role_history'));
      _role_watchdog_notification($rid, $vars);
      return $record;
    }
    else {
      watchdog('role_watchdog', 'Unable to save record in _role_watchdog_remove_role()', array(), WATCHDOG_ERROR, l(t('view'), 'user/' . $account->uid . '/track/role_history'));
      return ROLE_WATCHDOG_ROLE_NOCHANGE;
    }
  }
  return ROLE_WATCHDOG_ROLE_NOCHANGE;
}

/**
 * Internal function
 *
 * Handles notification of changes in selected roles.
 */
function _role_watchdog_notification($rid, $vars = array()) {
  $monitor_roles = variable_get('role_watchdog_monitor_roles', NULL);
  if (in_array($rid, (array) $monitor_roles)) {
    foreach (_role_watchdog_get_notification_list() as $recipient) {
      if (drupal_mail('role_watchdog', 'notification', $recipient, language_default(), $vars)) {
        watchdog('role_watchdog', 'Sent notification to %recipient', array(
          '%recipient' => $recipient,
        ), WATCHDOG_INFO);
      }
      else {
        watchdog('role_watchdog', 'Unable to send notification to %recipient', array(
          '%recipient' => $recipient,
        ), WATCHDOG_ERROR);
      }
    }
  }
}

/**
 * Internal function
 *
 * Handles building notification list
 */
function _role_watchdog_get_notification_list() {
  static $role_watchdog_notification_list;
  if (!isset($role_watchdog_notification_list)) {
    $role_watchdog_notification_list = array();
    $notification_roles = variable_get('role_watchdog_notify_roles', array());
    if (count($notification_roles)) {
      $result = db_query("SELECT DISTINCT u.mail FROM {users} u INNER JOIN {users_roles} r ON u.uid = r.uid WHERE r.rid IN (%s)", implode(",", $notification_roles));
      while ($account = db_fetch_object($result)) {
        $role_watchdog_notification_list[] = $account->mail;
      }

      // Special case: superuser.
      if (in_array(ROLE_WATCHDOG_SUPERUSER_RID, $notification_roles)) {
        $result = db_result(db_query("SELECT u.mail FROM {users} u WHERE u.uid = %d", 1));
        if (!in_array($result, $role_watchdog_notification_list)) {
          $role_watchdog_notification_list[] = $result;
        }
      }
    }
  }
  return $role_watchdog_notification_list;
}

/**
 * Access callback for viewing role history page.
 */
function _role_watchdog_history_access($account) {
  if (user_access('view role history')) {
    return TRUE;
  }
  global $user;
  return $user->uid == $account->uid && user_access('view own role history') ? TRUE : FALSE;
}

Functions

Namesort descending Description
role_watchdog_help Implementation of hook_help().
role_watchdog_mail Implementation of hook_mail().
role_watchdog_menu Implementation of hook_menu().
role_watchdog_perm Implementation of hook_perm().
role_watchdog_user Implementation of hook_user().
role_watchdog_user_block Process user block / unblock
role_watchdog_user_save Process role add/remove.
_role_watchdog_add_role Internal function
_role_watchdog_get_notification_list Internal function
_role_watchdog_history_access Access callback for viewing role history page.
_role_watchdog_notification Internal function
_role_watchdog_process_role_changes
_role_watchdog_remove_role Internal function
_role_watchdog_user_roles_cache Cache the user roles on the first call to hook_user('load') to guard against modules that may incorrectly edit the $account object before calling user_save().

Constants