OK, so I want to link a menu item to a file, but I don't want to do a full path because I'm changing the full path eventually from development to production. I've researched this (and in the past done some things—some things I'm not proud of...) and I see lots of hacks and what-not, but that all makes it seem to me like I'm not getting it

Let's say I want to create a menu that links to a file. Well, I know there's a sites/default/files directory, so for the file link I put in this:

sites/default/files/jackson_logo.png

This gets me back a don't-have-access-error.

This is odd, to me, since this very link will (does!) work if I put it in the theme menu.

Which I guess tells me themes have permissions that menus don't, or something. I've tried a lot of proxies as well, setting up another directory, etc.

I get the concept that drupal wants full links to things that are not part of it, but not why I can't make the file enough a part of it that I can link it directly.

Straighten me out, folks.

Comments

alex’s picture

A quick look into menu module's code tell us you re not allowed to add a local link that is not in the router. That leads to two obvious ways to quickly deal with it without hacking the code:
1. Manually adding the image url to your menu_router table;
OR
2. Adding the menu item in a module with hook_menu.

I've just tested the 1 and I do the 2 all the time.

bdub’s picture

Appreciate the info.

I still feel like I'm not getting it. Not that I can't do it, but I'm not grasping the philosophy. It seems like a menu item linking a file is a foreign concept somehow.

I guess what I expected was something like:

1. Upload a file. That creates a node.
2. Create a menu item. Link it to the node created in step 1.

It seems like files aren't first-class citizens in Drupal?

kpv’s picture

sveldkamp’s picture

but I found this:

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

over here: http://drupal.stackexchange.com/questions/2203/how-do-i-create-relative-...

I just made a custom module with the above function, *enabled it*, and I could suddenly link directly to my PDFs. Hope this helps someone else!

seanr’s picture

I tried this and it did allow me to create a menu link to a file, but then that link doesn't show in my menu or the admin page even though I can see it in the database and everything appears to be correct.

candelas’s picture

@sveldkamp thanks for your solution. I tried it but in multilingual I got the language prefix so I had to add a token in the module together with your code :)

/*allow to link files with absolute url*/
function my_module_menu() {
  $items = array();
  $items[$GLOBALS['base_url'].'sites/default/files/%'] = array(
    'title' => 'Folder Content',
    'page callback' => 'allow_menu_links_cb', /* never called */
    'access callback' => TRUE,
  );
  return $items; 
} 

/*Declare token*/
function my_module_token_info() {
  $info['tokens']['site']['url-abs'] = array(
    'name' => t('Absolute URL'),
    'description' => t('Url of the site, no language prefix garanted'),
  );
  return $info;
}
/*Token replacement*/
function my_module_tokens($type, $tokens, array $data = array(), array $options = array()) {
  $replacements = array();
  $url = $GLOBALS['base_url'];
  if ($type == 'site') {
    foreach ($tokens as $name => $original) {
      switch ($name) {
        case 'url-abs':
          $replacements[$original] = $url;
          break;
      }
    }
  }
  return $replacements;
}

I hope someone can use it.

//trying to answer one question for each one that i make.
//this way, drupal will be more friendly and strong

supriyarajgopal’s picture

This worked like a charm, sveldkamp!

Thank you!

flyke’s picture

I am using Drupal 7 and I want my content editor to be able to add a menulink to any file that resides in the folder 'downloads' (in the root). I solved this by creating a custom module for this. The first part of my module I got from this thread. I did not find the solution above with token very clean nor was it working so I used a custom page callback function that executes a drupal goto with an absolute path (this is because on multilingual sites drupal adds the language prefix and otherwise you would have a false link to /en/downloads/xxx instead of /downloads/xxx).

Here is the code of my custom_module:

/**
 * Implements hook_menu().
 *
 * This module allows to create a menu item that links to a file that is in the downloads folder
 */

function custom_module_menu() {
  // Define a router item for the downloads directory.
  $items['downloads/%'] = array(
    'title' => 'Direct file link',
    'page callback' =>'open_file',  //our custom function that will actually open the file
    'access callback' => TRUE);
  return $items;
}

function open_file(){
  global $base_url;
  $file = 'downloads/'.arg(1);  //I am assuming that the file is directly in the folder downloads, so arg(0) will be 'downloads' and arg(1) will be the file name
  if (file_exists($file)) {
    $file = $base_url . '/' .$file;
    drupal_goto($file, array('absolute' => 'true'));
  }else{
    $file = $base_url . '/' .$file;
    watchdog('file not found', 'there is a menulink that links to this file that can not be found: '.$file);
    drupal_goto('<front>');
  }
}