4

On my site, some nodes are unpublished forever instead of being deleted. I'd like to return Page not found (404) for anonymous trying to access unpublished node pages instead of Access denied (403). It's not about displaying a different message to the user, but rather about returning the 404 error code (important for bots).

So I've added the following to my module:

function mymodule_init() {
global $user;

  $normal_path = current_path();
  $parts=explode('/', $normal_path);
  $nid_part = $parts[1];
  $node = node_load($nid_part);
  if (!isset($node)) {
    return;
  }
  if (($node->status == 0) &&
    ($node->nid == $nid_part) &&
    ($user->uid == 0)) {
    drupal_not_found();
  }
}

But I still get Access denied as anonymous user on unpublished pages. How to implement it?

  • first, I would suggest using hook_boot(), not hook_init(), since hook_init() does not run on cached pages. If that doesnt solve it, I would start by setting a system message in your module, line by line to see what values the information is returning to ensure it is correct – Geoff May 1 '15 at 13:54
7

You should be able to do it with Rules:

The Rules module allows site administrators to define conditionally executed actions based on occurring events (known as reactive or ECA rules). It's a replacement with more features for the trigger module in core and the successor of the Drupal 5 workflow-ng module.

Using Rules...

Event: Content is being Viewed (by content type if you desire)

Condition: Content is Published, Negate

Event: Redirect to custom 404 page

enter image description here

This requires that you set up a custom 404 page, but most people do that anyways, and core takes care of everything else.

Specify 403 and 404 error pages for more info on setting up a custom 403 and 404 page

  • After setting the Rules like you suggest (i.imgur.com/Bfa7zSH.jpg) I still get "Access denied" (incorrect) for anonymous and "Page not found" (correct) for admin. So it looks like access check has higher priority than the rule. – camcam May 1 '15 at 5:58
  • hmm... Content Viewed must be too far along the path, that sucks it was a nice and clean solution. – Geoff May 1 '15 at 13:52
2

My solution was to allow access to unpublished nodes, but then return a 404 result before rendering the page. If you have other modules controlling or depending on node_access(), this solution may not be for you.

/**
 * Implements hook_node_access().
 *
 * Allow users to view unpublished nodes, but modify them later to return 404 
 * in hook_view_node_alter().
 */
function MODULE_node_access($node, $op, $account) {
  if ($op == 'view') {
    return NODE_ACCESS_ALLOW;
  }

  return NODE_ACCESS_IGNORE;
}

/**
 * Implements hook_node_view_alter().
 *
 * Provide a 404 page for unpublished nodes.
 * Access is allowed through hook_node_access().
 */
function MODULE_node_view_alter(&$build) {
  $node = $build['#node'];
  if ($node->status == NODE_NOT_PUBLISHED) {
    drupal_not_found();
    drupal_exit();
  }
}

My use case was much more limited, and so I was able to limit hook_node_access() to only affect permissions for anonymous users, and then have hook_node_view_alter() ignore logged in users.
I also inlined code from drupal_deliver_html_page() in order to provide a custom 404 message instead of the site-wide page given by drupal_not_found()

1
  1. Use the view_unpublished module to grant access to unpublished nodes for anonymous users. - https://www.drupal.org/project/view_unpublished

  2. Add this to your module:

    /**
     * Implements hook_init().
     */
    function MYMODULE_init() {
      // For anon users, show a 404 for unpublished content instead of a 403.
      if(user_is_anonymous() && ($node = menu_get_object()) && $node->status == NODE_NOT_PUBLISHED) {
      drupal_not_found();
      drupal_exit();
      }
    } 
0

I have used Hook_preprocess to direct them to the 'page not found' page.

I was able to redirect them to the 404 page by the following code. hope this helps for someone in future.

This was written for a Drupal 6 site. I have slightly modified @camcam's code for drupal 6. you'll be able to use his code for drupal 7 and use drupal_exit() for exit() method.

function Hook_preprocess_page(&$vars) {
   global $user;       
     if ($vars['title'] == 'Access denied') {


            $normal_path = $_GET['q'];
            $parts=explode('/', $normal_path);
            $nid_part = $parts[1];
            $node = node_load($nid_part);
            if (!isset($node)) {
              return;
            }
            if (($node->status == 0) &&
              ($node->nid == $nid_part) &&
              ($user->uid == 0)) {
              drupal_not_found();
              exit();
            }
    }

}

cheers. this is my first post here.

0

I've always used this: https://www.drupal.org/project/unpublished_404

It is currently unmaintained (and in the process of getting a new maintainer) due to a security issue, but there is a patch available in the Issues forum for this module.

https://www.drupal.org/node/2752665#comment-12157403

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.