tac_lite.module in Taxonomy Access Control Lite 6
Same filename and directory in other branches
Control access to site content based on taxonomy, roles and users.
File
tac_lite.moduleView source
<?php
/**
* @file
* Control access to site content based on taxonomy, roles and users.
*
*
*/
/**
* Implementation of hook_help().
*/
function tac_lite_help($section) {
switch ($section) {
case 'admin/help#tac_lite':
$output .= '<p>' . t('This module allows you to restrict access to site content. It uses a simple scheme based on Taxonomy, Users and Roles. It uses the node_access table and other features built into Drupal to hide content from unauthorized users.') . '</p>';
$output .= '<p>' . t('While this module has been designed to be as simple as possible to use, there are several steps required to set it up.') . '</p>';
$output .= '<ol>';
$output .= '<li>' . t('Define one or more vocabularies whose terms will control which users have access. For example, you could define a vocabulary called \'Privacy\' with terms \'Public\' and \'Private\'.') . '</li>';
$output .= '<li>' . t('Tell this module which vocabulary or vocabularies control privacy. (!link)', array(
'!link' => l(t('administer -> access control -> tac_lite'), 'admin/user/access/tac_lite'),
)) . '</li>';
$output .= '<li>' . t('Grant access to users based on their roles (!link), and/or...', array(
'!link' => l(t('administer -> access control -> tac_lite -> by role'), 'admin/user/access/tac_lite/roles'),
)) . '</li>';
$output .= '<li>' . t('Grant access to individual users. (See the taxonomy-based access tab under user -> edit.)') . '</li>';
$output .= '<li>' . t('Finally, if your site contains content, you will need to re-save all nodes. This ensures that Drupal\'s node_access table is up-to-date. Otherwise, content submitted before this module was configured will be hidden.') . '</li>';
$output .= '</ol>';
$output .= '<p>' . t('Currently, this module works with view grants only (no update or delete grants).') . '</p>';
return $output;
break;
}
}
/**
* Implementation of hook_perm().
*/
function tac_lite_perm() {
return array(
'administer tac_lite',
);
}
/**
* Implementation of hook_menu().
*/
function tac_lite_menu() {
global $user;
$items = array();
$items['admin/user/access/tac_lite'] = array(
'title' => 'Access by Taxonomy',
'description' => "taxonomy-based permissions by tac_lite",
'page callback' => 'drupal_get_form',
'page arguments' => array(
'tac_lite_admin_settings',
),
'type' => MENU_NORMAL_ITEM,
'weight' => 1,
// after 'roles' tab
'access arguments' => array(
'administer tac_lite',
),
);
$items['admin/user/access/tac_lite/settings'] = array(
'title' => 'Settings',
'type' => MENU_DEFAULT_LOCAL_TASK,
'weight' => -1,
'access arguments' => array(
'administer tac_lite',
),
);
$schemes = variable_get('tac_lite_schemes', 1);
for ($i = 1; $i <= $schemes; $i++) {
$scheme = variable_get('tac_lite_config_scheme_' . $i, FALSE);
if ($scheme) {
$title = $scheme['name'];
}
else {
$title = "Scheme {$i}";
}
$items['admin/user/access/tac_lite/scheme_' . $i] = array(
'title' => $title,
'page callback' => 'tac_lite_admin_settings_scheme',
'page arguments' => array(
(string) $i,
),
'type' => MENU_LOCAL_TASK,
'weight' => $i,
'access arguments' => array(
'administer tac_lite',
),
);
}
return $items;
}
/**
* Returns the settings form
*/
function tac_lite_admin_settings() {
$vocabularies = taxonomy_get_vocabularies();
if (!count($vocabularies)) {
$form['body'] = array(
'#type' => 'markup',
'#value' => t('You must <a href="!url">create a vocabulary</a> before you can use tac_lite.', array(
'!url' => url('admin/content/taxonomy/add/vocabulary'),
)),
);
return $form;
}
else {
$options = array();
foreach ($vocabularies as $vid => $vocab) {
$options[$vid] = $vocab->name;
}
$form['tac_lite_categories'] = array(
'#type' => 'select',
'#title' => t('Vocabularies'),
'#default_value' => variable_get('tac_lite_categories', NULL),
'#options' => $options,
'#description' => t('Select one or more vocabularies to control privacy. <br/>Use caution with hierarchical (nested) taxonomies as <em>visibility</em> settings may cause problems on node edit forms.<br/>Do not select free tagging vocabularies, they are not supported.'),
'#multiple' => TRUE,
'#required' => TRUE,
);
$scheme_options = array();
// Currently only view, edit, delete permissions possible, so 7
// permutations will be more than enough.
for ($i = 1; $i < 8; $i++) {
$scheme_options[$i] = $i;
}
$form['tac_lite_schemes'] = array(
'#type' => 'select',
'#title' => t('Schemes'),
'#description' => t('Each scheme allows for a different set of permissions. For example, use scheme 1 for read-only permission; scheme 2 for read and update; scheme 3 for delete; etc. Additional schemes increase the size of your node_access table, so use no more than you need.'),
'#default_value' => variable_get('tac_lite_schemes', 1),
'#options' => $scheme_options,
'#required' => TRUE,
);
$ret = system_settings_form($form);
// Special handling is required when this form is submitted.
$ret['#submit'][] = '_tac_lite_admin_settings_submit';
return $ret;
}
}
/**
* This form submit callback ensures that the form values are saved, and also
* the node access database table is rebuilt.
* 2008 : Modified by Paulo to be compliant with drupal 6
*/
function _tac_lite_admin_settings_submit($form, &$form_state) {
// First, save settings the default way.
system_settings_form_submit($form, $form_state);
// Next, rebuild the node_access table.
node_access_rebuild(TRUE);
//drupal_set_message(t('The content access permissions have been rebuilt.'));
// And rebuild menus, in case the number of schemes has changed.
menu_rebuild();
}
function tac_lite_admin_settings_scheme($i) {
return drupal_get_form('tac_lite_admin_scheme_form', $i);
}
/**
* helper function
*/
function _tac_lite_config($scheme) {
// different defaults for scheme 1
if ($scheme === 1) {
$config = variable_get('tac_lite_config_scheme_' . $scheme, array(
'name' => t('read'),
'perms' => array(
'grant_view',
),
));
}
else {
$config = variable_get('tac_lite_config_scheme_' . $scheme, array(
'name' => NULL,
'perms' => array(),
));
}
// Merge defaults, for backward compatibility.
$config += array(
'term_visibility' => isset($config['perms']['grant_view']) && $config['perms']['grant_view'],
);
// For backward compatability, use naming convention for scheme 1
if ($scheme == 1) {
$config['realm'] = 'tac_lite';
}
else {
$config['realm'] = 'tac_lite_scheme_' . $scheme;
}
return $config;
}
/**
* Returns the form for role-based privileges.
*/
function tac_lite_admin_scheme_form($form_state, $i) {
$vids = variable_get('tac_lite_categories', NULL);
$roles = user_roles();
if (count($vids)) {
$config = _tac_lite_config($i);
$form['#tac_lite_config'] = $config;
$form['tac_lite_config_scheme_' . $i] = array(
'#tree' => TRUE,
);
$form['tac_lite_config_scheme_' . $i]['name'] = array(
'#type' => 'textfield',
'#title' => t('Scheme name'),
'#description' => t('A human-readable name for administrators to see. For example, \'read\' or \'read and write\'.'),
'#default_value' => $config['name'],
'#required' => TRUE,
);
// Currently, only view, update and delete are supported by node_access
$options = array(
'grant_view' => 'view',
'grant_update' => 'update',
'grant_delete' => 'delete',
);
$form['tac_lite_config_scheme_' . $i]['perms'] = array(
'#type' => 'select',
'#title' => t('Permissions'),
'#multiple' => TRUE,
'#options' => $options,
'#default_value' => $config['perms'],
'#description' => t('Select which permissions are granted by this scheme. <br/>Note when granting update, it is best to enable visibility on all terms. Otherwise a user may unknowingly remove invisible terms while editing a node.'),
'#required' => FALSE,
);
$form['tac_lite_config_scheme_' . $i]['term_visibility'] = array(
'#type' => 'checkbox',
'#title' => t('Visibility'),
'#description' => t('If checked, this scheme determines whether a user can view <strong>terms</strong>. Note the <em>view</em> permission in the select field above refers to <strong>node</strong> visibility. This checkbox refers to <strong>term</strong> visibility, for example in a content edit form or tag cloud.'),
'#default_value' => $config['term_visibility'],
);
$form['helptext'] = array(
'#type' => 'markup',
'#value' => t('To grant to an individual user, visit the <em>access by taxonomy</em> tab on the account edit page.'),
'#prefix' => '<p>',
'#suffix' => '</p>',
);
$form['helptext2'] = array(
'#type' => 'markup',
'#value' => t('To grant by role, select the terms below.'),
'#prefix' => '<p>',
'#suffix' => '</p>',
);
$vocabularies = taxonomy_get_vocabularies();
$all_defaults = variable_get('tac_lite_grants_scheme_' . $i, array());
$form['tac_lite_grants_scheme_' . $i] = array(
'#tree' => TRUE,
);
foreach ($roles as $rid => $role_name) {
$form['tac_lite_grants_scheme_' . $i][$rid] = array(
'#type' => 'fieldset',
'#tree' => TRUE,
'#title' => check_plain(t('Grant permission by role: !role', array(
'!role' => $role_name,
))),
'#description' => t(''),
'#collapsible' => TRUE,
);
$defaults = isset($all_defaults[$rid]) ? $all_defaults[$rid] : NULL;
foreach ($vids as $vid) {
$v = $vocabularies[$vid];
$form['tac_lite_grants_scheme_' . $i][$rid][$vid] = _taxonomy_term_select(check_plain($v->name), NULL, isset($defaults[$vid]) ? $defaults[$vid] : NULL, $vid, '', TRUE, '<' . t('none') . '>');
}
}
$form['#submit'][] = 'tac_lite_admin_scheme_form_submit';
return system_settings_form($form);
}
else {
return array(
'body' => array(
'#type' => 'markup',
'#value' => t('First select vocabularies on the <a href=!url>settings page</a>.', array(
'!url' => url('admin/user/access/tac_lite'),
)),
),
);
}
}
/**
* Submit function for admin settings form to rebuild the menu.
*/
function tac_lite_admin_scheme_form_submit($form, &$form_state) {
variable_set('menu_rebuild_needed', TRUE);
}
/**
* Implementation of hook_user().
*/
function tac_lite_user($op, $edit, $account, $category = NULL) {
if (!user_access('administer tac_lite')) {
// Only for tac_lite administrators.
return;
}
switch ($op) {
case 'categories':
return array(
array(
'name' => 'tac_lite',
'title' => t('Access by taxonomy'),
'weight' => 5,
'access callback' => 'user_access',
'access arguments' => array(
'administer users',
),
),
);
break;
case 'form':
$vocabularies = taxonomy_get_vocabularies();
if ($category == 'tac_lite') {
$vids = variable_get('tac_lite_categories', NULL);
if (count($vids)) {
for ($i = 1; $i <= variable_get('tac_lite_schemes', 1); $i++) {
$config = _tac_lite_config($i);
$terms = isset($account->{$config}['realm']) ? $account->{$config}['realm'] : array();
if ($config['name']) {
$perms = $config['perms'];
if ($config['term_visibility']) {
$perms[] = t('term visibility');
}
$form['tac_lite'][$config['realm']] = array(
'#type' => 'fieldset',
'#title' => $config['name'],
'#description' => t('This scheme controls %perms.', array(
'%perms' => implode(' and ', $perms),
)),
'#tree' => TRUE,
);
foreach ($vids as $vid) {
$v = $vocabularies[$vid];
$form['tac_lite'][$config['realm']][$vid] = _taxonomy_term_select(check_plain($v->name), NULL, isset($terms[$vid]) ? $terms[$vid] : NULL, $vid, '', TRUE, '<' . t('none') . '>');
$form['tac_lite'][$config['realm']][$vid]['#description'] = t('Grant permission to this user by selecting terms. Note that permissions are in addition to those granted based on user roles.');
}
}
}
$form['tac_lite'][0] = array(
'#type' => 'markup',
'#value' => '<p>' . t('You may grant this user access based on the schemes and terms below. These permissions are in addition to <a href="!url">role based grants on scheme settings pages</a>.', array(
'!url' => url('admin/user/access/tac_lite/scheme_1'),
)) . "</p>\n",
'#weight' => -1,
);
return $form;
}
}
break;
}
}
/**
* Implements hook_node_access_records().
*
* We are given a node and we return records for the node_access table. In
* our case, we inpect the node's taxonomy and grant permissions based on the
* terms.
*/
function tac_lite_node_access_records($node) {
// all terms from all vocabs
$all_tids = _tac_lite_get_terms($node);
// just the vocabs we're interested in
$vids = variable_get('tac_lite_categories', NULL);
// now find just the terms we're interested in.
$tids = array();
if (count($all_tids) && count($vids)) {
$result = db_query("SELECT DISTINCT td.tid FROM {term_data} td WHERE td.vid IN (%s) AND td.tid IN (%s)", implode(',', $vids), implode(',', $all_tids));
while ($term = db_fetch_object($result)) {
$tids[] = $term->tid;
}
}
if (!count($tids)) {
// no relevant terms found.
// in drupal 4-7 we had to write a row into the database. In drupal 5 and later, it should be safe to do nothing.
}
else {
// if we're here, the node has terms associated with it which restrict
// access to the node.
$grants = array();
for ($i = 1; $i <= variable_get('tac_lite_schemes', 1); $i++) {
$config = _tac_lite_config($i);
foreach ($tids as $tid) {
$grant = array(
'realm' => $config['realm'],
'gid' => $tid,
);
foreach ($config['perms'] as $perm) {
$grant[$perm] = TRUE;
}
$grants[] = $grant;
}
}
return $grants;
}
}
/**
* Get terms from a newly udpated node.
* Terms are placed in $node->taxonomy by the form.
*/
function _tac_lite_get_terms(&$node) {
$tids = array();
// emulating code from taxonomy_node_save here.
// note that free tagging vocabs not currently supported
if (count($node->taxonomy)) {
foreach ($node->taxonomy as $term) {
if (is_array($term)) {
foreach ($term as $tid) {
if (is_numeric($tid)) {
$tids[$tid] = $tid;
}
else {
// non-numeric means free-tagging vocabulary.
// we do not support. Do nothing.
}
}
}
elseif (is_object($term)) {
// in drupal 5 term is an object. Is this right?
$tids[$term->tid] = $term->tid;
}
elseif (is_numeric($term)) {
// $term is a tid.
$tids[$term] = $term;
}
elseif ($term) {
drupal_set_message(t('Unexpected term value "%term" in tac_lite.', array(
'%term' => $term,
)), 'error');
}
}
}
return $tids;
}
function _tac_lite_get_terms_by_nid($nid) {
$tids = array();
$terms = taxonomy_node_get_terms($nid);
// terms is now an array of objects. We convert to a simple array of tids
foreach ($terms as $term) {
$tids[$term->tid] = $term->tid;
}
return $tids;
}
/**
* Return the term ids of terms this user is allowed to access.
*
* Users are granted access to terms either because of who they are,
* or because of the roles they have.
*/
function _tac_lite_user_tids($account, $scheme) {
// grant id 0 is reserved for nodes which were not given a grant id when they were created. By adding 0 to the grant id, we let the user view those nodes.
$grants = array(
0,
);
$config = _tac_lite_config($scheme);
$realm = $config['realm'];
if (isset($account->{$realm}) && count($account->{$realm})) {
// $account->$realm is array. Keys are vids, values are array of tids within that vocabulary, to which the user has access
foreach ($account->{$realm} as $tids) {
if (count($tids)) {
$grants = array_merge($grants, $tids);
}
}
}
// add per-role grants in addition to per-user grants
$defaults = variable_get('tac_lite_grants_scheme_' . $scheme, array());
foreach ($account->roles as $rid => $role_name) {
if (isset($defaults[$rid]) && count($defaults[$rid])) {
foreach ($defaults[$rid] as $tids) {
if (count($tids)) {
$grants = array_merge($grants, $tids);
}
}
}
}
// Because of some flakyness in the form API and the form we insert under
// user settings, we may have a bogus entry with vid set
// to ''. Here we make sure not to return that.
unset($grants['']);
return $grants;
}
/**
* Implementation of hook_node_grants().
*
* Returns any grants which may give the user permission to perform the
* requested op.
*/
function tac_lite_node_grants($account, $op) {
$grants = array();
for ($i = 1; $i <= variable_get('tac_lite_schemes', 1); $i++) {
$config = _tac_lite_config($i);
if (in_array('grant_' . $op, $config['perms'])) {
$grants[$config['realm']] = _tac_lite_user_tids($account, $i);
}
}
if (count($grants)) {
return $grants;
}
}
function tac_lite_db_rewrite_sql($query, $primary_table, $primary_field, $args) {
global $user;
if ($primary_field != 'tid') {
// Only changes things in link with 'tid'
return;
}
if (user_access('administer tac_lite')) {
// Only for tac_lite administrators.
return;
}
// the vocabularies containing protected info.
$vids = variable_get('tac_lite_categories', NULL);
$schemes = variable_get('tac_lite_schemes', 1);
if (!$vids || !count($vids) || !$schemes) {
return;
}
$term_visibility = FALSE;
// the terms this user is allowed to see
$tids = array();
for ($i = 1; $i <= $schemes; $i++) {
$config = _tac_lite_config($i);
if ($config['term_visibility']) {
$tids = array_merge($tids, _tac_lite_user_tids($user, $i));
$term_visibility = TRUE;
}
}
if ($term_visibility) {
// Prevent query from finding terms the current user does not have permission to see.
$join = "LEFT JOIN {term_data} tac_td ON {$primary_table}.tid = tac_td.tid";
$where = "{$primary_table}.tid IN (" . implode(', ', $tids) . ") OR tac_td.vid NOT IN (" . implode(',', $vids) . ")";
return array(
'join' => $join,
'where' => $where,
);
}
}
Functions
Name | Description |
---|---|
tac_lite_admin_scheme_form | Returns the form for role-based privileges. |
tac_lite_admin_scheme_form_submit | Submit function for admin settings form to rebuild the menu. |
tac_lite_admin_settings | Returns the settings form |
tac_lite_admin_settings_scheme | |
tac_lite_db_rewrite_sql | |
tac_lite_help | Implementation of hook_help(). |
tac_lite_menu | Implementation of hook_menu(). |
tac_lite_node_access_records | Implements hook_node_access_records(). |
tac_lite_node_grants | Implementation of hook_node_grants(). |
tac_lite_perm | Implementation of hook_perm(). |
tac_lite_user | Implementation of hook_user(). |
_tac_lite_admin_settings_submit | This form submit callback ensures that the form values are saved, and also the node access database table is rebuilt. 2008 : Modified by Paulo to be compliant with drupal 6 |
_tac_lite_config | helper function |
_tac_lite_get_terms | Get terms from a newly udpated node. Terms are placed in $node->taxonomy by the form. |
_tac_lite_get_terms_by_nid | |
_tac_lite_user_tids | Return the term ids of terms this user is allowed to access. |