' . 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.') . '

'; $output .= '

' . t('While this module has been designed to be as simple as possible to use, there are several steps required to set it up.') . '

'; $output .= '
    '; $output .= '
  1. ' . 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\'.') . '
  2. '; $output .= '
  3. ' . 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'))) . '
  4. '; $output .= '
  5. ' . 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'))) . '
  6. '; $output .= '
  7. ' . t('Grant access to individual users. (See the taxonomy-based access tab under user -> edit.)') . '
  8. '; $output .= '
  9. ' . 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.') . '
  10. '; $output .= '
'; $output .= '

' . t('Currently, this module works with view grants only (no update or delete grants).') . '

'; 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'), ); } $items['tac_lite/autocomplete'] = array( 'title' => 'TAC lite autocomplete', 'page callback' => 'tac_lite_autocomplete', 'access arguments' => array('access content'), 'type' => MENU_CALLBACK ); return $items; } function get_terms_from_tids($vid, $tids){ //print_r($tids); echo '---
'; if (!is_array($tids) || !$vid) return false; $s=''; foreach ($tids as $tid) if ($tid) { $result = db_query_range(db_rewrite_sql("SELECT name FROM {term_data} WHERE vid = $vid AND tid = $tid", 'term_data', 'tid'), 0, 1); while ($tag = db_fetch_object($result)) { $s.= $tag->name .', '; } } return $s; } function get_tids_from_terms($vid, $terms){ //print_r($terms); $tids=array(); if (is_array($terms) && $vid) foreach ($terms as $term){ $result = db_query_range(db_rewrite_sql("SELECT tid FROM {term_data} WHERE vid = $vid AND name = '$term'", 'term_data', 'tid'), 0, 1); while ($tag = db_fetch_object($result)) { $tids[$tag->tid]=$tag->tid; } } //echo '-B-'; print_r($tids); echo '-E-'; return $tids; } function tac_lite_autocomplete($vid, $string = '') { // The user enters a comma-separated list of tags. We only autocomplete the last tag. // This regexp allows the following types of user input: // this, "somecmpany, llc", "and ""this"" w,o.rks", foo bar $regexp = '%(?:^|,\ *)("(?>[^"]*)(?>""[^"]* )*"|(?: [^",]*))%x'; preg_match_all($regexp, $string, $matches); $array = $matches[1]; // print_r($string); die(); // Fetch last tag $last_string = trim(array_pop($array)); /* $p=strrpos($string, ','); if (!$p) $p=-1; $last_string =trim(substr($string, $p+1)); */ $matches = array(); //echo '--- '.$vid.' ---- '.$last_string; if ($last_string != '') { $result = db_query_range(db_rewrite_sql("SELECT t.tid, t.name AS name FROM {term_data} t WHERE t.vid = $vid AND t.name LIKE '%".$last_string."%'", 't', 'tid'), 0, 10); $prefix = count($array) ? implode(', ', $array) .', ' : ''; while ($tag = db_fetch_object($result)) { $n = $tag->name; $parents = taxonomy_get_parents_all($tag->tid); // Commas and quotes in terms are special cases, so encode 'em. if (strpos($tag->name, ',') !== FALSE || strpos($tag->name, '"') !== FALSE) { $n = '"'. str_replace('"', '""', check_plain($tag->name)) .'"'; } if(count($parents)>1){ $parent_array = array(); $parents = array_reverse($parents); array_pop($parents); foreach($parents as $parent){ $parent_array[] = check_plain($parent->name); } $matches[$prefix . $n] = check_plain($tag->name) ." (".implode(" » ",$parent_array).")"; } else { $matches[$prefix . $n] = check_plain($tag->name); } } } print drupal_to_js($matches); exit; } /** * 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 create a vocabulary 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.
Use caution with hierarchical (nested) taxonomies as visibility settings may cause problems on node edit forms.
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(); //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.
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 terms. Note the view permission in the select field above refers to node visibility. This checkbox refers to term 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 access by taxonomy tab on the account edit page.'), '#prefix' => '

', '#suffix' => '

', ); $form['helptext2'] = array( '#type' => 'markup', '#value' => t('To grant by role, select the terms below.'), '#prefix' => '

', '#suffix' => '

', ); $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', //'textfield', // '#tree' => TRUE, '#title' => check_plain(t('Grant permission by role: !role', array('!role' => $role_name))), '#description' => t(''), '#collapsible' => TRUE, '#autocomplete_path' => 'tac_lite/autocomplete/'.$form['#vocabulary']['vid'] ); $defaults = isset($all_defaults[$rid]) ? $all_defaults[$rid] : NULL; foreach ($vids as $vid) { $v = $vocabularies[$vid]; $form['tac_lite_grants_scheme_' . $i][$rid][$vid] = array( '#type' => 'textfield', //'#maxlength' => 10000, '#title' => $v->name, '#default_value' => get_terms_from_tids($vid, $defaults[$vid]), '#autocomplete_path' => 'tac_lite/autocomplete/'.$vid); } } $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 settings page.', array('!url' => url('admin/user/access/tac_lite')))))); } } function tac_lite_split_field($string, $vid){ // Add slashes first $string = addslashes($string); $regexp = '%(?:^|,\ *)("(?>[^"]*)(?>""[^"]* )*"|(?: [^",]*))%x'; preg_match_all($regexp, $string, $matches); $string_array = $matches[1]; //echo 's: '; print_r($string_array); return get_tids_from_terms($vid, $string_array); } /** * Submit function for admin settings form to rebuild the menu. */ function tac_lite_admin_scheme_form_submit($form, &$form_state) { //die('xxx'); $vids = variable_get('tac_lite_categories', NULL); $vocabularies = taxonomy_get_vocabularies(); $schemes = variable_get('tac_lite_schemes', 1); $roles = user_roles(); for ($i = 1; $i <= $schemes; $i++) { foreach ($form_state['values']['tac_lite_grants_scheme_'.$i] as &$rid) { //print_r($rid); die(); foreach ($vids as $vid) { $rid[$vid] = tac_lite_split_field($rid[$vid], $vid); } } } variable_set('menu_rebuild_needed', TRUE); } function tac_lite_user_form_submit($form, &$form_state) { //print_r($form_state['values']);die('xxx'); $x=array(); $x['tac_lite']=array(); $x['form_build_id']=$form_state['values']['_account']->form_build_id; $vids = variable_get('tac_lite_categories', NULL); foreach ($vids as $vid) { $x['tac_lite'][$vid]=tac_lite_split_field($form_state['values'][$vid], $vid); } //print_r(serialize($x)); //die(); db_query("UPDATE {users} SET data='%s' WHERE uid=".$form_state['values']['_account']->uid, serialize($x)); } /** * 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); //print_r($account); $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_grants_scheme_' . $i][$rid][$vid] = array( '#type' => 'textfield', //'#maxlength' => 10000, '#title' => $v->name, //'#description' => $form['taxonomy'][$vid]['#description'], '#default_value' => get_terms_from_tids($vid, $terms[$vid]), '#autocomplete_path' => 'tac_lite/autocomplete/'.$vid); $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' => '

' . t('You may grant this user access based on the schemes and terms below. These permissions are in addition to role based grants on scheme settings pages.', array('!url' => url('admin/user/access/tac_lite/scheme_1'))) . "

\n", '#weight' => -1, ); $form['#submit'][] = 'tac_lite_user_form_submit'; 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, // use term id as grant id ); 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); } }