Category Technology
Publication date
23 May 2010

Creating a Node View Which Bypasses Access Restrictions

First of all a disclaimer, part of the intention of this blog post is to see if anyone else has a better solution. This is something I came up with but I'm not entirely happy with the solution as it involves running the sql query twice. :(

Recently I was working on a site which had some subscription content, where basically only members of the site were allowed to view specific content types. However in order to encourage site visitors to register we wanted to display a teaser listing of the 5 most recent articles. We created a node view to display the listing, but the Views module, appropriately, only displays nodes which the user has access to. As the subscription content is only available to logged in users, this defeated the purpose of our teaser listing for site visitors.

To overcome the node access checks, I implemented hook_views_pre_render(). This is a Views hook which is invoked after the SQL query has been run, but before the view has been rendered. It checks that the view it is modifying is called 'myviewname' and that the display is 'block_1' - you'd need to change these as appropriate for your view. The following code essentially rebuilds the SQL query and reruns it a second time, but this time without the db_rewrite_sql() call that causes the node permissions to be checked.

<?php
/**
 * Implements hook_views_pre_render().
 */
function mymodule_views_pre_render(&$view) {

  // For myviewname, bypass node access checks.
  if ($view->name == 'myviewname' && $view->current_display == 'block_1' && empty($view->result)) {
    // This does the views token replacements.
    $replacements = module_invoke_all('views_query_substitutions', $view);
    $query = str_replace(array_keys($replacements), $replacements, $view->build_info['query']);
    $args = $view->build_info['query_args'];
    $offset = $view->pager['current_page'] * $view->pager['items_per_page'] + $view->pager['offset'];
    // Runs the query a second time.
    $result = db_query_range($query, $args, $offset, $view->pager['items_per_page']);
    // Overwrites the default empty result set with the results from our 2nd sql query.
    $view->result = array();
    while ($item = db_fetch_object($result)) {
      $view->result[] = $item;
    }
  }
}
?>

While the above solution works, I would be interested in learning if there is a better way that avoids running the query a second time and without creating the listing in a custom module that is.

Profile picture for user Stella Power

Stella Power Managing Director

As well as being the founder and managing director of Annertech, Stella is one of the best known Drupal contributors in the world.