0

I have node_access hook in a module. I tried to prevent anonymous user from access unpublished node like the following:

function MODULE_NAME_node_access($node, $op, $account) {
   global $user; 
if ($op == 'view' && $node->status == 0 ){
     return NODE_ACCESS_DENY;
  }  
}

This is working fine and redirect to access denied custom page. However, I would like it to be redirect to the custom 404 not found page. i.e the http response to be 404. I tried to find any replacement of NODE_ACCESS_DENY but I could not able to find anything.

I tried to use drupal_not_found() but it generates Server Error on the server.

Also, I would like to know, if there a method or property of the $user object that determine if the user is anonymous user or not. I find that there is user_is_anonymous() but I could not know how to use it in this context.

1

You can use drupal_goto("custom 404 page") to redirect a user to your custom page. For your second question you can check if user is anonymous using this condition. if($user->uid == 0)

Thus your hook can be rewritten like this.

function MODULE_NAME_node_access($node, $op, $account) {
  global $user;
  if ($op == 'view' && $node->status == 0 && $user->uid == 0){
    drupal_goto("custom_404_page");
   }
}

See here for drupal_goto documentation

Note: This function has been removed in Drupal 8. In Drupal 8 you can do this by this way.

return new RedirectResponse(\Drupal::url('user.page'));

or this way `use Drupal\Core\Controller\ControllerBase;

class MyControllerClass extends ControllerBase {
   public function foo() {
   //...
   return $this->redirect('user.page');
  }
}`

See here for Drupal 8 redirect info

|improve this answer|||||
  • hook_node_access() is not supposed to call drupal_goto(); it would redirect users in the wrong moment, and it would cause problems during cron tasks. hook_node_access() is not necessary called when a user is viewing a node. – kiamlaluno Sep 17 '15 at 2:20
1

hook_node_access() is not necessarily invoked when a user is viewing a node page: It is invoked from node_access() which in turn is called in different circumstances.

In all those cases, the functions are calling node_access('view') and redirecting the logged-in user or showing a 404 page would disrupt the normal Drupal behavior and would avoid Drupal completes what it is doing.

The only way to be sure a user is going to view a node is intercepting when the page callback for node/%node is being called. You cannot rely on node_view() being called when a user is going to view a node, since node_view() is called also from cron tasks (see _node_index_node(), which is indirectly called from search_cron(); this also means hook_node_view() cannot be used to redirect users while they are supposedly seeing a node page, since this is not true.

This would mean to implement hook_menu_alter() to change the page callback and the access callback of node/%node. You need to change the access callback because the default one is node_access() and users would still get a 403 error if they try to access directly a URL for an unpublished node. Since you are interested in showing a 404 page instead of a 403 page just for anonymous users, you could use the following code for the access callback.

function mymodule_node_access($op, $node) {
  if ($GLOBALS['user']->uid == 0 && $op == 'view' && $node->status == 0) {
    // We will return a 404 error to the user.
    return TRUE;
  }

  return node_access($op, $node);
}

Since you are just using the access callback for node/%node, its code doesn't need to check the operation, since it is going to be view.

function mymodule_node_access($op, $node) {
  if ($GLOBALS['user']->uid == 0 && $node->status == 0) {
    // We will return a 404 error to the user.
    return TRUE;
  }

  return node_access($op, $node);
}

The page callback would be similar to the following one.

function mymodule_node_page_view($node) {
  if ($GLOBALS['user']->uid == 0 && $node->status == 0) {
    drupal_not_found();
  }

  return node_page_view($node);
}

Notes

  • Since the access callback is returning TRUE for anonymous users, any link to unpublished nodes will be shown to anonymous users. This is something it cannot be avoided, since Drupal uses an access callback for two different purposes.

  • The alternative would be generally replacing a 403 error with a 404 error, but that would means it happens for every page. Checking the path being accessed would avoid making it happen for administrative pages, for example; checking for anonymous users would avoid it for authenticated users. (If I find a way to do this, I will updated my answer.)

As for user_is_anonymous(), it only works with the global $user and it is an hack for a specific case. The function you should call is user_is_logged_in(), which still works with the global $user.
Since the function has been removed from Drupal 8, I would rather check the user ID is 0, to understand if a user is anonymous user; in Drupal 8, you would change the code, but you would need to do that even when using user_is_anonymous() or user_is_logged_in().

|improve this answer|||||

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.