Skip to content

Instantly share code, notes, and snippets.

@arturo-c
Created October 6, 2011 15:15
Show Gist options
  • Save arturo-c/1267652 to your computer and use it in GitHub Desktop.
Save arturo-c/1267652 to your computer and use it in GitHub Desktop.
<?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),
'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'];
}
}
/**
* 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;
}
/**
* 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.
* @return boolean Access granted.
*/
function subuser_switch_user_access($uid) {
global $user;
if ($user->uid == 1 || (user_access('switch subuser') &&
((isset($_SESSION['subuser_uid']) && $uid == $_SESSION['subuser_uid']) ||
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));
$over_limit = ($limit - $current_subuser_count > 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;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment