I'm trying to implement my own access denied page, and I'd like to get some information about the context of the access being denied... for example, if the user is on a specific node type page, I'd like to still display a short snippet of that node before the access denied message, so the user knows he's at least in the right part of my site.

However, I can't use menu_get_object(), as arg(0) simply returns 'access denied' in my page callback. Is there any way of passing in the path information for an access denied page? See my code below:

 * Implements hook_menu().
function custom_menu() {
  $items['access-denied'] = array(
    'title' => 'Access Denied',
    'page callback' => 'custom_access_denied',
    'access arguments' => array('access content'),
    'type' => MENU_CALLBACK,
  return $items;

And in my page callback:

 * Access Denied page.
function fn_custom_access_denied() {
  global $user;
  $html = '';

  // Display access denied message and login form for anonymous users.
  if (!$user->uid) {
    $html .= '<p>' . t('The content you are trying to access is protected. Please log in or create an account to view the page.') . '</p>';
    $html .= drupal_render(drupal_get_form('user_login'));
    drupal_set_title('Login Required');

  // Display access denied message for logged-in users.
  if ($user->uid) {
    $html .= '<p>' . t('The content you are trying to access is protected.') . '</p>';
    drupal_set_title('Private Content');

  return $html;

Supposing that the site is using clean URLs, I would rather use the following code:

function fn_custom_access_denied() {
  $path = drupal_get_normal_path(trim($_SERVER['REQUEST_URI'], '/'));

  if (($node = menu_get_object('node', 1, $path)) && ($node->type == 'event')) {
    drupal_set_message(t("%event is a private event.", array('%event' => $node->title));

  return "";
  • I like. I was just too lazy to look up how drupal gets the path ;-) – geerlingguy Aug 16 '11 at 20:23
  • ...except that menu_get_object() still seems to be denying access to the node, thus the node is not actually retrieved. A manual node_load() is still needed... – geerlingguy Aug 16 '11 at 20:27

I eventually found that I could get at the original query string by accessing the $_SERVER['REQUEST_URI'] global, but I wish I could get the information in a more drupal-y way...

Here's how I did it in the page callback:

$arguments = explode('/', trim($_SERVER['REQUEST_URI'], '/'));
// If the content is node content, display contextual information.
if (is_numeric($arguments[1]) && $node = node_load($arguments[1])) {
  if ($node->type == 'event') {
    $html .= '<div class="messages warning">' . t("<strong>!event</strong> is a Private Event.", array('!event' => $node->title)) . '</div>';
  • You should check that the relative path starts with node. Also, the code you wrote doesn't consider the fact some URLs are loading a specific revision of the node; this means your code is always loading the latest revision independently from the revision the URL is referring to. – kiamlaluno Aug 16 '11 at 18:05
  • Could you say more about working with URLs and revisions? How is the above code deficient and how does your answer address the concern? Thanks. – keithm Aug 16 '11 at 18:41

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.