Drupal - How do I create relative menu links?

When you add a new link, the link is validated from menu_edit_item_validate(), which calls drupal_valid_path() to check if the path is valid, or it can be accessed from the user.
drupal_valid_path() returns TRUE if

  • The path is <front> (<front>/directory, or similar paths are excluded)
  • The path is an external path (the function url_is_external() returns TRUE)
  • The path is associated with a menu callback

Apart from using absolute URLs, the only solution is to have a custom module that defines a menu callback associated with the path you want to use.
When you use the URL that points to a file, or a directory, the web server should show that file or directory, without invoking Drupal. That is what happens with Apache, as the .htaccess file that comes with Drupal contains the following directives:

# Pass all requests not referring directly to files in the filesystem to
# index.php. Clean URLs are handled in drupal_environment_initialize().
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_URI} !=/favicon.ico
RewriteRule ^ index.php [L]

In your case, the custom module should contain code similar to the following:

function mymodule_menu() {
  $items = array();

  $items['the_folder_for_the_other_content/%menu_tail'] = array(
    'title' => 'Folder content', 
    'page callback' => 'mymodule_view', 
    'access callback' => TRUE,
    'load arguments' => array('%map', '%index'), 
  );

  return $items;
}

You can use any values for title and page callback, as that menu callback should never be called. The value of access callback says to Drupal that every user has access to that menu callback.

As an alternative you can define a menu callback associated to "alias_for_directory/%menu_tail" that redirects the user to the directory you want they see (using drupal_goto()). I would use this solution when the server does not use the content of .htaccess, and it would invoke Drupal even when the URL is pointing to an existing file or directory.


kiamlaluno's answer did not quite work for me. Using %menu_tail produced a storm of errors that the menu_tail_load function was having problems with missing arguments:

Warning: Missing argument 2 for menu_tail_load(), called in  ... /includes/menu.inc on line 579 and defined in menu_tail_load() (line 827 of ... /includes/menu.inc).
Warning: Missing argument 3 for menu_tail_load(), called in ... /includes/menu.inc on line 579 and defined in menu_tail_load() (line 827 of ... /includes/menu.inc).

What did work for me was

function allow_menu_links_menu() {
  $items = array();
  $items['sites/d8/files/%'] = array(
    'title' => 'Folder Content',
    'page callback' => 'allow_menu_links_cb', /* never called */
    'access callback' => TRUE,
  );
  return $items;
}

Then, I could serve up files in the sites/d8/files folder (and subfolders) using menu items with paths like sites/d8/files/Documents/MyFile.pdf

The menu system will then generate a link like href="/sites/d8/files/Documents/MyFile.pdf"

If you don't use the custom menu module, the menu system will reject a path starting as above.

If you try to use a path like http:sites/d8/files/Documents/MyFile.pdf , it will produce a link like href="http:sites/d8/files/Documents/MyFile.pdf", which will work when you are at the root of the site, but when you are on a content page, the browser will interpret the url relative to the content page, and it will not work.

If you use a path like http:/sites/d8/files/Documents/MyFile.pdf, the menu system will accept it, but the menu system will produce a link like href="http:/sites/d8/files/Documents/MyFile.pdf" , which the browser (at least Safari) will interpret as href="http://sites/d8/files/Documents/MyFile.pdf" , and fail trying to find the server named "sites".

(On further investigation, the reason the code using menu_tail fails is that you need to add the 'load arguments', as noted here: http://api.drupal.org/api/drupal/includes--menu.inc/function/menu_tail_load/7 . If you add 'load arguments' => array('%map', '%index'), to the definition of $items in kiamlaluno's code, it will work. The issue of whether menu_tail_load should require the explicit load arguments is also discussed here: http://drupal.org/node/298561)

(Don't forget to enable your new module, or the menu system will not accept the new links)


For me the following did the trick (on drupal 6).

I want to direct to another URL on the same domain, let's say Drupal is running on http://www.example.com and I want to have a link to http://www.example.com/frontend

In the menu item, please enter as path:

http:/frontend

The drupal menu will link to: http://www.example.com/frontend

When you use https, you should enter https:/frontend in the path field for the menu item.

Tags:

Routes