4

I have a views page that is only accessible by a certain user role. This user role also has permission to "view private files" and to "document: download any files" (coming from File Entity Module). The views page displays a table of all private .pdf files that lie in the private file directory and I would like to add a column to this table, using a Views PHP field (at least for prototyping), that displays the corresponding download link next to the file name. As mentioned above, any user that can view the table also has permission to view private files and to download documents, so this should be doable and not be a security issue.

For now, let's assume I always want to display the download link to a fixed private .pdf file, say with file id $fid=17. According to Working with files in Drupal,

once configured, files stored in the private directory are inaccessible via a direct link; however, if Drupal constructs a link to the file, the file will be accessible to anyone who can see the link.

What precisely is meant by "if Drupal constructs a link to the file"? I have read many times that file_create_url() will create a direct link that, if the user has access to view private files, will take the user to the private file. So I tried in my Views PHP field as output code

<?php
$fid = 17;
$file = file_load($fid);
$uri = $file->uri;
$url = file_create_url($uri);
print l(t('Link'), $url);
?>

and the link gets displayed as it should (with reference to mysite.com/system/files/Name_Of_Private_File_17.pdf). But even though the user has permission to view and download private document files, clicking on the link will result in an "Access denied. You are not authorized to access this page."

Can anyone tell me the correct workaround?

0
4
+50

When I add the field 'File: Download link' to the view it works fine.

The views field handler for the 'File: Download link' has this snippet to check access:

if (!file_entity_access('download', $file)) {
   return;
}

and this snippet to construct the uri for the download link:

$uri = file_entity_download_uri($file);

The code for file_entity_download_uri():

function file_entity_download_uri($file) {
  $uri = array(
    'path' => "file/{$file->fid}/download",
    'options' => array(),
  );
  if (!variable_get('file_entity_allow_insecure_download', FALSE)) {
    $uri['options']['query']['token'] = file_entity_get_download_token($file);
  }
  return $uri;
}

show the download token being added to the options query key of $uri which is returned.

Adding that to the PHP code field:

<?php
    $fid = $row->fid;
    $file = file_load($fid);
    if (file_entity_access('download', $file)) {
        $uri = file_entity_download_uri($file);
        print l(t('Download'), $uri['path'], array('query' => $uri['options']['query']));
    }
?>

results in a download link to the private file.

0
1

Drupal 8

I needed to do this in Drupal 8. And then did it the same like I found it in file_preprocess_file_link in core. Doing it like that makes it agnostic, no matter if public or private file. You get the link.

use Drupal\Core\Link;
use Drupal\Core\Url;

$mime_type = $file->getMimeType();
$file_size = $file->getSize();
$file_name = $file->getFilename();
$options = [
  'attributes' => [
    'type' => $mime_type . '; length=' . $file_size,
    'title' => $file_name,
  ],
];
$url = $file->createFileUrl(FALSE);

$build['#link'] = Link::fromTextAndUrl(t('Download'), Url::fromUri($url, $options))->toRenderable();

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.