You are here

subuser.module in Subuser 6

Same filename and directory in other branches
  1. 8 subuser.module
  2. 5 subuser.module
  3. 7.2 subuser.module

Allows users of a particular role to create sub user account in another role.

Copyright 2008-2009 by Jimmy Berry ("boombatower", http://drupal.org/user/214218)

File

subuser.module
View source
<?php

/**
 * @file
 * Allows users of a particular role to create sub user account in another role.
 *
 * Copyright 2008-2009 by Jimmy Berry ("boombatower", http://drupal.org/user/214218)
 */

/*
 * Variables loaded as constants.
 */
define('SUBUSER_PARENT', variable_get('subuser_parent', 'Parent'));
define('SUBUSER_LIST', variable_get('subuser_list', 'Subusers'));
define('SUBUSER_CREATE', variable_get('subuser_create', 'Create subuser'));
define('SUBUSER_ADMINISTER', variable_get('subuser_administer', 'Administer subusers'));

/**
 * Implementation of hook_menu().
 */
function subuser_menu() {
  $items = array();
  $items['admin/settings/subuser'] = array(
    'title' => 'Subuser',
    'description' => 'Define what, if any, roles are assigned to a new subuser.',
    'page callback' => 'drupal_get_form',
    'page arguments' => array(
      'subuser_settings_form',
    ),
    'access arguments' => array(
      'administer subuser settings',
    ),
    'file' => 'subuser.pages.inc',
  );
  $items['user/%user/subuser/create'] = array(
    'title' => variable_get('subuser_create', 'Create subuser'),
    'page callback' => 'drupal_get_form',
    'page arguments' => array(
      'subuser_create_form',
      1,
    ),
    'access callback' => 'subuser_user_create_access',
    'access arguments' => array(
      1,
    ),
    'type' => MENU_CALLBACK,
    'file' => 'subuser.pages.inc',
  );
  $items['subuser/switch/%'] = array(
    'title' => 'Switch subuser',
    'page callback' => 'subuser_switch_user',
    'page arguments' => array(
      2,
    ),
    'access callback' => 'subuser_switch_user_access',
    'access arguments' => array(
      2,
      TRUE,
    ),
    'type' => MENU_CALLBACK,
  );
  return $items;
}

/**
 * Implementation of hook_perm().
 */
function subuser_perm() {
  return array(
    'create subuser',
    'switch subuser',
    'administer subuser settings',
    'administer subusers',
    'bypass subuser limit',
  );
}

/**
 * Implementation of hook_menu_link_alter().
 *
 * Allow the logout link to be altered.
 *
 * @see, subuser_translated_menu_link_alter()
 */
function subuser_menu_link_alter(&$item, $menu) {
  if ($item['link_path'] == 'logout') {
    $item['options']['alter'] = TRUE;
  }
}

/**
 * Implementation of hook_translated_menu_link_alter().
 *
 * If currently running as a child user change the "Log out" link to
 * "Log out (return)".
 */
function subuser_translated_menu_link_alter(&$item, $map) {
  if ($item['href'] == 'logout' && isset($_SESSION['subuser_uid'])) {
    $item['title'] = t('Log out (return)');
    $item['href'] = 'subuser/switch/' . $_SESSION['subuser_uid'];
    $item['localized_options']['query']['token'] = drupal_get_token('subuser/switch/' . $_SESSION['subuser_uid']);
  }
}

/**
 * Implementation of hook_menu_link_alter().
 */
function subuser_menu_alter(&$items) {
  $items['user/%user_category/edit']['access callback'] = 'subuser_user_edit_access';
  $items['admin/user/user']['access callback'] = 'subuser_administer_users_access';
  $items['admin/user/user']['title callback'] = 'subuser_administer_users_title';
}

/**
 * Modified version of user_edit_access() for 'administer subusers' logic.
 */
function subuser_user_edit_access($account) {

  // Condition from user_edit_access().
  if (($GLOBALS['user']->uid == $account->uid || user_access('administer users')) && $account->uid > 0) {
    return TRUE;
  }

  // Check if user can administer subusers and the user being editted is a
  // subuser of the active user.
  if (user_access('administer subusers') && db_result(db_query('SELECT uid FROM {subuser_relationship} WHERE uid = %d AND parent_id = %d', $account->uid, $GLOBALS['user']->uid))) {
    return TRUE;
  }
  return FALSE;
}

/**
 * Implements hook_mail_alter().
 */
function subuser_mail_alter(&$message) {
  if (variable_get('subuser_cc_parent', 0)) {
    $parent_uid = subuser_get_parent($message['params']['account']->uid);
    $parent = user_load($parent_uid);
    $message['headers']['cc'] = $parent->mail;
  }
}

/**
 * Access callback for user/%user/subuser/create.
 *
 * If user has 'administer users' or 'administer subusers' or if they have
 * 'create subser' and they are adding a subuser to their own account then
 * allow access.
 *
 * @param $account
 *   User object representing the account which subusers will be added to.
 *
 * @return
 *   TRUE if access is granted otherwise FALSE.
 */
function subuser_user_create_access($account) {
  global $user;
  if (user_access('administer users') || (user_access('create subuser') || user_access('administer subusers')) && $account->uid == $user->uid) {
    return TRUE;
  }
  return FALSE;
}

/**
 * Access callback for admin/user/user page.
 *
 * If user has 'administer users' or 'administer subusers' then allow them to
 * view the administer users page. Filter the view to only the user's they are
 * a parent of if they do not have the 'administer users' permission.
 *
 * @return boolean TRUE access granted, otherwise FALSE.
 */
function subuser_administer_users_access() {
  if (user_access('administer users')) {
    return TRUE;
  }
  if (user_access('administer subusers')) {
    global $user;
    if (!isset($_SESSION['user_overview_filter'])) {
      $_SESSION['user_overview_filter'] = array();
    }

    // Look for the subuser filter and ensure it is set to the current user if
    // found, otherwise it will be added bellow.
    $found = FALSE;
    foreach ($_SESSION['user_overview_filter'] as $index => $filter) {
      list($key, $value) = $filter;
      if ($key == 'subuser') {
        $_SESSION['user_overview_filter'][$index][1] = $user->uid;
        $found = TRUE;
        break;
      }
    }

    // Explicitly add the filter.
    if (!$found) {
      $_SESSION['user_overview_filter'][] = array(
        'subuser',
        $user->uid,
      );
    }
    return TRUE;
  }
  return FALSE;
}

/**
 * Title callback for admin/user/user page.
 *
 * Set the title to the custom subuser administer title when user has
 * 'administer subusers' and not 'administer users' permission.
 *
 * @return string Either default title or custom subuser title.
 */
function subuser_administer_users_title() {
  if (!user_access('administer users') && user_access('administer subusers')) {
    return t(SUBUSER_ADMINISTER);
  }
  return t('Users');
}

/**
 * Check if the user has permission to switch the specified user.
 *
 * Pass cases:
 *   - Super user.
 *   - Returning to parent account.
 *   - The user is a parent of the user being switched to.
 *
 * @param integer $uid User ID being switched to.
 * @param boolean $check_token Boolean flag to check the token (used by router).
 * @return boolean Access granted.
 */
function subuser_switch_user_access($uid, $check_token = FALSE) {
  global $user;

  // Check for valid token or ignore token if $check_token is FALSE.
  if (!$check_token || isset($_GET['token']) && drupal_valid_token($_GET['token'], 'subuser/switch/' . $uid)) {

    // Check if user is switching back.
    if (isset($_SESSION['subuser_uid']) && $uid == $_SESSION['subuser_uid']) {
      return TRUE;
    }

    // Check if user is #1 or has the 'switch subuser' permission and is the
    // parent of the user to be switched to.
    if ($user->uid == 1 || user_access('switch subuser') && db_result(db_query('SELECT uid FROM {subuser_relationship} WHERE uid = %d AND parent_id = %d', $uid, $user->uid))) {
      return TRUE;
    }
  }
  return FALSE;
}

/**
 * Switch from a parent user to a subuser (or child user).
 *
 * @param $uid The user id to switch to.
 */
function subuser_switch_user($uid) {
  global $user;
  if ($uid) {
    $_SESSION['subuser_uid'] = isset($_SESSION['subuser_uid']) && $uid == $_SESSION['subuser_uid'] ? NULL : $user->uid;
    $user = user_load($uid);
  }
  drupal_goto('user/' . $uid);
}

/**
 * Implementation of hook_user().
 */
function subuser_user($op, &$edit, &$account, $category = NULL) {
  global $user;
  switch ($op) {
    case 'form':

      // Allow users with sufficient permission to limit the number of subusers on a per-user basis.
      if (user_access('administer subuser settings')) {
        $user_limit = db_result(db_query("SELECT subuser_limit FROM {subuser_limit} WHERE uid=%d", $account->uid));
        $form['subuser_limit'] = array(
          '#type' => 'textfield',
          '#title' => t('Subuser limit'),
          '#description' => t('Enter the maximum number of subusers this user can create'),
          '#default_value' => $user_limit ? $user_limit : variable_get('subuser_limit', NULL),
          '#size' => 5,
          '#maxlength' => 4,
        );
        return $form;
      }
      break;
    case 'insert':
      if (isset($edit['origin']) && $edit['origin'] == 'subuser') {
        db_query('INSERT INTO {subuser_relationship} (parent_id, uid)
                  VALUES (%d, %d)', $edit['parent_user'], $account->uid);
      }
      break;
    case 'update':

      // Check and see if we even need to bother ourselves with subusers at all.
      $subusers = subuser_get_subusers($account->uid);
      if (count($subusers) > 0) {

        // If we're blocking a user, get the subusers so we can block them too.
        $block_subusers = variable_get('subuser_children_block', NULL);
        if ($account->status == 1 && $block_subusers == 1 && isset($edit['status']) && $edit['status'] == 0) {
          subuser_block_subusers($subusers);
        }

        // If we're unblocking a user, get the subusers so we can unblock them too.
        $unblock_subusers = variable_get('subuser_children_unblock', NULL);
        if ($account->status == 0 && $unblock_subusers == 1 && isset($edit['status']) && $edit['status'] == 1) {
          subuser_unblock_subusers($subusers);
        }
      }

      // Allow role changes to cascade down to subusers.
      $role_change_subusers = variable_get('subuser_children_roles', NULL);
      if ($role_change_subusers == 1) {
        $new_roles = $edit['roles'];
        $old_roles = $account->roles;

        // Compare the count of old and new roles, > 1 means roles added, < 0 means they've been removed.
        $role_change = count($new_roles) - count($old_roles);
        if ($role_change !== 0) {
          if (is_array($new_roles) && is_array($old_roles)) {

            // Are we exempting a role from cascading? If so pull it out of new & old roles.
            $rid = variable_get('subuser_cascade_exempt_rid', NULL);
            if (isset($rid)) {
              unset($old_roles[$rid]);
              unset($new_roles[$rid]);
            }
            $old_keys = array_keys($old_roles);
            $new_keys = array_keys($new_roles);

            // We need to do the diff backwards depending on if we have a role addition or subtraction.
            $diff = $role_change > 0 ? array_diff($new_keys, $old_keys) : array_diff($old_keys, $new_keys);
            $op = $role_change > 0 ? 'add_role' : 'remove_role';
            subuser_tweak_roles_of_subusers($subusers, $op, $diff);
          }
        }
      }

      // Check to see if this user has a subuser limit set (and it's not the default value).
      if (isset($edit['subuser_limit']) && $edit['subuser_limit'] !== variable_get('subuser_limit', NULL)) {
        db_query("INSERT INTO {subuser_limit} (uid, subuser_limit) VALUES (%d, %d) ON DUPLICATE KEY UPDATE subuser_limit = %d", $account->uid, $edit['subuser_limit'], $edit['subuser_limit']);
        unset($edit['subuser_limit']);
      }
      break;
    case 'delete':
      $subusers = subuser_get_subusers($account->uid);

      // If there are subusers for this account, figure out what to do with them.
      if (count($subusers) > 0) {
        $subuser_orphaned_children = variable_get('subuser_orphaned_children', 0);
        if ($subuser_orphaned_children == 1) {

          // Block the subuser accounts too.
          subuser_block_subusers($subusers);
        }
        if ($subuser_orphaned_children == 2) {

          // Delete the subuser accounts too.
          foreach ($subusers as $uid) {
            user_delete(NULL, $uid);
          }
        }
      }
      db_query('DELETE FROM {subuser_relationship} WHERE uid = %d', $account->uid);
      break;
    case 'view':
      $parent = db_fetch_object(db_query('SELECT parent_id
                                          FROM {subuser_relationship}
                                          WHERE uid = %d', $account->uid));
      if ($parent) {

        // Display link to parent user if available.
        $parent = user_load($parent->parent_id);
        $account->content['subuser_parent'] = array(
          '#type' => 'user_profile_item',
          '#title' => t(SUBUSER_PARENT),
          '#value' => theme('username', $parent),
          '#weight' => 10,
        );
      }

      // The parent user should either have access to create subusers, or have
      // existing subusers.
      $create = subuser_user_create_access($account);

      // Check for a per-user limit on the number of subusers that this user can create.
      $limit = subuser_get_subuser_limit($account->uid);
      $current_subuser_count = count(subuser_get_subusers($account->uid));

      // The user is over the limit if they do have a limit and they have used it up.  They are not over the limit if the limit = 0 (unlimited).
      $over_limit = $limit - $current_subuser_count > 0 || $limit == 0 ? FALSE : TRUE;
      $administer = user_access('administer users') || user_access('administer subusers') && $account->uid == $user->uid;
      $show_administer_link = variable_get('subuser_show_admin', 1);
      $view = views_get_view('subusers');
      if ($create || isset($view->results) && $view->results) {
        $view = views_embed_view('subusers');
        if ($create && (!$over_limit || user_access('bypass subuser limit'))) {
          $links[] = l(t(SUBUSER_CREATE), 'user/' . $account->uid . '/subuser/create');
        }
        if ($administer && $current_subuser_count >= 1 && $show_administer_link) {
          $links[] = l(t(SUBUSER_ADMINISTER), 'admin/user/user');
        }
        if (isset($links)) {
          $output = implode(' | ', $links);
        }
        $output .= '<br />' . $view;
        $account->content['subuser'] = array(
          '#type' => 'user_profile_category',
          '#title' => t(SUBUSER_LIST),
          '#weight' => 11,
        );
        $account->content['subuser']['list'] = array(
          '#type' => 'user_profile_item',
          '#value' => $output,
          '#weight' => 11,
        );
      }
      break;
  }
}

/**
 * API function to get the subuser accounts of a specified parent account.
 *
 * @param $uid
 *  [optional] The uid of a potential parent account. Defaults to current user.
 * @return
 *  Array of subuser uids.
 */
function subuser_get_subusers($uid = NULL) {
  if ($uid == NULL) {
    $uid = $GLOBALS['user']->uid;
  }
  $users = array();
  $results = db_query("SELECT uid FROM {subuser_relationship} WHERE parent_id = %d", $uid);
  while ($u = db_fetch_array($results)) {
    $users[$u['uid']] = $u['uid'];
  }
  return $users;
}

/**
 * API function to identify any parent for a given user account.
 *
 * @param $uid
 *  [optional] The uid of a potential subuser account. Defaults to current user.
 * @return
 *  The parent uid or FALSE.
 */
function subuser_get_parent($uid = NULL) {
  if ($uid == NULL) {
    $uid = $GLOBALS['user']->uid;
  }
  return db_result(db_query("SELECT parent_id FROM {subuser_relationship} WHERE uid = %d", $uid));
}

/**
 * API function to get the max number of subusers this user is allowed to create.
 *
 * @param $uid
 * [optional] The uid of the parent account. Defaults to the global site-wide limit.
 * @return
 * Integer limit
 */
function subuser_get_subuser_limit($uid = NULL) {

  // Check to see if this user has a specific limit.
  if ($uid !== NULL) {
    $limit = (int) db_result(db_query("SELECT subuser_limit FROM {subuser_limit} WHERE uid = %d", $uid));
  }

  // If we don't have a limit for this user, or we don't have a uid use the global limit.
  if ($uid == NULL || $limit == NULL) {
    $limit = (int) variable_get('subuser_limit', NULL);
  }
  return $limit;
}

/**
 * Function to block subusers when a parent is blocked.
 *
 * @param $uid
 * [optional] The array of subusers, from subuser_get_subusers(), we're interested in blocking.
 */
function subuser_block_subusers($subusers = NULL) {
  if ($subusers == NULL) {
    global $user;
    $subusers = subuser_get_subusers($user->uid);
  }
  foreach ($subusers as $uid) {
    $account = user_load(array(
      'uid' => (int) $uid,
    ));

    // Skip blocking user if they are already blocked.
    if ($account !== FALSE && $account->status == 1) {
      user_save($account, array(
        'status' => 0,
      ));
    }
  }
}

/**
 * Function to unblock subusers when a parent is unblocked.
 *
 * @param $subusers
 * [optional] The array of subusers, from subuser_get_subusers(), we're interested in un-blocking.
 */
function subuser_unblock_subusers($subusers = NULL) {
  if ($subusers == NULL) {
    global $user;
    $subusers = subuser_get_subusers($user->uid);
  }
  foreach ($subusers as $uid) {
    $account = user_load(array(
      'uid' => (int) $uid,
    ));

    // Skip unblocking user if they are already unblocked.
    if ($account !== FALSE && $account->status == 0) {
      user_save($account, array(
        'status' => 1,
      ));
    }
  }
}

/**
 * Function to add roles to subusers based on the parent's account change.
 *
 * @param $subusers
 * An array of user id's (key & value uid) as returned by subuser_get_subusers.
 * @param $op
 * Either 'add_role' or 'remove_role' as required by user_multiple_role_edit().
 * @param $diff
 * An array of role id's to add or remove from each of the subusers.
 */
function subuser_tweak_roles_of_subusers($subusers, $op, $diff) {
  if (is_array($diff) && is_array($subusers)) {
    foreach ($diff as $rid) {
      user_multiple_role_edit($subusers, $op, $rid);
    }
  }
}

/**
 * Implementation of hook_views_api().
 */
function subuser_views_api() {
  return array(
    'api' => 2,
  );
}

/**
 * Implementation of hook_views_data().
 */
function subuser_views_data() {
  $data['subuser_relationship']['table']['group'] = t('Subuser Relationship');
  $data['subuser_relationship']['table']['join']['users'] = array(
    'left_field' => 'uid',
    'field' => 'uid',
  );
  $data['subuser_relationship']['rid'] = array(
    'title' => t('Relationship: ID'),
    'help' => t('The relationship id'),
  );
  $data['subuser_relationship']['uid'] = array(
    'title' => t('Uid'),
    'help' => t('The ID of the Sub User.'),
    'field' => array(
      'handler' => 'views_handler_field_user',
      'click sortable' => TRUE,
    ),
    'argument' => array(
      'handler' => 'views_handler_argument_user_uid',
      'name field' => 'title',
      'numeric' => TRUE,
      'validate type' => 'uid',
    ),
    'filter' => array(
      'handler' => 'views_handler_filter_numeric',
    ),
    'sort' => array(
      'handler' => 'views_handler_sort',
    ),
  );
  $data['subuser_relationship']['parent_id'] = array(
    'title' => t('Parent Id'),
    'help' => t('The ID of the Parent User.'),
    'field' => array(
      'handler' => 'views_handler_field_user',
      'click sortable' => TRUE,
    ),
    'argument' => array(
      'handler' => 'views_handler_argument_user_uid',
      'name field' => 'title',
      'numeric' => TRUE,
      'validate type' => 'uid',
    ),
    'relationship' => array(
      'base' => 'users',
      'field' => 'uid',
      'handler' => 'views_handler_relationship',
      'label' => t('Parent'),
    ),
    'filter' => array(
      'handler' => 'views_handler_filter_numeric',
    ),
    'sort' => array(
      'handler' => 'views_handler_sort',
    ),
  );

  // Provide an alternative edit link that respects administer subusers
  // permissions and uses subuser_user_edit_access() to verify the current user
  // can access the account in question.
  $data['users']['edit_subuser'] = array(
    'field' => array(
      'title' => t('Edit subuser link'),
      'help' => t('Provide a simple link to edit the subuser. Respects administer subusers permission.'),
      'handler' => 'subuser_handler_field_user_link_edit',
    ),
  );

  // Provide a link for switching to a subuser account that respects switch
  // subusers permissions and uses subuser_switch_user_access() to verify the
  // current user can switch to the account in question.
  $data['users']['switch_subuser'] = array(
    'field' => array(
      'title' => t('Switch to subuser link'),
      'help' => t('Provide a simple link to switch to the subuser.'),
      'handler' => 'subuser_handler_field_user_link_switch',
    ),
  );
  return $data;
}

Functions

Namesort descending Description
subuser_administer_users_access Access callback for admin/user/user page.
subuser_administer_users_title Title callback for admin/user/user page.
subuser_block_subusers Function to block subusers when a parent is blocked.
subuser_get_parent API function to identify any parent for a given user account.
subuser_get_subusers API function to get the subuser accounts of a specified parent account.
subuser_get_subuser_limit API function to get the max number of subusers this user is allowed to create.
subuser_mail_alter Implements hook_mail_alter().
subuser_menu Implementation of hook_menu().
subuser_menu_alter Implementation of hook_menu_link_alter().
subuser_menu_link_alter Implementation of hook_menu_link_alter().
subuser_perm Implementation of hook_perm().
subuser_switch_user Switch from a parent user to a subuser (or child user).
subuser_switch_user_access Check if the user has permission to switch the specified user.
subuser_translated_menu_link_alter Implementation of hook_translated_menu_link_alter().
subuser_tweak_roles_of_subusers Function to add roles to subusers based on the parent's account change.
subuser_unblock_subusers Function to unblock subusers when a parent is unblocked.
subuser_user Implementation of hook_user().
subuser_user_create_access Access callback for user/%user/subuser/create.
subuser_user_edit_access Modified version of user_edit_access() for 'administer subusers' logic.
subuser_views_api Implementation of hook_views_api().
subuser_views_data Implementation of hook_views_data().

Constants