simple_access.module in Simple Access 5
Same filename and directory in other branches
This module allows administrators to make nodes viewable by specific 'access groups'. Each access group can contain any number of roles. If a node is not assigned to any access groups, it will remain viewable by all users.
Database definition:
File
simple_access.moduleView source
<?php
/**
* @file
* This module allows administrators to make nodes viewable by specific
* 'access groups'. Each access group can contain any number of roles.
* If a node is not assigned to any access groups, it will remain viewable
* by all users.
*
* Database definition:
* @code
* @endcode
*
*/
/**
* Implementation of hook_menu().
*/
function simple_access_menu($may_cache) {
$access = user_access('manage simple access');
if ($may_cache) {
$items[] = array(
'path' => 'admin/user/simple_access',
'title' => t('Simple Access'),
'access' => $access,
'callback' => 'simple_access_page_overview',
'type' => MENU_NORMAL_ITEM,
'description' => t('Manage groups of users for node-specific access control.'),
);
$items[] = array(
'path' => 'admin/user/simple_access/list',
'title' => t('List'),
'access' => $access,
'type' => MENU_DEFAULT_LOCAL_TASK,
'weight' => -8,
);
$items[] = array(
'path' => 'admin/user/simple_access/add',
'title' => t('Add Group'),
'callback' => 'drupal_get_form',
'callback arguments' => array(
'simple_access_group_form',
),
'access' => $access,
'type' => MENU_LOCAL_TASK,
'weight' => -6,
);
$items[] = array(
'path' => 'admin/user/simple_access/edit',
'title' => t('Edit Group'),
'callback' => 'drupal_get_form',
'callback arguments' => array(
'simple_access_group_form',
),
'access' => $access,
'type' => MENU_CALLBACK,
);
$items[] = array(
'path' => 'admin/user/simple_access/delete',
'title' => t('Delete Group'),
'callback' => 'drupal_get_form',
'callback arguments' => array(
'simple_access_delete_group_confirm',
),
'access' => $access,
'type' => MENU_CALLBACK,
);
$items[] = array(
'path' => 'admin/settings/simple_access',
'title' => t('Simple Access'),
'callback' => 'drupal_get_form',
'callback arguments' => array(
'simple_access_settings_page',
),
'access' => $access,
'type' => MENU_NORMAL_ITEM,
'description' => t('Configure which kinds of access (view, edit, delete) users with permission to use Simple Access can define for each node.'),
);
$items[] = array(
'path' => 'admin/content/simple_access',
'title' => t('Simple Access'),
'access' => user_access('administer nodes'),
'callback' => 'simple_access_nodes',
'type' => MENU_NORMAL_ITEM,
'description' => t('View node access which has been set up via Simple Access.'),
);
$items[] = array(
'path' => 'admin/content/simple_access/view',
'title' => t('View'),
'callback' => 'simple_access_nodes',
'access' => user_access('administer nodes'),
'type' => MENU_DEFAULT_LOCAL_TASK,
'weight' => -5,
);
$items[] = array(
'path' => 'admin/content/simple_access/edit',
'title' => t('Edit Access'),
'callback' => 'simple_access_nodes',
'access' => user_access('administer nodes'),
'type' => MENU_CALLBACK,
'weight' => -4,
);
$items[] = array(
'path' => 'admin/content/simple_access/delete',
'title' => t('Delete Access'),
'callback' => 'simple_access_nodes',
'access' => user_access('administer nodes'),
'type' => MENU_CALLBACK,
'weight' => -3,
);
}
return $items;
}
/**
* Implementation of hook_perm().
*/
function simple_access_perm() {
return array(
'manage simple access',
'assign access to nodes',
);
}
/**
* Implementation of hook_nodeapi()
*/
function simple_access_nodeapi(&$node, $op) {
switch ($op) {
case 'prepare':
if (!isset($node->simple_access)) {
$node->simple_access = array();
}
break;
case 'load':
$extra = array();
$result = db_query('SELECT na.gid, na.view, na.update, na.delete FROM {simple_access_node} na WHERE na.nid = %d', $node->nid);
while ($grant = db_fetch_array($result)) {
$gid = $grant['gid'];
unset($grant['gid']);
$extra[$gid] = $grant;
}
$node->simple_access = $extra;
break;
case 'update':
case 'insert':
db_query('DELETE FROM {simple_access_node} WHERE nid = %d', $node->nid);
if (isset($node->simple_access)) {
foreach ($node->simple_access as $gid => $access) {
if ($access['view'] || $access['update'] || $access['delete']) {
db_query("INSERT INTO {simple_access_node} (`nid`, `gid`, `view`, `update`, `delete`) VALUES (%d, %d, %d, %d, %d)", $node->nid, $gid, $access['view'], $access['update'], $access['delete']);
}
}
}
break;
case 'delete':
db_query('DELETE FROM {simple_access_node} WHERE nid = %d', $node->nid);
break;
}
}
/**
* Implementation of hook_node_access_records
*/
function simple_access_node_access_records($node) {
$records = array();
if ($node->simple_access) {
// loop through simple_access arrays from page submission
// $type is either 'view', 'update', or 'delete'
foreach ($node->simple_access as $gid => $access) {
if ($access['view'] || $access['update'] || $access['delete']) {
$records[] = array(
'realm' => 'simple_access',
'gid' => $gid,
'grant_view' => $access['view'],
'grant_update' => $access['update'],
'grant_delete' => $access['delete'],
'priority' => 1,
);
}
}
}
if (!empty($records) && $node->uid) {
// FIXME: Need to work out a better method of determining the authors
// access to the node.
$records[] = array(
'realm' => 'simple_access_author',
'gid' => $node->uid,
'grant_view' => user_access('access content'),
'grant_update' => user_access('access content'),
'grant_delete' => user_access('access content'),
'priority' => 1,
);
}
return $records;
}
/**
* Implementation of hook_node_grants().
*
* @TODO implement to correcly return groups in all cases.
*/
function simple_access_node_grants($account, $op) {
$gids = simple_access_groups_from_roles(array_keys($account->roles));
$grants['simple_access'] = $gids;
$grants['simple_access_author'] = array(
$account->uid,
);
return $grants;
}
function simple_access_form_alter($form_id, &$form) {
// if this is a node form...
if (isset($form['type']) && $form['type']['#value'] . '_node_form' == $form_id) {
if ($simple_access_form = simple_access_form($form['#node'])) {
$form = array_merge($form, $simple_access_form);
}
}
}
function simple_access_form($node) {
drupal_add_css(drupal_get_path('module', 'simple_access') . '/simple_access.css');
$user_groups = array_filter($node->simple_access, '_simple_access_filter_access');
// set up the outer fieldset
$form['simple_access'] = array(
'#title' => t('Access'),
'#type' => 'fieldset',
'#collapsible' => TRUE,
'#collapsed' => empty($user_groups),
'#tree' => TRUE,
'#weight' => 5,
'#access' => user_access('assign access to nodes') || user_access('administer nodes'),
'#theme' => 'simple_access_form',
);
// See what form elements we should include. If not configured,
// only enable the 'view' elements by default.
$variable = variable_get('sa_display', array(
'view' => 1,
));
// Get the array of checkbox options to use for each form element.
// If the "Show groups even when user is not a member" setting is
// enabled, or if the current user has 'administer nodes', let
// them choose from any of the SA groups.
$groups = simple_access_group_select();
foreach ($groups as $gid => $group) {
$access = $group->access || user_access('administer nodes');
$form['simple_access'][$gid] = array(
'#access' => $access,
);
$form['simple_access'][$gid]['name'] = array(
'#value' => $group->name,
'#access' => $access,
);
$form['simple_access'][$gid]['view'] = array(
'#type' => 'checkbox',
'#default_value' => $node->simple_access[$gid]['view'],
'#access' => $access && $variable['view'],
);
$form['simple_access'][$gid]['update'] = array(
'#type' => 'checkbox',
'#default_value' => $node->simple_access[$gid]['update'],
'#access' => $access && $variable['update'],
);
$form['simple_access'][$gid]['delete'] = array(
'#type' => 'checkbox',
'#default_value' => $node->simple_access[$gid]['delete'],
'#access' => $access && $variable['delete'],
);
}
return $form;
}
function theme_simple_access_form($form) {
$variable = variable_get('sa_display', array(
'view' => 1,
));
$head = array(
t('Access Group'),
);
if ($variable['view']) {
$head[] = t('View');
}
if ($variable['update']) {
$head[] = t('Update');
}
if ($variable['delete']) {
$head[] = t('Delete');
}
foreach (element_children($form) as $gid) {
$row = array(
array(
'data' => drupal_render($form[$gid]['name']),
),
);
if ($variable['view']) {
$row[] = array(
'data' => drupal_render($form[$gid]['view']),
);
}
if ($variable['update']) {
$row[] = array(
'data' => drupal_render($form[$gid]['update']),
);
}
if ($variable['delete']) {
$row[] = array(
'data' => drupal_render($form[$gid]['delete']),
);
}
$rows[] = $row;
}
$output .= theme('table', $head, $rows);
return $output;
}
function simple_access_delete_group_confirm() {
$gid = arg(4);
$form['gid'] = array(
'#type' => 'hidden',
'#value' => $gid,
);
return confirm_form($form, t('Are you sure you want to delete this group?'), 'admin/user/simple_access', t('This action cannot be undone.'), t('Delete'), t('Cancel'));
}
function simple_access_delete_group_confirm_submit($form_id, $form_values) {
simple_access_delete_group($form_values['gid']);
return 'admin/user/simple_access';
}
function simple_access_page_overview() {
if (count($rg = simple_access_get_groups())) {
drupal_set_title(t('Access Groups'));
$header = array(
t('Group'),
t('Roles'),
t('Operations'),
);
$roles = user_roles();
foreach ($rg as $g) {
$gid = $g['gid'];
$rows[$gid]['group'] = $g['name'];
$r = array();
foreach ($g['roles'] as $rid) {
$r[] = $roles[$rid];
}
$rows[$gid]['roles'] = "<span style='font-size:xx-small'>" . implode(', ', $r) . "</span>";
$rows[$gid]['ops'] = l('edit', 'admin/user/simple_access/edit/' . $gid) . ' ' . l('delete', 'admin/user/simple_access/delete/' . $gid);
}
$output .= theme('table', $header, $rows, array(
'style' => 'width:100%',
));
$output .= '<br />' . l(t('add another access group'), 'admin/user/simple_access/add');
return $output;
}
else {
drupal_set_message(t('You have not yet defined any access groups.'));
drupal_goto('admin/user/simple_access/add');
}
}
function simple_access_settings_page() {
drupal_set_title(t('Simple Access Settings'));
$options = array(
'view' => t('<strong>View</strong>: Displays viewability selections at top of node form. Selected access groups will be the only users who can view the node. All unselected = normal node behavior (viewable by all).<br />'),
'update' => t('<strong>Edit</strong>: Displays editability selections at top of node form. Users who are part of selected access groups will be able to edit this node. All unselected = "normal" node behavior (only author and admins may edit).<br />'),
'delete' => t('<strong>Delete</strong>: Displays deleteability selections at top of node form. Users who are part of selected access groups will be able to delete this node. All unselected = "normal" node behavior (only author and admins may delete).<br />'),
);
$form['sa_display'] = array(
'#type' => 'checkboxes',
'#title' => t('Display'),
'#default_value' => variable_get('sa_display', array(
'view',
)),
'#options' => $options,
'#description' => t('Which options should appear on node add/edit pages for administrators? Select at least one.'),
'#required' => TRUE,
);
$form['sa_showgroups'] = array(
'#type' => 'checkbox',
'#title' => 'Show groups even when user is not a member.',
'#default_value' => variable_get('sa_showgroups', 0),
'#description' => 'This is useful when you want to have a user be able to make content viewable by themselves and a higher privileged group (e.g. students sharing work with faculty)',
);
return system_settings_form($form);
}
function simple_access_group_form($gid = NULL) {
if ($gid) {
drupal_set_title(t('Edit Access Group'));
$group = db_fetch_object(db_query('SELECT name, weight FROM {simple_access_groups} WHERE gid = %d', $gid));
$name = $group->name;
$weight = $group->weight;
$roles = simple_access_get_roles($gid);
$form['gid'] = array(
'#type' => 'hidden',
'#value' => $gid,
);
}
else {
drupal_set_title(t('Create Access Group'));
$weight = 0;
}
$form['name'] = array(
'#type' => 'textfield',
'#title' => t('Name'),
'#default_value' => $name,
'#size' => 40,
'#maxlength' => 80,
'#description' => t('The name for the access group as it will appear on the content editing form.'),
'#attributes' => $attributes = NULL,
'#required' => TRUE,
);
$form['roles'] = array(
'#type' => 'checkboxes',
'#title' => t('Roles'),
'#default_value' => $roles,
'#options' => user_roles(),
'#description' => t('Roles that can view'),
);
$form['weight'] = array(
'#type' => 'weight',
'#title' => 'Weight',
'#default_value' => $weight,
'#delta' => 10,
'#description' => t('When setting permissions, heavier names will sink and lighter names will be positioned nearer the top.'),
);
$form[] = array(
'#type' => 'submit',
'#value' => t('Submit'),
);
return $form;
}
function simple_access_group_form_submit($form_id, $form_values) {
simple_access_save_group($form_values);
return 'admin/user/simple_access';
}
function simple_access_get_roles($gid) {
$roles = array();
$sql = db_query('SELECT rid FROM {simple_access_roles} WHERE gid = %d', $gid);
while ($row = db_fetch_object($sql)) {
$roles[] = $row->rid;
}
return $roles;
}
function simple_access_get_groups() {
$groups = array();
$sql = db_query('SELECT gid, name FROM {simple_access_groups} ORDER BY weight, name');
while ($g = db_fetch_object($sql)) {
$groups[$g->gid]['name'] = $g->name;
$groups[$g->gid]['gid'] = $g->gid;
$groups[$g->gid]['roles'] = simple_access_get_roles($g->gid);
}
return $groups;
}
function simple_access_group_select() {
static $groups;
if (empty($groups)) {
global $user;
$default_access = user_access('administer nodes');
$groups = array();
$result = db_query('SELECT gid, name FROM {simple_access_groups} ORDER BY weight, name');
while ($group = db_fetch_object($result)) {
$groups[$group->gid] = $group;
$groups[$group->gid]->access = $default_access;
}
if (!$default_access) {
// return just groups for which user is a member
$roles = array_keys($user->roles);
$result = db_query('SELECT DISTINCT g.gid FROM {simple_access_groups} g INNER JOIN {simple_access_roles} r ON g.gid = r.gid WHERE r.rid IN (%s) ORDER BY weight, name', implode(',', $roles));
while ($group = db_fetch_object($result)) {
$groups[$group->gid]->access = TRUE;
}
}
}
return $groups;
}
/**
* Get a list of group/grant ids based on a list of user roles
* $roles should be a linear list a role ids
*/
function simple_access_groups_from_roles($roles) {
// there probably should be some 'static' stuff going on here
// always return gid 0 just to be safe.
$gids = array(
0,
);
$result = db_query("SELECT gid FROM {simple_access_roles} WHERE rid IN (%s)", implode(",", $roles));
while ($g = db_fetch_object($result)) {
$gids[] = $g->gid;
}
return $gids;
}
/**
* Save group of roles into the database
* $roles is an associative array of roles where the keys are role ids
* $name is the name of the group
* $gid is the group id
*
*/
function simple_access_save_group($edit) {
if (!$edit['gid']) {
$edit['gid'] = db_next_id('{simple_access_groups}_gid');
}
db_query('DELETE FROM {simple_access_roles} WHERE gid = %d', $edit['gid']);
db_query('DELETE FROM {simple_access_groups} WHERE gid = %d', $edit['gid']);
$success = db_query("INSERT INTO {simple_access_groups} (gid, name, weight) VALUES (%d, '%s', %d)", $edit['gid'], $edit['name'], $edit['weight']);
if (is_array($edit['roles'])) {
foreach ($edit['roles'] as $key => $value) {
if ($value) {
$success = $success && db_query('INSERT INTO {simple_access_roles} (rid, gid) VALUES (%d, %d)', $key, $edit['gid']);
}
}
}
if (!$success) {
drupal_set_message(t('There was a problem saving to the database.'));
}
return $success;
}
function simple_access_delete_group($gid) {
db_query('DELETE FROM {simple_access_roles} WHERE gid = %d', $gid);
db_query('DELETE FROM {simple_access_groups} WHERE gid = %d', $gid);
}
/**
* List hidden, editable, and deletable nodes
*/
function simple_access_nodes() {
switch (arg(3)) {
case 'edit':
drupal_set_title(t('Items With Edit Access Set'));
$output = '<div>' . t('These nodes have been set as "additionally editable by" certain Simple Access <a href="!url">groups</a>. ', array(
'!url' => url('admin/user/simple_access'),
)) . '</div><br />';
$sql = "SELECT DISTINCT n.title, na.nid FROM {node} n INNER JOIN {node_access} na ON n.nid = na.nid WHERE na.realm='simple_access' AND na.gid > 0 AND na.grant_update = 1";
break;
case 'delete':
drupal_set_title(t('Items With Delete Access Set'));
$output = '<div>' . t('These nodes have been set as "additionally deletable by" certain Simple Access <a href="!url">groups</a>. ', array(
'!url' => url('admin/user/simple_access'),
)) . '</div><br />';
$sql = "SELECT DISTINCT n.title, na.nid FROM {node} n INNER JOIN {node_access} na ON n.nid = na.nid WHERE na.realm='simple_access' AND na.gid > 0 AND na.grant_delete = 1";
break;
case 'view':
default:
drupal_set_title(t('Items With View Access Set'));
$output = '<div>' . t('These nodes have been set as "only viewable by" certain Simple Access <a href="!url">groups</a>.', array(
'!url' => url('admin/user/simple_access'),
)) . '</div><br />';
$sql = "SELECT DISTINCT n.title, na.nid FROM {node} n INNER JOIN {node_access} na ON n.nid = na.nid WHERE na.realm='simple_access' AND na.gid > 0 AND na.grant_view = 1";
break;
}
$header = array(
array(
'data' => t('ID'),
'field' => 'n.nid',
'sort' => 'desc',
),
array(
'data' => t('Title'),
'field' => 'n.title',
),
array(
'data' => ' ',
),
);
$sql .= tablesort_sql($header);
$result = pager_query($sql, 50);
$groups_info = simple_access_get_groups();
while ($r = db_fetch_object($result)) {
$groups = array();
$rs2 = db_query('SELECT na.gid, na.grant_view, na.grant_update, na.grant_delete FROM {node_access} na WHERE na.nid = %d AND na.realm = \'simple_access\'', $r->nid);
while ($r2 = db_fetch_object($rs2)) {
$groups[] = $groups_info[$r2->gid]['name'];
}
$rows[$r->nid]['nid'] = array(
'data' => $r->nid,
'style' => 'vertical-align:top',
);
$info = '<div>' . $r->title . '</div><div style="font-size:xx-small">' . implode(', ', $groups) . '</div>';
$rows[$r->nid]['title'] = array(
'data' => $info,
'style' => 'vertical-align:top',
);
$rows[$r->nid]['ops'] = array(
'data' => l(t('view'), 'node/' . $r->nid) . ' ' . l(t('edit'), 'node/' . $r->nid . '/edit', array(), drupal_get_destination()),
'style' => 'vertical-align:top',
);
}
if ($rows) {
$output .= theme('table', $header, $rows, array(
'style' => 'width:100%',
));
$output .= theme('pager', array(), 50);
}
else {
$output .= '<div>' . t('No nodes match this criteria.') . '</div>';
}
return $output;
}
/**
* Filter the access records for the corrent user
*/
function _simple_access_filter_access($a) {
$groups = simple_access_group_select();
return isset($groups[$a->gid]->access) && $groups[$a->gid]->access;
}