You are here

function editor_file_download in Drupal 9

Same name and namespace in other branches
  1. 8 core/modules/editor/editor.module \editor_file_download()

Implements hook_file_download().

See also

file_file_download()

file_get_file_references()

File

core/modules/editor/editor.module, line 494
Adds bindings for client-side "text editors" to text formats.

Code

function editor_file_download($uri) {

  // Get the file record based on the URI. If not in the database just return.

  /** @var \Drupal\file\FileInterface[] $files */
  $files = \Drupal::entityTypeManager()
    ->getStorage('file')
    ->loadByProperties([
    'uri' => $uri,
  ]);
  if (count($files)) {
    foreach ($files as $item) {

      // Since some database servers sometimes use a case-insensitive comparison
      // by default, double check that the filename is an exact match.
      if ($item
        ->getFileUri() === $uri) {
        $file = $item;
        break;
      }
    }
  }
  if (!isset($file)) {
    return;
  }

  // Temporary files are handled by file_file_download(), so nothing to do here
  // about them.
  // @see file_file_download()
  // Find out if any editor-backed field contains the file.
  $usage_list = \Drupal::service('file.usage')
    ->listUsage($file);

  // Stop processing if there are no references in order to avoid returning
  // headers for files controlled by other modules. Make an exception for
  // temporary files where the host entity has not yet been saved (for example,
  // an image preview on a node creation form) in which case, allow download by
  // the file's owner.
  if (empty($usage_list['editor']) && ($file
    ->isPermanent() || $file
    ->getOwnerId() != \Drupal::currentUser()
    ->id())) {
    return;
  }

  // Editor.module MUST NOT call $file->access() here (like file_file_download()
  // does) as checking the 'download' access to a file entity would end up in
  // FileAccessControlHandler->checkAccess() and ->getFileReferences(), which
  // calls file_get_file_references(). This latter one would allow downloading
  // files only handled by the file.module, which is exactly not the case right
  // here. So instead we must check if the current user is allowed to view any
  // of the entities that reference the image using the 'editor' module.
  if ($file
    ->isPermanent()) {
    $referencing_entity_is_accessible = FALSE;
    $references = empty($usage_list['editor']) ? [] : $usage_list['editor'];
    foreach ($references as $entity_type => $entity_ids_usage_count) {
      $referencing_entities = \Drupal::entityTypeManager()
        ->getStorage($entity_type)
        ->loadMultiple(array_keys($entity_ids_usage_count));

      /** @var \Drupal\Core\Entity\EntityInterface $referencing_entity */
      foreach ($referencing_entities as $referencing_entity) {
        if ($referencing_entity
          ->access('view', NULL, TRUE)
          ->isAllowed()) {
          $referencing_entity_is_accessible = TRUE;
          break 2;
        }
      }
    }
    if (!$referencing_entity_is_accessible) {
      return -1;
    }
  }

  // Access is granted.
  $headers = file_get_content_headers($file);
  return $headers;
}