/[drupal]/contributions/modules/taxonomy_access/taxonomy_access.module
ViewVC logotype

Contents of /contributions/modules/taxonomy_access/taxonomy_access.module

Parent Directory Parent Directory | Revision Log Revision Log | View Revision Graph Revision Graph


Revision 1.107 - (show annotations) (download) (as text)
Sat Feb 23 20:48:48 2008 UTC (21 months ago) by keve
Branch: MAIN
CVS Tags: HEAD
Branch point for: DRUPAL-6--1
Changes since 1.106: +1 -27 lines
File MIME type: text/x-php
deleting hook_disable() - no need for it in D6
1 <?php
2 // $Id: taxonomy_access.module,v 1.106 2008/02/23 20:45:19 keve Exp $
3 // Based on original taxonomy_access.module made by pyromanfo
4
5 /**
6 * @file
7 * Allows administrators to specify how each category (in the taxonomy) can be used by various roles.
8 */
9
10
11 /**
12 * Implementation of hook_help
13 */
14 function taxonomy_access_help($path, $arg) {
15 switch ($path) {
16 case 'admin/help#taxonomy_access':
17 $message = '<p>'. t('The Taxonomy Access Control module allows users to specify how each category can be used by various roles.') .'</p>';
18 $message .= '<p>'. t('Permissions can be set differently to each USER ROLES. Be aware that setting Taxonony Access permissions works ONLY WITHIN ONE USER ROLE. <br /><em>(For users with multiple user roles, see section "GOOD TO KNOW" below.)</em>') .'</p>';
19 $message .= '<p>'. t('On the category permissions page for each role, each category displays a list of the terms within it, each with five types of permission: <em>View, Update, Delete, Create</em> and <em>List</em>:') .'</p>';
20 $message .= '<ul>';
21 $message .= '<li>'. t('<strong>VIEW</strong> enables the user to access content (nodes) with given term.') .'</li>';
22 $message .= '<li>'. t('<strong>UPDATE, DELETE</strong> enables the user to Update/Delete <u>ALL</u> nodes with the given term. <br><em>(These two permissions are <u>administrator permissions</u>, that should be given ONLY to e.g. content administrators.)</em>') .'</li>';
23 $message .= '<li>'. t('<strong>CREATE</strong> enables the user to set that term when adding a new node or when editing a node.') .'</li>';
24 $message .= '<li>'. t('<strong>LIST</strong> enables the user to view the name of the given term below the title of a node or in category lists. It also controls whether a user can access the taxonomy page for the given term. (e.g. "taxonomy/term/*")') .'</li>';
25 $message .= '</ul>';
26 $message .= '<p>'. t('VIEW, UPDATE, and DELETE control the node access system. LIST and CREATE control if a user can view and select a given term. (Note: In previous versions of Taxonomy Access Control, there was no LIST permission; its functionality was controlled by the VIEW permission.)') .'</p>';
27 $message .= '<p>'. t('<strong>VIEW, UPDATE and DELETE have three options for each term: <u>A</u>llow, <u>I</u>gnore, and <u>D</u>eny.</strong> Indicate which rights each role should have for each term.') .'</p>';
28 $message .= '<p>'. t('<strong>CREATE and LIST have only two options for each term: YES (selected) or NO (deselected).</strong> Indicate what each role should be allowed to do with each term.') .'</p>';
29 $message .= '<p>'. t('<strong>IMPORTANT NOTE:</strong><br><u>The DENY directives are processed after the ALLOW directives. (DENY overrides ALLOW.)</u> So, if a multicategory node is in Categories "A" and "B" and a user has ALLOW permissions for VIEW in Category "A" and DENY permissions for VIEW in Category "B", then the user will NOT be permitted to VIEW the node. (DENY overrides ALLOW.)<br><u>Access is denied by default.</u> So, if a multicategory node is in Categories "C" and "D" and a user has IGNORE permissions for VIEW in both Category "C" and "D", then the user will NOT be permitted to VIEW the node.<br>(If you are familiar with Apache mod_access, this permission system works similar to directive: <em>ORDER ALLOW, DENY</em>)') .'</p>';
30 $message .= '<p>'. t('<strong>Allow/Ignore/Deny All</strong> or <strong>Select/Deselect All:</strong><br>Beside each vocabulary title there are dropdowns containing the options that can be set for individual terms. Selecting one of these options using the dropdown effectively <u>selects that option for ALL of the individual terms inside that vocabulary when the options are saved.</u><br>Selecting "--" does not make any automatic changes to the permission of the terms in that vocabulary; only manual changes that you make will be saved.<br>NOTE: This does <u>not</u> change the "Default" option (described below).') .'</p>';
31 $message .= '<p>'. t('<strong>Default:</strong><br>This option, just underneath the vocabulary title, <u>sets the permission that will automatically be given</u> to the role, <u>for any new terms</u> that are added within the vocabulary. This includes terms that are added via free tagging.') .'</p>';
32 $message .= '<p><strong>'. t('GOOD TO KNOW:') .'</strong></p>';
33 $message .= '<p>'. t('<strong>Users with multiple user roles:</strong> Allow/Ignore/Deny options are interpreted <u>only within one user role</u>. When a user belongs to multiple user roles, then <u>user gets access if ANY of his user roles</u> has the access granted. <br />In this case, permissions for the given user are calculated, so that the <u>permissions of ALL of his user roles are "OR-ed" together</u>. Meaning that Allow will take precedence over Deny. This is different from how node access permissions (for multi-category nodes) are handled within ONE USER ROLE, as noted above.') .'</p>';
34 $message .= '<p>'. t('<br><strong>Input formats:</strong> <u>Node editing/deleting is blocked</u>, even when user has <em>UPDATE/DELETE</em> permission to the node, <u>when user is not allowed to use a filter format</u> that the node was saved at.') .'</p>';
35 $message .= '<p>&nbsp;</p>';
36 return $message;
37
38 // TODO: update help
39 /* default:
40 if (strpos($path,'admin/user/taxonomy_access') === 0) {
41 $rid = arg(3);
42 if (isset($tac_user_roles[$rid]) && $tac_user_roles[$rid]) {
43 $output = t('<p><strong>Vocabulary Settings:</strong> Each vocabulary displays a list of the terms within it, each with five types of permission: <em>View, Update, Delete, Create</em> and <em>List</em>.</p><p>For a detailed description of these permissions and how to use them, see <a href="@taxonomy_access_help">Taxonomy Access Control help</a>. If you are new to Taxonomy Access Control, it is very important that you read the help page.</p>', array('@taxonomy_access_help' => url('admin/help/taxonomy_access')));
44 return $output;
45 }
46 else {
47 return '<p>'.t('In this area you will define the permissions that each <a href="@role">user role</a> has for each category. Each category can have <em>View, Update, Delete, Create</em> and <em>List</em> permissions set for each user role.', array('@role' => url('admin/user/roles'))).'</p>';
48 }
49 }*/
50 }
51 }
52
53 /**
54 * Implementation of hook_enable().
55 *
56 * Housekeeping: while we were away, did you delete any terms/vocabs/roles?
57 * 1: delete ta rows for missing terms
58 * 2: delete tad rows for missing vocabs
59 * 3: delete ta, tad rows for missing roles
60 * 4: rebuild node_access
61 */
62 function taxonomy_access_enable() {
63 db_query('DELETE ta FROM {term_access} ta LEFT JOIN {term_data} td ON ta.tid = td.tid WHERE ta.tid <> 0 AND ISNULL(td.tid)');
64 db_query('DELETE tad FROM {term_access_defaults} tad LEFT JOIN {vocabulary} v ON tad.vid = v.vid WHERE tad.vid <> 0 AND ISNULL(v.vid)');
65 db_query('DELETE ta FROM {term_access} ta LEFT JOIN {role} r ON ta.rid = r.rid WHERE ISNULL(r.rid)');
66 db_query('DELETE tad FROM {term_access_defaults} tad LEFT JOIN {role} r ON tad.rid = r.rid WHERE ISNULL(r.rid)');
67 }
68
69 /**
70 * Implementation of hook_node_grants()
71 * Gives access to taxonomies based on the taxonomy_access table
72 */
73 function taxonomy_access_node_grants($user, $op) {
74 return array('term_access' => array_keys(is_array($user->roles) ? $user->roles : array(1 => 'anonymous user')));
75 }
76
77 /**
78 * Implementation of hook_node_access_records().
79 * TODO: change query to behave properly when no term or vocab record is present
80 */
81 function taxonomy_access_node_access_records($node) {
82 $grants = array();
83
84 if (is_array($node->taxonomy) AND count($node->taxonomy)) {
85 // TODO: how does deny/ignore work with this?
86 $result = db_query('SELECT tadg.rid,
87 BIT_OR(COALESCE( ta.grant_view, tad.grant_view, tadg.grant_view )) AS grant_view,
88 BIT_OR(COALESCE( ta.grant_update, tad.grant_update, tadg.grant_update )) AS grant_update,
89 BIT_OR(COALESCE( ta.grant_delete, tad.grant_delete, tadg.grant_delete )) AS grant_delete
90 FROM {term_node} tn
91 INNER JOIN {term_data} t ON t.tid = tn.tid
92 INNER JOIN {term_access_defaults} tadg ON tadg.vid = 0
93 LEFT JOIN {term_access_defaults} tad ON tad.vid = t.vid AND tad.rid = tadg.rid
94 LEFT JOIN {term_access} ta ON ta.tid = t.tid AND ta.rid = tadg.rid
95 WHERE tn.nid = %d
96 GROUP BY tadg.rid', $node->nid);
97 }
98 else {
99 // Use the default for nodes with no category
100 $result = db_query('SELECT n.nid, tadg.rid AS rid, tadg.grant_view AS grant_view, tadg.grant_update AS grant_update, tadg.grant_delete AS grant_delete
101 FROM {node} n INNER JOIN {term_access_defaults} tadg ON tadg.vid = 0
102 WHERE n.nid = %d', $node->nid);
103 }
104 // A bit of explanation for future generations regarding the deny/allow override behavior:
105 // only a value of '1' is considered an 'allow'. Ignore (really a weak deny) is '0', deny is '2' ('10' in binary).
106 // Allow always gets through the BIT_OR above as '1', unless a deny is present, in which case the value will be '3' ('11' in binary)
107
108 while ($row = db_fetch_array($result)) {
109 $grants[] = array(
110 'realm' => 'term_access',
111 'gid' => $row['rid'],
112 'grant_view' => ($row['grant_view'] == 1) ? 1 : 0,
113 'grant_update' => ($row['grant_update'] == 1) ? 1 : 0,
114 'grant_delete' => ($row['grant_delete'] == 1) ? 1 : 0,
115 'priority' => 0,
116 );
117 }
118
119 return $grants;
120 }
121
122 /**
123 * Implementation of hook_init
124 */
125 function taxonomy_access_init() {
126 if (arg(0) == 'admin' AND ((arg(1) == 'user' AND arg(2) == 'taxonomy_access') OR (arg(1) == 'content' AND arg(2) == 'taxonomy'))) {
127 // Only include administrative callbacks and css if we are viewing an admin page.
128 $path = drupal_get_path('module', 'taxonomy_access');
129 include_once($path .'/taxonomy_access_admin.inc');
130 drupal_add_css($path .'/admin.css');
131 }
132 }
133
134 /**
135 * Implementation of hook_theme().
136 */
137 function taxonomy_access_theme() {
138 return array(
139 'taxonomy_access_admin_form' => array(
140 'arguments' => array('form' => NULL),
141 ),
142 );
143 }
144
145 /**
146 * Implementation of hook_menu
147 */
148 function taxonomy_access_menu() {
149 $items = array();
150
151 $items['admin/user/taxonomy_access'] = array(
152 'title' => t('Taxonomy access permissions'),
153 'description' => t('Taxonomy-based access control for content'),
154 'page callback' => 'taxonomy_access_admin',
155 'access arguments' => array('administer access control'),
156 );
157 $items['admin/user/taxonomy_access/delete'] = array(
158 'type' => MENU_CALLBACK,
159 'page callback' => 'drupal_get_form',
160 'page arguments' => array('taxonomy_access_admin_delete_role'),
161 'access arguments' => array('administer access control'),
162 );
163 $items['admin/user/taxonomy_access/edit'] = array(
164 'type' => MENU_CALLBACK,
165 'page callback' => 'drupal_get_form',
166 'page arguments' => array('taxonomy_access_admin_form'),
167 'access arguments' => array('administer access control'),
168 );
169
170 return $items;
171 }
172
173 /**
174 * Implementation of hook_form_alter()
175 */
176 function taxonomy_access_form_alter(&$form, &$form_state, $form_id) {
177 //TODO: Move control of "create" op here
178 //TODO: look at feasability to eliminate _restore_terms and _preserve_terms by simply setting the '#access' attribute for those terms
179 if ($form['#id'] == 'node-form' && is_numeric($form['nid']['#value'])) {
180 $form['tac_protected_terms'] = array(
181 '#type' => 'value',
182 '#value' => taxonomy_access_preserve_terms($form['#node'])
183 );
184 }
185 }
186
187 /**
188 * Implementation of hook_nodeapi().
189 */
190 function taxonomy_access_nodeapi(&$node, $op, $arg = 0) {
191 switch ($op) {
192 case 'presave':
193 // When TAC grants 'update' access to edit node,
194 // Changing $node->uid back to original creator (changed by node_submit)
195 // TODO: do we still need this? (with node_revisions table etc)
196 if (($node->nid) && !user_access('administer nodes') && (node_access('update', $node))) {
197 // Populate the "authored by" field.
198 $old_node = node_load($node->nid);
199 if ($account = user_load(array('name' => $old_node->name))) {
200 $node->uid = $account->uid;
201 }
202 else {
203 $node->uid = 0;
204 }
205 }
206 break;
207
208 case 'update':
209 // restore terms that the user shouldn't have access to delete
210 taxonomy_access_restore_terms($node->nid, $node->tac_protected_terms);
211 break;
212 }
213 }
214
215 /**
216 * Implementation of hook_taxonomy
217 * Hook_taxonomy is called when changes are made to the taxonomy structure
218 **/
219 function taxonomy_access_taxonomy($op, $type, $array = NULL) {
220 if ($type == 'term') {
221 switch ($op) {
222 case 'delete': // delete everything from term_access and node_access
223 // issue #167977 - klance
224 $affected_nodes = _taxonomy_access_get_nodes_for_term($array['tid']);
225 db_query('DELETE FROM {term_access} WHERE tid = %d', $array['tid']);
226 // issue #167977 - klance
227 _taxonomy_access_node_access_update($affected_nodes);
228 //node_access_rebuild();
229 break;
230 }
231 }
232 if ($type == 'vocabulary') {
233 switch ($op) {
234 case 'delete': // delete vocabulary from table 'term_access_defaults'
235
236 // issue #167977 - klance
237 $affected_nodes = _taxonomy_access_get_nodes_for_vocabulary($array['vid'], NULL);
238 db_query('DELETE FROM {term_access_defaults} WHERE vid = %d', $array['vid']);
239 // issue #167977 - klance
240 _taxonomy_access_node_access_update($affected_nodes);
241 // TODO: need rebuild here? can we avoid multiple rebuilds on large vocab delete?
242 break;
243 }
244 }
245 return;
246 }
247
248 /**
249 * Provide default values for term_access_defaults
250 */
251 function _taxonomy_access_defaults($vid, $rid) {
252 if ($rid == 1 || $rid == 2)
253 return array($vid, $rid, 1, 0, 0, 1, 1);
254 else
255 return array($vid, $rid, 0, 0, 0, 0, 0);
256 }
257
258 /**
259 * Implementation of hook_db_rewrite_sql()
260 */
261 function taxonomy_access_db_rewrite_sql($query, $table, $field) {
262 if (!user_access('administer taxonomy') && ($field =='vid' || $field =='tid')) {
263 global $user;
264 $op = (arg(0) == 'node' && (arg(1) == 'add' || arg(2) == 'edit')) ? 'create' : 'list';
265
266 // let's cache
267 static $taxonomy_access_sql_clause;
268 $clause = array();
269
270 if (!isset($taxonomy_access_sql_clause)) {
271 $taxonomy_access_sql_clause = array();
272 }
273 if (!isset($taxonomy_access_sql_clause[$op][$field])) {
274 if (isset($user) && is_array($user->roles)) {
275 $rids = array_keys($user->roles);
276 }
277 else {
278 $rids[] = 1;
279 }
280
281 $sql = db_query('SELECT t.tid AS tid, t.vid AS vid FROM {term_data} t
282 INNER JOIN {term_access_defaults} tdg ON tdg.vid=0
283 LEFT JOIN {term_access_defaults} td ON td.vid=t.vid AND td.rid=tdg.rid
284 LEFT JOIN {term_access} ta ON ta.tid=t.tid AND ta.rid=tdg.rid
285 WHERE tdg.rid IN ('. implode(',', $rids) .")
286 GROUP BY t.tid, t.vid HAVING BIT_OR(COALESCE(ta.grant_$op, td.grant_$op, tdg.grant_$op)) > 0");
287 // issue #172675 - Ddaffyd
288 // GROUP BY t.tid HAVING BIT_OR(COALESCE(ta.grant_$op, td.grant_$op, tdg.grant_$op))");
289
290 while ($result = db_fetch_object($sql)) {
291 $tids[]= $result->tid;
292 $vids[$result->vid]= $result->vid;
293 }
294
295 // Insert required vocabularies to avoid skipping of validation at node submission
296 if ($op == 'create') {
297 $sql = db_query('SELECT vid FROM {vocabulary} WHERE required = 1 OR tags = 1');
298 while ($row = db_fetch_array($sql)) {
299 $vids[$row['vid']] = $row['vid'];
300 }
301 }
302
303 $clause[$op]['tid'] = isset($tids) ? implode("','", $tids) : '';
304 $clause[$op]['vid'] = isset($vids) ? implode("','", $vids) : '';
305 $taxonomy_access_sql_clause = $clause;
306 }
307 else {
308 $clause[$op][$field] = $taxonomy_access_sql_clause[$op][$field];
309 }
310 $return['where'] = ($clause[$op][$field]) ? "$table.$field IN ('". $clause[$op][$field] ."')" : "$table.$field IS NULL";
311 return $return;
312 }
313 else {
314 return array();
315 }
316 }
317
318 /**
319 * used to preserve terms deleted by taxonomy_node_delete()
320 * that the user shouldn't have access to delete.
321 * see http://drupal.org/node/92355 and http://drupal.org/node/93086
322 *
323 * Called by hook_form_alter()
324 */
325 // TODO: should be possible to replace this with #access per term-field
326 function taxonomy_access_preserve_terms($node) {
327 $nid = $node->nid;
328
329 // prepare/cache return value
330 static $tids = array();
331
332 // a valid numeric nid is required
333 if (!is_numeric($nid)) {
334 return array();
335 }
336
337 // use cached values if possible
338 if (isset($tids[$nid])) {
339 return $tids[$nid];
340 }
341
342 // get a list of terms this user has access to/over
343 // (invokes hook_db_rewrite_sql() to limit access)
344 $user_terms = taxonomy_node_get_terms($node);
345
346 // get a list of all terms this node is in regardless of user's
347 // access settings. Don't use db_rewrite_sql() api call here (or
348 // any other API call, the taxonomy API functions all use
349 // db_rewrite_sql() so we must query the database tables directly)
350 $result = db_query('SELECT tid FROM {term_node} WHERE nid = %d', $nid);
351 while ($row = db_fetch_array($result)) {
352 // only include those terms the current user does not have access to
353 if (!isset($user_terms[$row['tid']])) {
354 $tids[$nid][$row['tid']] = $row['tid'];
355 }
356 }
357
358 // return only terms current user does not have access
359 // to and therefore need restoring after edit/update
360 return $tids[$nid];
361 }
362
363 /**
364 * used to restore terms deleted by taxonomy_node_delete()
365 * that the user shouldn't have access to delete.
366 * see http://drupal.org/node/92355 and http://drupal.org/node/93086
367 *
368 * Called by taxonomy_access_nodeapi()
369 */
370 function taxonomy_access_restore_terms($nid, $protected_terms) {
371 if (isset($protected_terms)) {
372 $terms = $protected_terms;
373 if (count($terms)) {
374 db_query('DELETE FROM {term_node} WHERE nid = %d AND tid IN ('. implode(',', $terms) .')', $nid);
375 foreach ($terms as $tid) {
376 $replace_terms[] = '('. $nid .','. $tid .')';
377 }
378 db_query('INSERT INTO {term_node} (nid,tid) VALUES '. implode(',', $replace_terms));
379 }
380 }
381 }

  ViewVC Help
Powered by ViewVC 1.1.2