I have a site I am working on that has two different "areas." This first consists of pages and they are viewable by anyone. The other part of the site is the forums and they are only for authenticated users. Basically, if some one tries to access the forums and they are not an authenticated user, I need to redirect them to a login page. I was going to create a node that had the nice text to ask them to login and presented the login block.

Here's my question: what is the best way to do this? The forum section has its own theme. Is there something I can do at the theme level to redirect this if they are not logged in? (I build on a theme using xtemplate) Or, should I modify the forum module so that if $user->id is not set they get redirected to a specific node? If I should modify the forum module, can someone tell me what function I need to modify and how I can redirect to a specific mode?

Comments

Dublin Drupaller’s picture

One simple way of doing that would be..

  1. Create a "Sorry u need to be logged in to view that page...sign up here" friendly node..such as a SIGN UP information page. i.e. go to CREATE CONTENT -> PAGE and make a note of the Node #ID when you have submitted it (e.g. www.example.com/node/22 where 22 is the Node #id)
  2. If necessary, The Login Block is on by default, but, in case you have switched it off go to your ADMINISTER -> BLOCKS settings page and ensure that the LOGIN BLOCK is set to appear with that SIGN UP node.
  3. go to ADMINISTER -> SETTINGS and specify your sign up node (e.g. node/22) to be the DEFAULT 403 node. (which is triggered when a visitor clicks on a page that requires login - "access denied")

now when a user clicks on a page that requires login, they are automatically directed to your "SIGN UP" page..and just in case they are existing users using someone elses computer, the LOGIN BLOCK also appears so they can login - and be redirected back to the page they were looking for.

I hope that makes sense..

Dub

Currently in Switzerland working as an Application Developer with UBS Investment Bank...using Drupal 7 and lots of swiss chocolate

green monkey’s picture

Dub,
Thanks for the post, handy and useful knowledge. I think I can use this too. Thanks

llyra’s picture

Hi Dub,

I am looking on how to do this too. I have done what you stated- thank you so much!- but I am probably doing something wrong or need to add something to the pages that are only accessible once logged in.

The pages I created for the 403 and 404 errors are showing up in my navigation menu. How do I remove a page from this menu? I did go to admin-> menus -> and changed the location (from navigation to under administrator->content) but that didn't remove it from the Navigational (generic) side menu.

I would also like to know if there is a way to list the login box in the content section rather than in the sidebox/block?

Also, how do I code a specific 'members only' page so that members need to sign in to view this page (if not already signed in)? Sorry, I'm a newbie and probably missing an obvious method(s)!

Using 4.7 RC3

Thanks so much!

sclough’s picture

That makes perfect sense and thanks for such a great explanation. The only problem is that I need to do this with the forum module. By default, the forum module allows view access to anybody. I tried using taxonomy permissions to restrict the forum, but the forum page loads fine it just doesn't display any forums. So basically, you get a blank forum page because the forum module thinks everything is ok, but the taxonomy security module is making sure nothing shoes.

What you have here would work perfectly if I could get the forum module to issue a 403 any time $user->uid is not set. Do you know how I would do that? I am reading through module documentation, but I have not figure it out yet. I assume there must be any entry point in the module where it parses the module's url and decides what to display? I would think I could add a simple if (!$user->uid) { issue 403 } to it?

coreyp_1’s picture

The forum module uses the "access content" permission to determine whether or not content should be shown.

If you want anonymous users to be able to read regular nodes, but not access the forums, then you need to set up a different access permission for the forums. The following might work:

In forum.module around line 57, change:

  return array('create forum topics', 'edit own forum topics', 'administer forums');

to add the permission "access forum content"

  return array('create forum topics', 'edit own forum topics', 'administer forums', 'access forum content');

now you need to change the permission associated with the forum/* request. On lines 417 - 420, change:

    $items[] = array('path' => 'forum', 'title' => t('forums'),
      'callback' => 'forum_page',
      'access' => user_access('access content'),
      'type' => MENU_SUGGESTED_ITEM);

to the new access permission created above:

    $items[] = array('path' => 'forum', 'title' => t('forums'),
      'callback' => 'forum_page',
      'access' => user_access('access forum content'),
      'type' => MENU_SUGGESTED_ITEM);

Don't use forums myself, so I couldn't test it.

(BTW, on mine I have to disable then re-enable the module in order for the access and menu (forum/*) changes to be picked up.

- Corey

sclough’s picture

Thanks, I will try that and let you know.

sclough’s picture

Dub, thanks for the 403 info that works like a charm.

Corey, thanks so much for the code sample, it worked perfectly.

BTW, is there any reason not to add this to the core forum module? I have seen this question asked repeatedly with various hacks like taxonomy access being used to accomplish this.

toonopoly’s picture

So I tried it on image_galleries and blog. WORKS!

mfredrickson’s picture

Create a page.
Use the PHP filter to enter input.
Enter this text:

drupal_goto('user/login'); // or whatever your login page is

Submit the page. (beware, you will not be able to ever view this page - as it will always redirect you. You will need to find the node/nid number in some clever way - say using the admin/content screen)

In admin/settings set the default 403 page to point to that page.

Viola, instant redirection for all 403 errors.

--
http://www.ppmaf.org

Dublin Drupaller’s picture

Hi mfredrickson..

That snippet will work, but, I'm not sure if that's a good idea....from an ergonomics point of view..I think that snippet will cause the site to "forget" which page the person tried to access..

I.e. one of the neat things about being able to customise and set a default ACCESS DENIED page under ADMINISTER -> SETTINGS, is that when a registered user tries to access a page, but, has logged out/session timed out, they'll be automatically redirected to the page they initially requested after they login again...in other words the site "remembers" which page they wanted.

I stand corrected, but, using that snippet (I think) will redirect the user back to the login page when they eventually login. So the site "forgets" which page they initially wanted.

I haven't tried your snippet, so I stand corrected...did you test it?

My two cents would be to do the following instead of using that php redirect snippet:

  1. compose a "Sorry, you can't view that page, you need to register first etc. etc....sign up here" type user-friendly node.
  2. if you don't want to use the LOGIN BLOCK, paste the login block snippet (found in the handbook under PHP SNIPPETS -> BLOCK SNIPPETS) into the same node and select PHP as the input filter
  3. Set that node to be your Default 403 page under ADMINISTER -> SETTINGS

hope that makes sense..

Dub

Currently in Switzerland working as an Application Developer with UBS Investment Bank...using Drupal 7 and lots of swiss chocolate

mfredrickson’s picture

True, this doesn't set a destination. I'll come up with another snippet (and test it) soon. It shouldn't be too hard.

--
http://www.ppmaf.org

mfredrickson’s picture

Ok, here's some code to do what we need. Insert it into a PHP input filter page.

Version one: immediate drupal_goto

drupal_set_message("Access Denied: Please Login");
$dest = drupal_get_destination();
drupal_goto('user/login', $dest); // this remembers where the user is coming from

Version two: use a meta refresh after 10 seconds

$dest = drupal_get_destination();
$url = "http://www.youdomain.com/user/login?destination=$dest"
drupal_set_html_head('<meta http-equiv="refresh" content="10;URL=' . $url . '">');

The advantage of version 2 is that you have 10 seconds during to alert the user that they are going somewhere else. You can wrap the PHP in standard HTML to give more information.

Both versions should return the user to the requested page after login.

Beware, there is a good likelihood you will find yourself in an endless loop of redirects after you submit the node with this PHP in it. This is because you are already logged in.

If fact, you may want to use version 3: the safe version:

global $user
if ($user->uid) { // this user is already logged in
print "Access Denied: You do not have access to this page.";
} else {
drupal_set_message("Access Denied: Please Login");
$dest = drupal_get_destination();
drupal_goto('user/login', $dest); // this remembers where the user is coming from
}

This is somewhat tested code. Caveat emptor.
--
http://www.ppmaf.org

rblomme@drupal.org’s picture

I tried code version 3 under drupal 4.7.2, but instead of being redirected to the login page, I ended up with an endless loop. The reason for this loop is the drupal_goto() code: it overwrites the first parameter ($path) with the destination url. In common.inc:

function drupal_goto($path = '', $query = NULL, $fragment = NULL) {
  if (isset($_REQUEST['destination'])) {
    extract(parse_url($_REQUEST['destination']));
  }
  else if (isset($_REQUEST['edit']['destination'])) {
    extract(parse_url($_REQUEST['edit']['destination']));
  }
  $url = url($path, $query, $fragment, TRUE);
...

The extract(parse_url()) function overwrites the $path parameter with the destination url.
I replaced "$path" with "$thispath" in the drupal_goto code, and that solved my problem.

Anyone else with the same problem?

drubeedoo’s picture

Hopefully someone else finds this helpful... My "/access-denied" page looks like this:

<?php
global $user;

if ($user->uid) { 

print $user->name; ?>,
<p>We apologize, but you do not have permission to access this page.</p>
<p>If you feel that you have received this message in error, please <a href="/contact">contact us</a> with specific details so that we may review your access to our web site.</p>
<p>Thank You</p>
<?php } 

else { 

$dest = drupal_get_destination();

?><p>The page you requested is available to members only...</p>
<div class="links">&raquo; <a href="/user/login?<?php print $dest ?>">login</a> or <a href="/user/register?<?php print $dest ?>">register</a> to view this page</div>
<?php } ?>

The code provided earlier that does a drupal_goto() was causing me trouble, so I modified it a bit to show different messages based on whether the user is logged in, or unauthenticated. The $dest portion of the code takes the user to the destination page after they have logged in.

ahales’s picture

The code works well for me. The only problem is that I have a multi-site installation. The main site is at drupal.example.com with other sites at drupal.example.com/site1 and drupal.example,com/site2. The code works unchanged in the main site. For the other sites it requires that the urls be modified to include the site directory, otherwise the user gets directed to the main site. For site1 I had to replace "/contact" with "/site1/contact", also "/user/login" was replaced with "/site1/user/login" and "/user/register" with "/site1/user/register". Similar changes had to be made for site2. Once those changes are made it worked fine on all sites.

I am still checking my own installation to see if there are any configuration problems

niklp’s picture

Thanks for this, saved me a few minutes :)

Also you could put the login form directly in the 403 page by embedding <?php print drupal_get_form('user_login'); ?> into the page too, if that's appropriate for the site (or should I say *application*? :) )

Kineta Systems - Web Development in Nottingham

crocodyl’s picture

This page is my dream come true! Seriously, I had been trying to scratch that particular itch for about a month, and finally have a solution because of your awesome code snippet! The language is so polite, yet firm! Thanks so much!

reqIUm’s picture

The code which denies access to the forums works great (ie adding view access)!
but i have a menu item pointing to the forums and for some reason it dissapears if you are not logged in... this is bad, i still want them to be able to find the forums! ;)

im not sure how denying forum access could hide a menu link, is there anything i can do to bring it back?

mrgoltra’s picture

...version 5.1?

TheRec’s picture

Hello,

I tested this code in Drupal 5.2 and it just does a loop of redirection when drupal_goto function is invoked.
Did anyone find a fix for this or maybe there's another way to acheive this now ? I think redirecting directly with header() isn't really a good idea, I guess it will mess up the session :S

TheRec’s picture

Well I fixed this infinite loop issue. The problem was $_REQUEST['destination'] which is set to the URL of the page that caused the 403 error. And it is used in place of the $dest variable we pass to drupal_goto (which is kinda illogical, why offering a parameter if it doesn't replace the "default behaviour" ?). So it gives this code :

<?php
  global $user;
  if ($user->uid > 0) {
    $output = "<strong>".t("You do not have permission to view this page.")."</strong><br />";
    $output.= t("This page is only accessible to certain users.");
  } else {
    drupal_set_message(t("The page you requested is only accessible to certain users. Please log in so that we may determine your permissions."));
    unset($_REQUEST['destination']);
    drupal_goto("user/login",drupal_get_destination());
  }
  print $output;
?>

In fact only two minor changes from the previous snippet :

  1. Messages are passed through the locale module (that's not really a must, but I needed that for multilanguage purpose)
  2. unset($_REQUEST['destination']); avoids the infinite loop

I hope this helps someone. Maybe my code is inconsistent, if it's the case just tell me :) Hopefully this login box for anonymous users behaviour will be implemented in Drupal 6.x (http://drupal.org/node/111321), that's really a great usability issue.

vkr11’s picture

Subscribe..

-Victor
Income tax India - http://elagaan.com
Better way to search Drupal.org - http://drupalsearch.org

brianfisher’s picture

here's a drupal 6 version that preserves querystring parameters other than 'q' and 'destination'

global $user;
if ($user->uid) { // this user is already logged in
  print "Access Denied: You do not have access to this page.";
} else {
  drupal_set_message(t("Please log in first."));
  $querystring = $_GET;
  unset($querystring['q'], $querystring['destination']);
  $destination = drupal_get_destination() . urlencode('?' . drupal_query_string_encode($querystring));
  $url = url('user') . '?' . $destination;
  header( 'Location: ' . $url );
  exit;
}