You are here

simple_access.module in Simple Access 5

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.module
View 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) . '&nbsp;' . 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' => '&nbsp;',
    ),
  );
  $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;
}

Functions

Namesort descending Description
simple_access_delete_group
simple_access_delete_group_confirm
simple_access_delete_group_confirm_submit
simple_access_form
simple_access_form_alter
simple_access_get_groups
simple_access_get_roles
simple_access_groups_from_roles Get a list of group/grant ids based on a list of user roles $roles should be a linear list a role ids
simple_access_group_form
simple_access_group_form_submit
simple_access_group_select
simple_access_menu Implementation of hook_menu().
simple_access_nodeapi Implementation of hook_nodeapi()
simple_access_nodes List hidden, editable, and deletable nodes
simple_access_node_access_records Implementation of hook_node_access_records
simple_access_node_grants Implementation of hook_node_grants().
simple_access_page_overview
simple_access_perm Implementation of hook_perm().
simple_access_save_group 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
simple_access_settings_page
theme_simple_access_form
_simple_access_filter_access Filter the access records for the corrent user