#803824 by moshe weitzman. drush generate-content fails on types param. D6.
[project/devel.git] / devel_node_access.module
CommitLineData
361dbb76 1<?php
2120c1bd 2// $Id$
361dbb76 3/**
3c1264d3
JC
4 * @file
5 *
2d35faff 6 * This module gives developers feedback as to what their
361dbb76
DC
7 * node_access table contains, and which nodes are protected or
8 * visible to the public.
3c1264d3 9 *
361dbb76
DC
10 */
11
12define('DNA_ACCESS_VIEW', 'view devel_node_access information');
13
14function devel_node_access_perm() {
1674b6e9 15 return array('view devel_node_access information');
361dbb76
DC
16}
17
18/**
19 * Implementation of hook_help().
20 */
91d809c6 21function devel_node_access_help($path, $arg) {
22 switch ($path) {
23 case 'admin/settings/modules#description':
24 return t('Development helper for node_access table');
25 break;
26 case 'admin/help#devel_node_access':
2f9fa4a3 27 $output = '<p>'. t('This module helps in site development. Specifically, when an access control module is used to limit access to some or all nodes, this module provides some feedback showing the node_access table in the database.') ."</p>\n";
28 $output .= '<p>'. t('The node_access table is one method Drupal provides to hide content from some users while displaying it to others. By default, Drupal shows all nodes to all users. There are a number of optional modules which may be installed to hide content from some users.') ."</p>\n";
2d35faff 29 $output .= '<p>'. t('If you have not installed any of these modules, you really have no need for the devel_node_access module. This module is intended for use during development, so that developers and admins can confirm that the node_access table is working as expected. You probably do not want this module enabled on a production site.') ."</p>\n";
30 $output .= '<p>'. t('This module provides two blocks. One called Devel Node Access by User is visible when a single node is shown on a page. This block shows which users can view, update or delete the node shown. Note that this block uses an inefficient algorithm to produce its output. You should only enable this block on sites with very few user accounts.') ."</p>\n";
22c8c8e4 31 $output .= '<p>'. t('The second block provided by this module shows the entries in the node_access table for any nodes shown on the current page. You can enable the debug mode on the <a href="@settings_page">settings page</a> to display much more information, but this can cause considerable overhead. Because the tables shown are wide, it is recommended to enable the blocks in the page footer rather than a sidebar.',
32 array('@settings_page' => url('admin/settings/devel', array('fragment' => 'edit-devel-node-access-debug-mode')))
2d35faff 33 ) ."</p>\n";
22c8c8e4 34 $output .= '<p>'. t('This module also provides a <a href="@summary_page">summary page</a> which shows general information about your node_access table. If you have installed the Views module, you may browse node_access by realm.',
35 array('@summary_page' => url('devel/node_access/summary'))
2f9fa4a3 36 ) ."</p>\n";
91d809c6 37 return $output;
361dbb76
DC
38 }
39}
40
aa3e9ef2 41function devel_node_access_menu() {
361dbb76 42 $items = array();
aa3e9ef2 43
24b12983 44 // add this to the custom menu 'devel' created by devel module.
aa3e9ef2 45 $items['devel/node_access/summary'] = array(
2f9fa4a3 46 'title' => 'Node_access summary',
47 'page callback' => 'dna_summary',
48 'access arguments' => array(DNA_ACCESS_VIEW),
49 'menu_name' => 'devel',
50 );
aa3e9ef2 51
a921952b 52 if (!module_exists('devel')) {
53 $items['admin/settings/devel'] = array(
54 'title' => 'Devel node access',
55 'description' => 'Helper pages and blocks to assist Drupal developers and admins with node_access. The devel blocks can be managed via the Blocks (admin/build/block) page.',
56 'page callback' => 'drupal_get_form',
57 'page arguments' => array('devel_node_access_admin_settings'),
58 'access arguments' => array('administer site configuration'),
59 'type' => MENU_NORMAL_ITEM
60 );
61 }
62
361dbb76
DC
63 return $items;
64}
65
a921952b 66function devel_node_access_admin_settings() {
67 $form = array();
68 return system_settings_form($form);
69}
70
71function devel_node_access_form_alter(&$form, $form_state, $form_id) {
22c8c8e4 72 $tr = 't';
a921952b 73 if ($form_id == 'devel_admin_settings' || $form_id == 'devel_node_access_admin_settings') {
74 $form['devel_node_access_debug_mode'] = array(
75 '#type' => 'checkbox',
76 '#title' => t('Devel Node Access debug mode'),
77 '#default_value' => variable_get('devel_node_access_debug_mode', FALSE),
3ec8e29b 78 '#description' => t('Debug mode verifies the grant records in the node_access table against those that would be set by running !Rebuild_permissions, and displays them all; this can cause considerable overhead.', array('!Rebuild_permissions' => l('['. $tr('Rebuild permissions') .']', 'admin/content/node-settings'))),
a921952b 79 );
80 // push these down:
81 $form['devel_error_handler']['#weight'] = 1;
82 $form['smtp_library']['#weight'] = 1;
83 $form['buttons']['#weight'] = 2;
84 }
85}
86
361dbb76
DC
87function dna_summary() {
88 // Warn user if they have any entries that could grant access to all nodes
91d809c6 89 $output = '';
361dbb76 90 $result = db_query('SELECT DISTINCT realm FROM {node_access} WHERE nid=0 AND gid=0');
aa3e9ef2 91 $rows = array();
92 while ($row = db_fetch_object($result)) {
93 $rows[] = array($row->realm);
94 }
91d809c6 95 if (!empty($rows)) {
2f9fa4a3 96 $output .= '<h3>'. t('Access Granted to All Nodes (All Users)') ."</h3>\n";
97 $output .= '<p>'. t('Your node_access table contains entries that may be granting all users access to all nodes. Depending on which access control module(s) you use, you may want to delete these entries. If you are not using an access control module, you should probably leave these entries as is.') ."</p>\n";
91d809c6 98 $headers = array(t('realm'));
99 $output .= theme('table', $headers, $rows);
b98126b3 100 $access_granted_to_all_nodes = TRUE;
361dbb76
DC
101 }
102
103 // how many nodes are not represented in the node_access table
3ec8e29b 104 $result = db_fetch_object(db_query('SELECT COUNT(n.nid) AS num_nodes FROM {node} n LEFT JOIN {node_access} na ON n.nid = na.nid WHERE na.nid IS NULL'));
361dbb76 105 if ($num = $result->num_nodes) {
2f9fa4a3 106 $output .= '<h3>'. t('Legacy Nodes') ."</h3>\n";
91d809c6 107 $output .= '<p>'.
2f9fa4a3 108 t('You have !num nodes in your node table which are not represented in your node_access table. If you have an access control module installed, these nodes may be hidden from all users. This could be caused by publishing nodes before enabling the access control module. If this is the case, manually updating each node should add it to the node_access table and fix the problem.', array('!num' => l($num, 'devel/node_access/view/NULL')))
109 ."</p>\n";
b98126b3 110 if (!empty($access_granted_to_all_nodes)) {
111 $output .= '<p>'.
112 t('This issue may be masked by the one above, so look into the former first.')
113 ."</p>\n";
114 }
361dbb76
DC
115 }
116 else {
2f9fa4a3 117 $output .= '<h3>'. t('All Nodes Represented') ."</h3>\n";
118 $output .= '<p>'. t('All nodes are represented in the node_access table.') ."</p>\n";
361dbb76
DC
119 }
120
121
122 // a similar warning to the one above, but slightly more specific
2f9fa4a3 123 $result = db_query('SELECT DISTINCT realm FROM {node_access} WHERE nid = 0 AND gid <> 0');
aa3e9ef2 124 $rows = array();
125 while ($row = db_fetch_object($result)) {
126 $rows[] = array($row->realm);
127 }
91d809c6 128 if (!empty($rows)) {
2f9fa4a3 129 $output .= '<h3>'. t('Access Granted to All Nodes (Some Users)') ."</h3>\n";
130 $output .= '<p>'. t('Your node_access table contains entries that may be granting some users access to all nodes. This may be perfectly normal, depending on which access control module(s) you use.') ."</p>\n";
91d809c6 131 $headers = array(t('realm'));
132 $output .= theme('table', $headers, $rows);
361dbb76
DC
133 }
134
135
136 // find specific nodes which may be visible to all users
2f9fa4a3 137 $result = db_query('SELECT DISTINCT realm, COUNT(DISTINCT nid) as node_count FROM {node_access} WHERE gid = 0 AND nid > 0 GROUP BY realm');
aa3e9ef2 138 $rows = array();
139 while ($row = db_fetch_object($result)) {
140 $rows[] = array($row->realm,
2f9fa4a3 141 array('data' => $row->node_count,
142 'align' => 'center'));
aa3e9ef2 143 }
91d809c6 144 if (!empty($rows)) {
2f9fa4a3 145 $output .= '<h3>'. t('Access Granted to Some Nodes') ."</h3>\n";
91d809c6 146 $output .= '<p>'.
2f9fa4a3 147 t('The following realms appear to grant all users access to some specific nodes. This may be perfectly normal, if some of your content is available to the public.')
148 ."</p>\n";
91d809c6 149 $headers = array(t('realm'), t('public nodes'));
150 $output .= theme('table', $headers, $rows, array(), t('Public Nodes'));
361dbb76 151 }
3c1264d3 152
361dbb76
DC
153
154 // find specific nodes protected by node_access table
2f9fa4a3 155 $result = db_query('SELECT DISTINCT realm, COUNT(DISTINCT nid) as node_count FROM {node_access} WHERE gid <> 0 AND nid > 0 GROUP BY realm');
aa3e9ef2 156 $rows = array();
157 while ($row = db_fetch_object($result)) {
a921952b 158 // no Views yet:
159 //$rows[] = array(l($row->realm, "devel/node_access/view/$row->realm"),
160 $rows[] = array($row->realm,
2f9fa4a3 161 array('data' => $row->node_count,
162 'align' => 'center'));
aa3e9ef2 163 }
91d809c6 164 if (!empty($rows)) {
2f9fa4a3 165 $output .= '<h3>'. t('Summary by Realm') ."</h3>\n";
166 $output .= '<p>'. t('The following realms grant limited access to some specific nodes.') ."</p>\n";
91d809c6 167 $headers = array(t('realm'), t('private nodes'));
168 $output .= theme('table', $headers, $rows, array(), t('Protected Nodes'));
361dbb76 169 }
3c1264d3 170
361dbb76
DC
171 return $output;
172}
173
769e07f1 174function dna_visible_nodes($nid = NULL) {
361dbb76
DC
175 static $nids = array();
176 if ($nid) {
2f9fa4a3 177 $nids[$nid] = $nid;
361dbb76
DC
178 }
179 return $nids;
180}
181
182function devel_node_access_nodeapi(&$node, $op, $teaser = NULL, $page = NULL) {
183 if ($op == 'view') {
2f9fa4a3 184 // remember this node, for display in our block
185 dna_visible_nodes($node->nid);
361dbb76
DC
186 }
187}
188
aa425eca 189function _devel_node_access_module_invoke_all() { // array and scalar returns only!
76b9c98c 190 $args = func_get_args();
191 $hook = array_shift($args);
192 $return = array();
193 foreach (module_implements($hook) as $module) {
194 $function = $module .'_'. $hook;
195 $result = call_user_func_array($function, $args);
196 if (isset($result)) {
aa425eca 197 if (is_array($result)) {
198 foreach ($result as $key => $value) {
199 // add name of module that returned the value:
200 $result[$key]['#module'] = $module;
201 }
202 }
203 else {
204 // build array with result keyed by $module:
205 $result = array($module => $result);
76b9c98c 206 }
207 $return = array_merge($return, $result);
208 }
209 }
210 return $return;
211}
212
361dbb76 213function devel_node_access_block($op = 'list', $delta = 0) {
76b9c98c 214 global $user;
898421d0 215 global $theme_key;
216 static $block1_visible, $hint = '';
217 if (!isset($block1_visible)) {
d04716c6 218 $block1_visible = db_result(db_query("SELECT status FROM {blocks} WHERE module = 'devel_node_access' AND delta = '1' AND theme = '%s'", $theme_key));
898421d0 219 if (!$block1_visible) {
220 $hint = t('For per-user access permissions enable the second DNA <a href="@block">block</a>.', array('@block' => url('admin/build/block')));
221 }
222 }
223
361dbb76 224 switch ($op) {
2f9fa4a3 225 case 'list':
226 $blocks[0]['info'] = t('Devel Node Access');
227 $blocks[0]['status'] = 1;
228 $blocks[0]['region'] = 'footer';
7f4add34 229 $blocks[1]['info'] = t('Devel Node Access by User');
230 $blocks[1]['status'] = 0;
231 $blocks[1]['region'] = 'footer';
2f9fa4a3 232 return $blocks;
233
234 case 'view':
235 if (!user_access(DNA_ACCESS_VIEW)) {
236 return;
237 }
2d35faff 238 switch ($delta) {
239 case 0:
240 if (!count(dna_visible_nodes())) {
241 return;
242 }
243
244 // include rows where nid == 0
245 $nids = array_merge(array(0 => 0), dna_visible_nodes());
97c9e69a 246 $result = db_query('SELECT na.*, n.title FROM {node_access} na LEFT JOIN {node} n ON n.nid = na.nid WHERE na.nid IN ('. db_placeholders($nids) .') ORDER BY na.nid, na.realm, na.gid', $nids);
76b9c98c 247
248 if (!variable_get('devel_node_access_debug_mode', FALSE)) {
249 $headers = array(t('node'), t('realm'), t('gid'), t('view'), t('update'), t('delete'), t('explained'));
250 $rows = array();
251 while ($row = db_fetch_object($result)) {
252 $explained = module_invoke_all('node_access_explain', $row);
f4a27524 253 $rows[] = array('<a href="#node-'. $row->nid .'">'. _devel_node_access_get_node_title($row, TRUE) .'</a>',
76b9c98c 254 $row->realm,
255 $row->gid,
256 $row->grant_view,
257 $row->grant_update,
258 $row->grant_delete,
5050cad5 259 implode('<br />', $explained));
76b9c98c 260 }
261 $output = theme('table', $headers, $rows, array('style' => 'text-align: left'));
898421d0 262 $hint = t('To see more details enable <a href="@debug_mode">debug mode</a>.', array('@debug_mode' => url('admin/settings/devel', array('fragment' => 'edit-devel-node-access-debug-mode')))) .' '. $hint;
7f4add34 263 }
76b9c98c 264 else {
e6ed9115 265 $tr = 't';
3ec8e29b 266 $variables = array('!na' => '{node_access}');
76b9c98c 267 $states = array(
3ec8e29b 268 'default' => array(t('default'), 'ok', t('Default grant supplied by core in the absence of any other non-empty grants, in !na.', $variables)),
269 'ok' => array(t('ok'), 'ok', t('Highest priority grant, in !na.', $variables)),
aa425eca 270 'static' => array(t('static'), 'ok', t('Non-standard grant in !na.', $variables)),
b98126b3 271 'unexpected' => array(t('unexpected'), 'warning', t('The 0/0/all/... grant applies to all nodes and all users -- usually it should not be present if any node access module is active!')),
3ec8e29b 272 'ignored' => array(t('ignored'), 'warning', t('Lower priority grant, not in !na and thus ignored.', $variables)),
273 'empty' => array(t('empty'), 'warning', t('Does not grant any access, but could block lower priority grants; not in !na.', $variables)),
274 'missing' => array(t('missing'), 'error', t("Should be in !na but isn't!", $variables)),
275 'illegitimate' => array(t('illegitimate'), 'error', t('Should NOT be in !na because of lower priority!', $variables)),
276 'alien' => array(t('alien'), 'error', t('Should NOT be in !na because of unknown origin!', $variables)),
76b9c98c 277 );
306fd114 278 $active_states = array('default', 'ok', 'static', 'unexpected', 'illegitimate', 'alien');
76b9c98c 279 $headers = array(t('node'), t('prio'), t('status'), t('realm'), t('gid'), t('view'), t('update'), t('delete'), t('explained'));
280 $active_grants = array();
281 while ($active_grant = db_fetch_object($result)) {
282 $active_grants[$active_grant->nid][$active_grant->realm][$active_grant->gid] = $active_grant;
283 }
76b9c98c 284 $all_grants = $checked_grants = $checked_status = array();
285 foreach ($nids as $nid) {
286 $acquired_grants_nid = array();
898421d0 287 if ($node = node_load($nid)) {
76b9c98c 288 // check node_access_acquire_grants()
91588099 289 $grants = _devel_node_access_module_invoke_all('node_access_records', $node);
290 if (!empty($grants)) {
8f001996 291 $top_priority = NULL;
76b9c98c 292 foreach ($grants as $grant) {
91588099 293 $priority = intval($grant['priority']);
8f001996 294 $top_priority = (isset($top_priority) ? max($top_priority, $priority) : $priority);
91588099 295 $grant['priority'] = (isset($grant['priority']) ? $priority : '&ndash;&nbsp;');
76b9c98c 296 $acquired_grants_nid[$priority][$grant['realm']][$grant['gid']] = $grant + array(
bd6c9d19 297 '#title' => _devel_node_access_get_node_title($node),
76b9c98c 298 '#module' => (isset($grant['#module']) ? $grant['#module'] : ''),
299 );
300 }
301 krsort($acquired_grants_nid);
302 }
303 // check node_access_grants()
304 $checked_status[$nid] = $node->status;
305 if ($node->nid && $node->status) {
306 foreach (array('view', 'update', 'delete') as $op) {
307 $checked_grants[$nid][$op] = array_merge(
308 array('all' => array(0)),
309 _devel_node_access_module_invoke_all('node_grants', $user, $op)
310 );
311 }
312 }
313 }
314 // check for grants in the node_access table that aren't returned by node_access_acquire_grants()
315 $found = FALSE;
316 if (isset($active_grants[$nid])) {
317 foreach ($active_grants[$nid] as $realm => $active_grants_realm) {
318 foreach ($active_grants_realm as $gid => $active_grant) {
e6ed9115 319 $count_nonempty_grants = 0;
76b9c98c 320 foreach ($acquired_grants_nid as $priority => $acquired_grants_nid_priority) {
321 if (isset($acquired_grants_nid_priority[$realm][$gid])) {
322 $found = TRUE;
323 }
324 }
e6ed9115 325 if ($acquired_grants_nid_priority = reset($acquired_grants_nid)) { // highest priority only
326 foreach ($acquired_grants_nid_priority as $acquired_grants_nid_priority_realm) {
327 foreach ($acquired_grants_nid_priority_realm as $acquired_grants_nid_priority_realm_gid) {
328 $count_nonempty_grants += (!empty($acquired_grants_nid_priority_realm_gid['grant_view']) || !empty($acquired_grants_nid_priority_realm_gid['grant_update']) || !empty($acquired_grants_nid_priority_realm_gid['grant_delete']));
329 }
330 }
331 }
aa425eca 332 $fixed_grant = (array) $active_grant;
e6ed9115 333 if ($count_nonempty_grants == 0 && $realm == 'all' && $gid == 0 ) {
aa425eca 334 $fixed_grant += array(
76b9c98c 335 'priority' => '&ndash;',
e6ed9115 336 'state' => 'default',
76b9c98c 337 );
338 }
339 elseif (!$found) {
aa425eca 340 $acknowledged = _devel_node_access_module_invoke_all('node_access_acknowledge', $fixed_grant);
341 if (empty($acknowledged)) {
342 // no one acknowledged this record, mark it as alien:
343 $fixed_grant += array(
344 'priority' => '?',
345 'state' => 'alien',
346 );
347 }
348 else {
349 // at least one module acknowledged the record, attribute it to the first one:
350 $fixed_grant += array(
351 'priority' => '&ndash;',
352 'state' => 'static',
353 '#module' => reset(array_keys($acknowledged)),
354 );
355 }
76b9c98c 356 }
e6ed9115 357 else {
358 continue;
359 }
360 $fixed_grant += array(
bd6c9d19 361 'nid' => $nid,
f4a27524 362 '#title' => _devel_node_access_get_node_title($node),
e6ed9115 363 );
364 $all_grants[] = $fixed_grant;
76b9c98c 365 }
366 }
367 }
368 // order grants and evaluate their status
369 foreach ($acquired_grants_nid as $priority => $acquired_grants_priority) {
370 ksort($acquired_grants_priority);
371 foreach ($acquired_grants_priority as $realm => $acquired_grants_realm) {
372 ksort($acquired_grants_realm);
373 foreach ($acquired_grants_realm as $gid => $acquired_grant) {
374 if ($priority == $top_priority) {
375 if (empty($acquired_grant['grant_view']) && empty($acquired_grant['grant_update']) && empty($acquired_grant['grant_delete'])) {
376 $acquired_grant['state'] = 'empty';
377 }
378 else {
379 $acquired_grant['state'] = (isset($active_grants[$nid][$realm][$gid]) ? 'ok' : 'missing');
380 if ($acquired_grant['state'] == 'ok') {
381 foreach (array('view', 'update', 'delete') as $op) {
382 $active_grant = (array) $active_grants[$nid][$realm][$gid];
383 if (empty($acquired_grant["grant_$op"]) != empty($active_grant["grant_$op"]) ) {
384 $acquired_grant["grant_$op!"] = $active_grant["grant_$op"];
385 }
386 }
387 }
388 }
389 }
390 else {
391 $acquired_grant['state'] = (isset($active_grants[$nid][$realm][$gid]) ? 'illegitimate' : 'ignored');
392 }
393 $all_grants[] = $acquired_grant + array('nid' => $nid);
394 }
395 }
396 }
397 }
398 // fill in the table rows
399 $rows = array();
e6ed9115 400 $error_count = 0;
76b9c98c 401 foreach ($all_grants as $grant) {
769e07f1 402 $row = new stdClass();
76b9c98c 403 $row->nid = $grant['nid'];
404 $row->title = $grant['#title'];
405 $row->priority = $grant['priority'];
406 $row->state = array('data' => $states[$grant['state']][0], 'title' => $states[$grant['state']][2]);
407 $row->realm = $grant['realm'];
408 $row->gid = $grant['gid'];
409 $row->grant_view = $grant['grant_view'];
410 $row->grant_update = $grant['grant_update'];
411 $row->grant_delete = $grant['grant_delete'];
5050cad5 412 $row->explained = implode('<br />', module_invoke_all('node_access_explain', $row));
122d4dc8 413 unset($row->title); // possibly needed above
b98126b3 414 if ($row->nid == 0 && $row->gid == 0 && $row->realm == 'all' && count($all_grants) > 1) {
415 $row->state = array('data' => $states['unexpected'][0], 'title' => $states['unexpected'][2]);
416 $class = $states['unexpected'][1];
417 }
418 else {
419 $class = $states[$grant['state']][1];
420 }
e6ed9115 421 $error_count += ($class == 'error');
76b9c98c 422 $row = (array) $row;
423 foreach (array('view', 'update', 'delete') as $op) {
424 $row["grant_$op"] = array('data' => $row["grant_$op"]);
306fd114 425 if ((isset($checked_grants[$grant['nid']][$op][$grant['realm']]) && in_array($grant['gid'], $checked_grants[$grant['nid']][$op][$grant['realm']]) || ($row['nid'] == 0 && $row['gid'] == 0 && $row['realm'] == 'all')) && !empty($row["grant_$op"]['data']) && in_array($grant['state'], $active_states)) {
76b9c98c 426 $row["grant_$op"]['data'] .= '&prime;';
427 $row["grant_$op"]['title'] = t('This entry grants access to this node to this user.');
428 }
429 if (isset($grant["grant_$op!"])) {
284ca4ae 430 $row["grant_$op"]['data'] = $grant["grant_$op!"] .'&gt;'. (!$row["grant_$op"]['data'] ? 0 : $row["grant_$op"]['data']);
76b9c98c 431 $row["grant_$op"]['class'] = 'error';
432 }
433 }
122d4dc8 434 $row['nid'] = '<a href="#node-'. $grant['nid'] .'">'. $row['nid'] .'</a>';
76b9c98c 435 foreach (array('nid', 'priority', 'gid') as $key) {
436 $row[$key] = array('data' => $row[$key], 'style' => 'text-align: right');
437 }
438 $row['nid']['title'] = $grant['#title'];
439 $row['realm'] = (empty($grant['#module']) || strpos($grant['realm'], $grant['#module']) === 0 ? '' : $grant['#module'] .':<br />') . $grant['realm'];
440 $rows[] = array('data' => array_values($row), 'class' => 'even '. $class);
441 }
898421d0 442 $output = theme('table', $headers, $rows, array('class' => 'system-status-report', 'style' => 'text-align: left'));
76b9c98c 443
7148543e 444 $output .= theme_item(array('#value' => '', '#description' => '(Some of the table elements provide additional information if you hover your mouse over them.)'));
76b9c98c 445
e6ed9115 446 if ($error_count > 0) {
3ec8e29b 447 $variables['!Rebuild_permissions'] = '<a href="'. url('admin/content/node-settings/rebuild') .'">'. $tr('Rebuild permissions') .'</a>';
448 $output .= theme_item(array('#value' => '<div class="error">'. t("You have errors in your !na table! You may be able to fix these for now by running !Rebuild_permissions, but this is likely to destroy the evidence and make it impossible to identify the underlying issues. If you don't fix those, the errors will probably come back again. <br /> DON'T do this just yet if you intend to ask for help with this situation.", $variables) .'</div>'));
e6ed9115 449 }
450
bd6c9d19 451 // Explain whether access is granted or denied, and why (using code from node_access()).
898421d0 452 $tr = 't';
453 array_shift($nids); // remove the 0
454 $accounts = array();
5050cad5 455 $variables += array(
456 '!username' => theme('username', $user),
457 '%uid' => $user->uid,
458 );
bd6c9d19 459
898421d0 460 if (user_access('administer nodes')) {
461 $variables['%administer_nodes'] = $tr('administer nodes');
462 $output .= t('!username has the %administer_nodes permission and thus full access to all nodes.', $variables) .'<br />&nbsp;';
463 }
464 else {
465 $variables['!list'] = '<div style="margin-left: 2em">'. _devel_node_access_get_grant_list($nid, $checked_status, $checked_grants) .'</div>';
1e72ce24 466 $variables['%access'] = 'view';
467 $output .= "\n<div style='text-align: left' title='". t('These are the grants returned by hook_node_grants() for this user.') ."'>". t('!username (user %uid) can use these grants for %access access (if they are present above): !list', $variables) ."</div>\n";
bd6c9d19 468 $accounts[] = $user;
898421d0 469 }
470 if (arg(0) == 'node' && is_numeric(arg(1)) && !$block1_visible) { // only for single nodes
bd6c9d19 471 if (user_is_logged_in()) {
472 $accounts[] = user_load(0); // Anonymous, too
76b9c98c 473 }
bd6c9d19 474 foreach ($accounts as $account) {
475 $variables['!username'] = theme('username', $account);
476 $output .= "\n<div style='text-align: left'>". t("!username has the following access", $variables) .' ';
477 $nid_items = array();
478 foreach ($nids as $nid) {
479 $op_items = array();
480 foreach (array('create', 'view', 'update', 'delete') as $op) {
481 $explain = _devel_node_access_explain_access($op, $nid, $account);
898421d0 482 $op_items[] = "<div style='width: 5em; display: inline-block'>". t('%op:', array('%op' => $op)) .' </div>'. $explain[2];
76b9c98c 483 }
898421d0 484 $nid_items[] = t('to node !nid:', array('!nid' => l($nid, 'node/'. $nid)))
bd6c9d19 485 ."\n<div style='margin-left: 2em'>". theme('item_list', $op_items, NULL, 'ul') .'</div>';
76b9c98c 486 }
bd6c9d19 487 if (count($nid_items) == 1) {
488 $output .= $nid_items[0];
489 }
490 else {
491 $output .= "\n<div style='margin-left: 2em'>". theme('item_list', $nid_items, NULL, 'ul') .'</div>';
492 }
493 $output .= "\n</div>\n";
76b9c98c 494 }
495 }
496 }
898421d0 497
498 if (!empty($hint)) {
499 $output .= theme_item(array('#value' => '', '#description' => '('. $hint .')'));
500 }
2d35faff 501 $subject = t('node_access entries for nodes shown on this page');
502 return array('subject' => $subject, 'content' => $output .'<br /><br />');
503
504 case 1:
505 // show which users can access this node
00f41710 506 if (arg(0) == 'node' && is_numeric($nid = arg(1)) && arg(2) == null && $node = node_load($nid)) {
bd6c9d19 507 $headers = array(t('username'), '<span title="'. t("Create nodes of the '@Node_type' type.", array('@Node_type' => node_get_types('name', $node))) .'">'. t('create') .'</span>', t('view'), t('update'), t('delete'));
2d35faff 508 $rows = array();
509 // Find all users. The following operations are very inefficient, so we
510 // limit the number of users returned. It would be better to make a
511 // pager query, or at least make the number of users configurable. If
512 // anyone is up for that please submit a patch.
513 $result = db_query_range('SELECT DISTINCT u.* FROM {users} u ORDER BY u.access DESC', 0, 10);
514 while ($data = db_fetch_object($result)) {
898421d0 515 $account = user_load($data->uid);
2d35faff 516 $rows[] = array(theme('username', $data),
bd6c9d19 517 theme('dna_permission', _devel_node_access_explain_access('create', $nid, $account)),
518 theme('dna_permission', _devel_node_access_explain_access('view', $nid, $account)),
519 theme('dna_permission', _devel_node_access_explain_access('update', $nid, $account)),
520 theme('dna_permission', _devel_node_access_explain_access('delete', $nid, $account)),
2d35faff 521 );
522 }
523 if (count($rows)) {
524 $output = theme('table', $headers, $rows, array('style' => 'text-align: left'));
5050cad5 525 $output .= theme_item(array('#value' => '', '#description' => t('(This table lists the most-recently active users. Hover your mouse over each result for more details.)')));
2d35faff 526 return array('subject' => t('Access permissions by user'),
527 'content' => $output);
528 }
7f4add34 529 }
2d35faff 530 break;
2f9fa4a3 531 }
2d35faff 532 break;
361dbb76
DC
533 }
534}
535
aa60cf2c 536/**
bd6c9d19 537 * Helper function that mimicks node.module's node_access() function.
898421d0 538 *
bd6c9d19 539 * Unfortunately, this needs to be updated manually whenever node.module changes!
898421d0 540 *
bd6c9d19 541 * @return
542 * An array suitable for theming with theme_dna_permission().
543 */
97c9e69a 544function _devel_node_access_explain_access($op, $node, $account = NULL) {
bd6c9d19 545 global $user;
546 static $filter_formats;
547
898421d0 548 if (is_numeric($node) && !($node = node_load($node))) {
bd6c9d19 549 return array( FALSE, '???',
550 t('Unable to load the node &ndash; this should never happen!'),
551 );
552 }
553 if ($op == 'create' && is_object($node)) {
554 $node = $node->type;
555 }
556
557 if (!empty($account)) {
898421d0 558 $filter_formats = filter_formats(); // use real current user first!
bd6c9d19 559 // To try to get the most authentic result we impersonate the given user!
560 // This may reveal bugs in other modules, leading to contradictory results.
561 $saved_user = $user;
562 session_save_session(FALSE);
563 $user = $account;
564 $result = _devel_node_access_explain_access($op, $node, NULL);
565 $user = $saved_user;
566 session_save_session(TRUE);
567 $second_opinion = node_access($op, $node, $account);
568 if ($second_opinion != $result[0]) {
569 $result[1] .= '<span class="'. ($second_opinion ? 'ok' : 'error') .'" title="DNA and Core seem to disagree on this item. This is a bug in either one of them and should be fixed! Try to look at this node as this user and check whether there is still disagreement.">*</span>';
570 }
571 return $result;
572 }
573
574 $variables = array(
575 '!NO' => t('NO'),
576 '!YES' => t('YES'),
577 );
578
579 if ($op == 'update' && !_devel_node_access_filter_access($node->format)) {
580 return array( FALSE,
581 t('!NO: input format', $variables),
582 t("!NO: This user is not allowed to use the input format '!format' (!fid).", $variables += array(
583 '!fid' => $node->format,
584 '!format' => (isset($filter_formats[$node->format]) ? $filter_formats[$node->format]->name : '***'),
585 )),
586 );
587 }
588 if (user_access('administer nodes')) {
589 return array( TRUE,
590 t('!YES: administer nodes', $variables),
591 t("!YES: This user has the '!administer_nodes' permission and may do everything with nodes.", $variables += array(
592 '!administer_nodes' => t('administer nodes'),
593 )),
594 );
595 }
596 elseif (!user_access('access content')) {
597 return array( FALSE,
598 t('!NO: access content', $variables),
599 t("!NO: This user does not have the '!access_content' permission and is denied doing anything with content.", $variables += array(
600 '!access_content' => t('access content'),
601 )),
602 );
603 }
604 $module = node_get_types('module', $node);
898421d0 605 $access = module_invoke(($module == 'node' ? 'node_content' : $module), 'access', $op, $node, $user);
bd6c9d19 606 if (!is_null($access)) {
607 $variables += array(
608 '@module' => $module,
609 '@content_type' => (is_object($node) ? $node->type : $node),
610 );
611 if ($access) {
612 return array( TRUE,
613 t('!YES: by the module', $variables),
898421d0 614 t("!YES: The '@module' module (which defines the '@content_type' content type) allows this, probably based on some permission.", $variables),
bd6c9d19 615 );
616 }
617 else {
618 return array( FALSE,
619 t('!NO: by the module', $variables),
620 t("!NO: The '@module' module (which defines the '@content_type' content type) denies this.", $variables),
621 );
622 }
623 }
624
625 if ($op != 'create' && $node->nid && $node->status) {
626 if (node_access($op, $node, $user)) { // delegate this part
627 return array( TRUE,
628 t('!YES: node access', $variables),
629 t('!YES: Node access allows this.', $variables),
630 );
631 }
632 else {
633 return array( FALSE,
634 t('!NO: node access', $variables),
635 t('!NO: Node access denies this.', $variables),
636 );
637 }
638 }
639
640 if ($op == 'view' && $user->uid == $node->uid && $user->uid != 0) {
641 return array( TRUE,
642 t('!YES: own node', $variables),
643 t('!YES: The user may view his/her own node.', $variables),
644 );
645 }
646 return array( FALSE,
647 t('!NO: no reason', $variables),
648 t("!NO: None of the checks resulted in allowing this, so it's denied.", $variables) .
649 ($op != 'create' && !$node->status ? ' '. t('Node access was not checked because the node is not published.') : '') .
3ec8e29b 650 ($op == 'create' ? ' '. t('This is most likely due to a withheld permission.') : ''),
bd6c9d19 651 );
652}
653
654/*
655 * Helper function that mimicks filter.modules' filter_access(), but with the
656 * help of the user-aware _devel_node_access_filter_formats() function.
657 */
658function _devel_node_access_filter_access($format) {
659 $format = filter_resolve_format($format);
660 if (user_access('administer filters') || ($format == variable_get('filter_default_format', 1))) {
661 return TRUE;
662 }
663 else {
664 return (bool) _devel_node_access_filter_formats($format);
665 }
666}
667
668/*
669 * Helper function that mimicks filter.module's filter_formats(), but for a
670 * specific user. If #470840 gets committed, we can remove this here.
671 */
672function _devel_node_access_filter_formats($index, $account = NULL) {
673 global $user;
674 static $formats = array();
675
676 if (!isset($account)) {
677 $account = $user;
678 }
679
898421d0 680 // Administrators can always use all text formats.
bd6c9d19 681 $all = user_access('administer filters', $account);
682
683 if (!isset($formats[$account->uid])) {
684 $formats[$account->uid] = array();
685
686 $query = 'SELECT * FROM {filter_formats}';
687
688 // Build query for selecting the format(s) based on the user's roles.
689 $args = array();
690 if (!$all) {
691 $where = array();
692 foreach ($account->roles as $rid => $role) {
693 $where[] = "roles LIKE '%%,%d,%%'";
694 $args[] = $rid;
695 }
696 $query .= ' WHERE '. implode(' OR ', $where) .' OR format = %d';
697 $args[] = variable_get('filter_default_format', 1);
698 }
699
700 $result = db_query($query, $args);
701 while ($format = db_fetch_object($result)) {
702 $formats[$account->uid][$format->format] = $format;
703 }
704 }
705 if (isset($index)) {
706 return isset($formats[$account->uid][$index]) ? $formats[$account->uid][$index] : FALSE;
707 }
708 return $formats[$account->uid];
709}
710
711/**
712 * Helper function to create a list of the grants returned by hook_node_grants().
713 */
97c9e69a 714function _devel_node_access_get_grant_list($nid, $checked_status, $checked_grants) {
bd6c9d19 715 if (!empty($checked_status[$nid])) {
716 $cgs_by_realm = array();
717 foreach ($checked_grants[$nid]['view'] as $realm => $cg) {
718 if (isset($cg['#module'])) {
719 $module = $cg['#module'];
720 unset($cg['#module']);
721 if (!empty($module) && (strpos($realm, $module) !== 0)) {
722 $realm = $module .':'. $realm;
723 }
724 }
725 $cgs_by_realm[$realm] = $realm .': '. implode(', ', $cg);
726 }
727 if (!empty($cgs_by_realm)) {
728 return theme('item_list', array_values($cgs_by_realm), NULL, 'ul');
729 }
730 }
731}
732
733/**
769e07f1 734 * Implementation of hook_node_access_explain().
2f9fa4a3 735 */
aa60cf2c 736function devel_node_access_node_access_explain($row) {
fec1eb09 737 if ($row->gid == 0 && $row->realm == 'all') {
b98126b3 738 foreach (array('view', 'update', 'delete') as $op) {
739 $gop = 'grant_'. $op;
740 if (!empty($row->$gop)) {
741 $ops[] = $op;
742 }
743 }
744 if (empty($ops)) {
898421d0 745 return '(No access granted to '. ($row->nid == 0 ? 'any nodes.)' : 'this node.)');
7148543e 746 }
2f9fa4a3 747 else {
b98126b3 748 return 'All users may '. implode('/', $ops) . ($row->nid == 0 ? ' all nodes.' : ' this node.');
fec1eb09 749 }
aa60cf2c 750 }
751}
361dbb76 752
7f4add34 753/**
bd6c9d19 754 * Helper function to return a sanitized node title.
755 */
f4a27524 756function _devel_node_access_get_node_title($node, $clip_and_decorate = FALSE) {
bd6c9d19 757 if (isset($node)) {
758 if (isset($node->title)) {
759 $node_title = check_plain($node->title);
760 if ($clip_and_decorate) {
761 if (drupal_strlen($node_title) > 20) {
762 $node_title = "<span title='node/$node->nid: $node_title'>". drupal_substr($node_title, 0, 15) .'...</span>';
763 }
764 $node_title = '<span title="node/'. $node->nid .'">'. $node_title .'</span>';
765 }
766 return $node_title;
767 }
768 elseif (isset($node->nid)) {
769 return $node->nid;
770 }
771 }
772 return '&mdash;';
773}
774
775/**
769e07f1 776 * Implementation of hook_theme().
7f4add34 777 */
778function devel_node_access_theme() {
779 return array(
780 'dna_permission' => array(
769e07f1 781 'arguments' => array('permission' => NULL),
7f4add34 782 ),
783 );
784}
785
786/**
787 * Indicate whether user has a permission or not.
7f4add34 788 */
789function theme_dna_permission($permission) {
bd6c9d19 790 return '<span class="'. ($permission[0] ? 'ok' : 'error') .'" title="'. $permission[2] .'">'. $permission[1] .'</span>';
7f4add34 791}