diff --git a/core/includes/file.inc b/core/includes/file.inc index e3ac8ea..47933db 100644 --- a/core/includes/file.inc +++ b/core/includes/file.inc @@ -7,9 +7,6 @@ use Drupal\Core\StreamWrapper\LocalStream; use Drupal\Component\PhpStorage\MTimeProtectedFastFileStorage; -use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException; -use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; -use Symfony\Component\HttpFoundation\BinaryFileResponse; /** * Stream wrapper bit flags that are the basis for composite types. @@ -1332,42 +1329,6 @@ function file_unmanaged_save_data($data, $destination = NULL, $replace = FILE_EX } /** - * Page callback: Handles private file transfers. - * - * Call modules that implement hook_file_download() to find out if a file is - * accessible and what headers it should be transferred with. If one or more - * modules returned headers the download will start with the returned headers. - * If a module returns -1 an AccessDeniedHttpException will be thrown. - * If the file exists but no modules responded an AccessDeniedHttpException will - * be thrown.If the file does not exist a NotFoundHttpException will be thrown. - * - * @see hook_file_download() - * @see system_menu() - */ -function file_download() { - // Merge remaining path arguments into relative file path. - $args = func_get_args(); - $scheme = array_shift($args); - $target = implode('/', $args); - $uri = $scheme . '://' . $target; - if (file_stream_wrapper_valid_scheme($scheme) && file_exists($uri)) { - // Let other modules provide headers and controls access to the file. - $headers = module_invoke_all('file_download', $uri); - foreach ($headers as $result) { - if ($result == -1) { - throw new AccessDeniedHttpException(); - } - } - if (count($headers)) { - return new BinaryFileResponse($uri, 200, $headers); - } - throw new AccessDeniedHttpException(); - } - throw new NotFoundHttpException(); -} - - -/** * Finds all files that match a given mask in a given directory. * * Directories and files beginning with a period are excluded; this diff --git a/core/lib/Drupal/Core/StreamWrapper/PrivateStream.php b/core/lib/Drupal/Core/StreamWrapper/PrivateStream.php index 833580f..5f71d05 100644 --- a/core/lib/Drupal/Core/StreamWrapper/PrivateStream.php +++ b/core/lib/Drupal/Core/StreamWrapper/PrivateStream.php @@ -30,6 +30,15 @@ public function getDirectoryPath() { */ function getExternalUrl() { $path = str_replace('\\', '/', $this->getTarget()); - return url('system/files/' . $path, array('absolute' => TRUE)); + $query = drupal_get_query_array($path); + $route_path = 'system/files/private'; + + // Allow modules like image to define a custom route. + if (isset($query['prefix'])) { + $route_path .= '/' . $query['prefix']; + unset($query['prefix']); + $path = drupal_http_build_query($query); + } + return url($route_path, array('absolute' => TRUE, 'query' => array('file' => $path))); } } diff --git a/core/modules/file/lib/Drupal/file/Tests/DownloadTest.php b/core/modules/file/lib/Drupal/file/Tests/DownloadTest.php index 0763507..106b08c 100644 --- a/core/modules/file/lib/Drupal/file/Tests/DownloadTest.php +++ b/core/modules/file/lib/Drupal/file/Tests/DownloadTest.php @@ -103,7 +103,7 @@ function testFileCreateUrl() { $script_path_original = $script_path; foreach (array('', 'index.php/') as $script_path) { $this->checkUrl('public', '', $basename, $base_url . '/' . file_stream_wrapper_get_instance_by_scheme('public')->getDirectoryPath() . '/' . $basename_encoded); - $this->checkUrl('private', '', $basename, $base_url . '/' . $script_path . 'system/files/' . $basename_encoded); + $this->checkUrl('private', '', $basename, $base_url . '/' . $script_path . 'system/files/private?file=' . $basename_encoded); } $script_path = $script_path_original; } diff --git a/core/modules/image/image.module b/core/modules/image/image.module index a7696a6..06ddcbe 100644 --- a/core/modules/image/image.module +++ b/core/modules/image/image.module @@ -725,6 +725,7 @@ function image_style_flush($style) { */ function image_style_url($style_name, $path) { $uri = image_style_path($style_name, $path); + // The token query is added even if the // 'image.settings:allow_insecure_derivatives' configuration is TRUE, so that // the emitted links remain valid if it is changed back to the default FALSE. @@ -739,6 +740,15 @@ function image_style_url($style_name, $path) { return url($directory_path . '/' . file_uri_target($uri), array('absolute' => TRUE, 'query' => $token_query)); } + // Replace the url for private files. + if (file_uri_scheme($uri) == 'private') { + list($schema, $path) = explode('//', $uri, 2); + $query = drupal_get_query_array($path); + $query['prefix'] = 'styles'; + $path = drupal_http_build_query($query); + $uri = $schema . '://' . $path; + } + $file_url = file_create_url($uri); // Append the query string with the token. return $file_url . (strpos($file_url, '?') !== FALSE ? '&' : '?') . drupal_http_build_query($token_query); diff --git a/core/modules/image/image.routing.yml b/core/modules/image/image.routing.yml index 0aa23ba..33c6b1b 100644 --- a/core/modules/image/image.routing.yml +++ b/core/modules/image/image.routing.yml @@ -11,3 +11,10 @@ image_effect_delete: _form: '\Drupal\image\Form\ImageEffectDeleteForm' requirements: _permission: 'administer image styles' + +image_style_private: + pattern: '/system/files/styles' + defaults: + _controller: '\Drupal\image\Controller\ImageStyleController::deliver' + + diff --git a/core/modules/system/lib/Drupal/system/FileController.php b/core/modules/system/lib/Drupal/system/FileController.php new file mode 100644 index 0000000..07e129f --- /dev/null +++ b/core/modules/system/lib/Drupal/system/FileController.php @@ -0,0 +1,77 @@ +moduleHandler = $module_handler; + } + + /** + * {@inheritdoc} + */ + public static function create(ContainerInterface $container) { + return new static($container->get('module_handler')); + } + + /** + * Handles private file transfers. + * + * Call modules that implement hook_file_download() to find out if a file is + * accessible and what headers it should be transferred with. If one or more + * modules returned headers the download will start with the returned headers. + * If a module returns -1 an AccessDeniedHttpException will be thrown. If the + * file exists but no modules responded an AccessDeniedHttpException will be + * thrown. If the file does not exist a NotFoundHttpException will be thrown. + * + * @see hook_file_download() + */ + public function fileDownload(Request $request, $scheme = 'private') { + $target = $request->query->get('file'); + // Merge remaining path arguments into relative file path. + $uri = $scheme . '://' . $target; + + if (file_stream_wrapper_valid_scheme($scheme) && file_exists($uri)) { + // Let other modules provide headers and controls access to the file. + $headers = $this->moduleHandler->invokeAll('file_download', array($uri)); + + foreach ($headers as $result) { + if ($result == -1) { + throw new AccessDeniedHttpException(); + } + } + + if (count($headers)) { + return new BinaryFileResponse($uri, 200, $headers); + } + + throw new AccessDeniedHttpException(); + } + + throw new NotFoundHttpException(); + } + +} diff --git a/core/modules/system/system.module b/core/modules/system/system.module index 0d472e5..11e15b0 100644 --- a/core/modules/system/system.module +++ b/core/modules/system/system.module @@ -617,13 +617,6 @@ function system_element_info() { * Implements hook_menu(). */ function system_menu() { - $items['system/files'] = array( - 'title' => 'File download', - 'page callback' => 'file_download', - 'page arguments' => array('private'), - 'access callback' => TRUE, - 'type' => MENU_CALLBACK, - ); $items['system/temporary'] = array( 'title' => 'Temporary files', 'page callback' => 'file_download', diff --git a/core/modules/system/system.routing.yml b/core/modules/system/system.routing.yml index 9859d6a..cecf327 100644 --- a/core/modules/system/system.routing.yml +++ b/core/modules/system/system.routing.yml @@ -87,3 +87,11 @@ date_format_localize_reset: _form: '\Drupal\system\Form\DateFormatLocalizeResetForm' requirements: _permission: 'administer site configuration' + +system_files: + pattern: '/system/files/{scheme}' + defaults: + _controller: 'Drupal\system\FileController::fileDownload' + scheme: private + requirements: + _access: 'TRUE'