5

D7, Views 3. I'm kinda puzzled with how Views path work. I have these paths at my site:

mysite.com/people - a view displaying a list of all people

mysite.com/people/alice - an article about Alice

mysite.com/people/bob - there is no such article at mysite.com

When I go to /people/alice I get my article. And when I go to /people/bob I naturally expect to get a 404 page. But /people view is shown instead, probably seeing the "people" part of the path and thinking I've called /people view with some parameter. As I do not expect /people view to have any parameters and always want it to display a list of all people, how do I tell it not to get into Drupal's process of resolving its paths?

0

3 Answers 3

4

This is not a problem with the Views module; it's how Drupal behaves.

If you look at menu_get_item(), you will notice the following code.

$original_map = arg(NULL, $path);

$parts = array_slice($original_map, 0, MENU_MAX_PARTS);
$ancestors = menu_get_ancestors($parts);
$router_item = db_query_range('SELECT * FROM {menu_router} WHERE path IN (:ancestors) ORDER BY fit DESC', 0, 1, array(':ancestors' => $ancestors))->fetchAssoc();

I tried the following code, in a test site.

$original_map = arg(NULL, 'people/bob');

$parts = array_slice($original_map, 0, MENU_MAX_PARTS);
$ancestors = menu_get_ancestors($parts);

What I got is an array containing the following items:

  • people/bob
  • people/%
  • people

In your case, as there isn't a menu callback for people/bob, and people/%, the query executed from menu_get_item() will find the menu callback for people, the view you have defined.

To avoid people/bob returns the view page, I would create a custom module with the following code.

function mymodule_menu() {
  $items['people/%'] = array(
    'page callback' => 'mymodule_people_view',
    'page arguments' => array(1), 
    'access arguments' => array('access content'), 
    'type' => MENU_CALLBACK,
  );
}

function mymodule_people_view($username) {
  $alias = "people/$username";
  $path = drupal_get_normal_path($alias);

  if ($alias == $path) {
    // $alias is not a defined alias; in this case, drupal_get_normal_path()
    // returns the string it gets as argument.
    drupal_not_found();
  }
  else {
    drupal_goto($path);
  }
}

There is a module to resolve this issue (Views 404), which has now a version for Drupal 7.

Are your views returning a 200 when it should return a 404? Views404 is the answer! This is very helpful for any caching layer and it will help SEO. [...] Standard Drupal behavior is to pass all arguments through, whether or not they requested. Sometimes this behavior is desired. The views 404 module assumes that by default you do not want arguments passed through. 404s for views can also be accomplished by setting the Global: Null under Arguments.

2
  • My understanding of Views 404 is that it doesn't allow any parameters to be passed. From the project page: The views 404 module assumes that by default you do not want arguments passed through so if you are sussing out valid versus invalid, the approach you outlined here is better still it seems.
    – cdmo
    Commented Jul 7, 2016 at 12:01
  • That last bit from the Views404 project page is the key. Views can do this, but it is very well hidden and non-intuitive: Add a contextual filter to your view, and set is a Global: Null. In the settings for Global: Null under WHEN THE FILTER VALUE IS IN THE URL OR A DEFAULT IS PROVIDED check Specify validation criteria and set Validator to Basic Validation and Action to Show Page not found. Under the collapsed MORE section check Fail basic validation if any argument is give. Commented Nov 13, 2017 at 20:42
0

I found creating a module (or installing a beta module) a bit of a hassle. So I set the empty text of my view to this

<?php
if (arg(1)){
  drupal_set_header($_SERVER ['SERVER_PROTOCOL'] . ' 404 Not Found');
  watchdog('page not found', check_plain($_GET ['q']), NULL, WATCHDOG_WARNING);
  drupal_set_title(t('Page not found'));
  echo t('The requested page could not be found.');
}
?>
&nbsp;

And set the input format to PHP code.

It works, but IMHO this should be the default behaviour of a view that doesn't use arguments.

0

Views can do this natively, but it is very well hidden and non-intuitive:

  • Add a contextual filter to your view, and set it as Global: Null.

  • In the settings for Global: Null under WHEN THE FILTER VALUE IS IN THE URL OR A DEFAULT IS PROVIDED check Specify validation criteria and set Validator to Basic Validation and Action to Show "Page not found". Under the collapsed MORE section check Fail basic validation if any argument is given.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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