[ Index ]

PHP Cross Reference of Drupal 6 (gatewave)

title

Body

[close]

/sites/all/modules/devel/ -> devel_node_access.module (source)

   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 : '&ndash;&nbsp;');
 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' => '&ndash;',
 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' => '&ndash;',
 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'] .= '&prime;';
 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!"] .'&gt;'. (!$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 />&nbsp;';
 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 &ndash; 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 '&mdash;';
 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  }


Generated: Thu Mar 24 11:18:33 2011 Cross-referenced by PHPXref 0.7