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