Issue #2669074 by kim.pepper, Berdir, gaydabura, andypost, ravi.shankar, KapilV, Wim Leers, bradjones1, nickolaj, alexpott, mondrake, 20th, yogeshmpawar, gaurav.kapoor, voleger, Pavan B S, Neslee Canil Pinto, aerozeppelin, Cameron Tod, daffie, dhruveshdtripathi, abramm, Nono95230, s.messaris, Mile23, claudiu.cristea, larowlan, catch, jibran, SpadXIII: Convert file_create_url() & file_url_transform_relative() to service, deprecate it

merge-requests/924/head
Alex Pott 2021-07-09 14:18:01 +01:00
parent fa3995b5dd
commit 3e4b17a7c9
No known key found for this signature in database
GPG Key ID: 31905460D4A69276
81 changed files with 1303 additions and 603 deletions

View File

@ -392,6 +392,9 @@ services:
file_system: file_system:
class: Drupal\Core\File\FileSystem class: Drupal\Core\File\FileSystem
arguments: ['@stream_wrapper_manager', '@settings', '@logger.channel.file'] arguments: ['@stream_wrapper_manager', '@settings', '@logger.channel.file']
file_url_generator:
class: Drupal\Core\File\FileUrlGenerator
arguments: ['@stream_wrapper_manager', '@request_stack', '@module_handler']
form_builder: form_builder:
class: Drupal\Core\Form\FormBuilder class: Drupal\Core\Form\FormBuilder
arguments: ['@form_validator', '@form_submitter', '@form_cache', '@module_handler', '@event_dispatcher', '@request_stack', '@class_resolver', '@element_info', '@theme.manager', '@?csrf_token'] arguments: ['@form_validator', '@form_submitter', '@form_cache', '@module_handler', '@event_dispatcher', '@request_stack', '@class_resolver', '@element_info', '@theme.manager', '@?csrf_token']
@ -1580,12 +1583,13 @@ services:
arguments: ['@settings'] arguments: ['@settings']
asset.css.collection_renderer: asset.css.collection_renderer:
class: Drupal\Core\Asset\CssCollectionRenderer class: Drupal\Core\Asset\CssCollectionRenderer
arguments: [ '@state' ] arguments: [ '@state', '@file_url_generator' ]
asset.css.collection_optimizer: asset.css.collection_optimizer:
class: Drupal\Core\Asset\CssCollectionOptimizer class: Drupal\Core\Asset\CssCollectionOptimizer
arguments: [ '@asset.css.collection_grouper', '@asset.css.optimizer', '@asset.css.dumper', '@state', '@file_system'] arguments: [ '@asset.css.collection_grouper', '@asset.css.optimizer', '@asset.css.dumper', '@state', '@file_system']
asset.css.optimizer: asset.css.optimizer:
class: Drupal\Core\Asset\CssOptimizer class: Drupal\Core\Asset\CssOptimizer
arguments: ['@file_url_generator']
asset.css.collection_grouper: asset.css.collection_grouper:
class: Drupal\Core\Asset\CssCollectionGrouper class: Drupal\Core\Asset\CssCollectionGrouper
asset.css.dumper: asset.css.dumper:
@ -1593,7 +1597,7 @@ services:
arguments: ['@file_system'] arguments: ['@file_system']
asset.js.collection_renderer: asset.js.collection_renderer:
class: Drupal\Core\Asset\JsCollectionRenderer class: Drupal\Core\Asset\JsCollectionRenderer
arguments: [ '@state' ] arguments: [ '@state', '@file_url_generator' ]
asset.js.collection_optimizer: asset.js.collection_optimizer:
class: Drupal\Core\Asset\JsCollectionOptimizer class: Drupal\Core\Asset\JsCollectionOptimizer
arguments: [ '@asset.js.collection_grouper', '@asset.js.optimizer', '@asset.js.dumper', '@state', '@file_system'] arguments: [ '@asset.js.collection_grouper', '@asset.js.optimizer', '@asset.js.dumper', '@state', '@file_system']
@ -1634,7 +1638,7 @@ services:
- { name: service_collector, tag: 'twig.extension', call: addExtension } - { name: service_collector, tag: 'twig.extension', call: addExtension }
twig.extension: twig.extension:
class: Drupal\Core\Template\TwigExtension class: Drupal\Core\Template\TwigExtension
arguments: ['@renderer', '@url_generator', '@theme.manager', '@date.formatter'] arguments: ['@renderer', '@url_generator', '@theme.manager', '@date.formatter', '@file_url_generator']
tags: tags:
- { name: twig.extension, priority: 100 } - { name: twig.extension, priority: 100 }
# @todo Figure out what to do about debugging functions. # @todo Figure out what to do about debugging functions.

View File

@ -5,9 +5,8 @@
* API for handling file uploads and server file management. * API for handling file uploads and server file management.
*/ */
use Drupal\Component\Utility\UrlHelper; use Drupal\Core\File\Exception\InvalidStreamWrapperException;
use Drupal\Core\File\FileSystemInterface; use Drupal\Core\File\FileSystemInterface;
use Drupal\Core\StreamWrapper\StreamWrapperManager;
/** /**
* @defgroup file File interface * @defgroup file File interface
@ -47,58 +46,24 @@ const FILE_STATUS_PERMANENT = 1;
* '/', nothing is done and the same string is returned. If a stream wrapper * '/', nothing is done and the same string is returned. If a stream wrapper
* could not be found to generate an external URL, then FALSE is returned. * could not be found to generate an external URL, then FALSE is returned.
* *
* @deprecated in drupal:9.3.0 and is removed from drupal:10.0.0.
* Use the appropriate method on \Drupal\Core\File\FileUrlGeneratorInterface
* instead.
*
* @see https://www.drupal.org/node/2940031
* @see https://www.drupal.org/node/515192 * @see https://www.drupal.org/node/515192
* @see file_url_transform_relative() * @see \Drupal\Core\File\FileUrlGeneratorInterface::generate()
* @see \Drupal\Core\File\FileUrlGeneratorInterface::generateString()
* @see \Drupal\Core\File\FileUrlGeneratorInterface::generateAbsoluteString()
* @see \Drupal\Core\File\FileUrlGeneratorInterface::transformRelative()
*/ */
function file_create_url($uri) { function file_create_url($uri) {
// Allow the URI to be altered, e.g. to serve a file from a CDN or static @trigger_error('file_create_url() is deprecated in drupal:9.3.0 and is removed from drupal:10.0.0. Use the appropriate method on \Drupal\Core\File\FileUrlGeneratorInterface instead. See https://www.drupal.org/node/2940031', E_USER_DEPRECATED);
// file server. try {
\Drupal::moduleHandler()->alter('file_url', $uri); return \Drupal::service('file_url_generator')->generateAbsoluteString($uri);
$scheme = StreamWrapperManager::getScheme($uri);
if (!$scheme) {
// Allow for:
// - root-relative URIs (e.g. /foo.jpg in http://example.com/foo.jpg)
// - protocol-relative URIs (e.g. //bar.jpg, which is expanded to
// http://example.com/bar.jpg by the browser when viewing a page over
// HTTP and to https://example.com/bar.jpg when viewing a HTTPS page)
// Both types of relative URIs are characterized by a leading slash, hence
// we can use a single check.
if (mb_substr($uri, 0, 1) == '/') {
return $uri;
}
else {
// If this is not a properly formatted stream, then it is a shipped file.
// Therefore, return the urlencoded URI with the base URL prepended.
$options = UrlHelper::parse($uri);
$path = $GLOBALS['base_url'] . '/' . UrlHelper::encodePath($options['path']);
// Append the query.
if ($options['query']) {
$path .= '?' . UrlHelper::buildQuery($options['query']);
}
// Append fragment.
if ($options['fragment']) {
$path .= '#' . $options['fragment'];
}
return $path;
}
} }
elseif ($scheme == 'http' || $scheme == 'https' || $scheme == 'data') { catch (InvalidStreamWrapperException $e) {
// Check for HTTP and data URI-encoded URLs so that we don't have to return FALSE;
// implement getExternalUrl() for the HTTP and data schemes.
return $uri;
}
else {
// Attempt to return an external URL using the appropriate wrapper.
if ($wrapper = \Drupal::service('stream_wrapper_manager')->getViaUri($uri)) {
return $wrapper->getExternalUrl();
}
else {
return FALSE;
}
} }
} }
@ -109,38 +74,23 @@ function file_create_url($uri) {
* content errors when using HTTPS + HTTP. * content errors when using HTTPS + HTTP.
* *
* @param string $file_url * @param string $file_url
* A file URL of a local file as generated by file_create_url(). * A file URL of a local file as generated by
* FileUrlGeneratorInterface::generateString().
* *
* @return string * @return string
* If the file URL indeed pointed to a local file and was indeed absolute, * If the file URL indeed pointed to a local file and was indeed absolute,
* then the transformed, relative URL to the local file. Otherwise: the * then the transformed, relative URL to the local file. Otherwise: the
* original value of $file_url. * original value of $file_url.
* *
* @see file_create_url() * @deprecated in drupal:9.3.0 and is removed from drupal:10.0.0.
* Use \Drupal\Core\File\FileUrlGenerator::transformRelative() instead.
*
* @see https://www.drupal.org/node/2940031
* @see \Drupal\Core\File\FileUrlGeneratorInterface::transformRelative()
*/ */
function file_url_transform_relative($file_url) { function file_url_transform_relative($file_url) {
// Unfortunately, we pretty much have to duplicate Symfony's @trigger_error('file_url_transform_relative() is deprecated in drupal:9.3.0 and is removed from drupal:10.0.0. Use \Drupal\Core\File\FileUrlGenerator::transformRelative() instead. See https://www.drupal.org/node/2940031', E_USER_DEPRECATED);
// Request::getHttpHost() method because Request::getPort() may return NULL return \Drupal::service('file_url_generator')->transformRelative($file_url);
// instead of a port number.
$request = \Drupal::request();
$host = $request->getHost();
$scheme = $request->getScheme();
$port = $request->getPort() ?: 80;
// Files may be accessible on a different port than the web request.
$file_url_port = parse_url($file_url, PHP_URL_PORT) ?? $port;
if ($file_url_port != $port) {
return $file_url;
}
if (('http' == $scheme && $port == 80) || ('https' == $scheme && $port == 443)) {
$http_host = $host;
}
else {
$http_host = $host . ':' . $port;
}
return preg_replace('|^https?://' . preg_quote($http_host, '|') . '|', '', $file_url);
} }
/** /**

View File

@ -347,13 +347,16 @@ function theme_get_setting($setting_name, $theme = NULL) {
} }
} }
/** @var \Drupal\Core\File\FileUrlGeneratorInterface $file_url_generator */
$file_url_generator = \Drupal::service('file_url_generator');
// Generate the path to the logo image. // Generate the path to the logo image.
if ($cache[$theme]->get('logo.use_default')) { if ($cache[$theme]->get('logo.use_default')) {
$logo = \Drupal::service('theme.initialization')->getActiveThemeByName($theme)->getLogo(); $logo = \Drupal::service('theme.initialization')->getActiveThemeByName($theme)->getLogo();
$cache[$theme]->set('logo.url', file_url_transform_relative(file_create_url($logo))); $cache[$theme]->set('logo.url', $file_url_generator->generateString($logo));
} }
elseif ($logo_path = $cache[$theme]->get('logo.path')) { elseif ($logo_path = $cache[$theme]->get('logo.path')) {
$cache[$theme]->set('logo.url', file_url_transform_relative(file_create_url($logo_path))); $cache[$theme]->set('logo.url', $file_url_generator->generateString($logo_path));
} }
// Generate the path to the favicon. // Generate the path to the favicon.
@ -361,14 +364,14 @@ function theme_get_setting($setting_name, $theme = NULL) {
$favicon_path = $cache[$theme]->get('favicon.path'); $favicon_path = $cache[$theme]->get('favicon.path');
if ($cache[$theme]->get('favicon.use_default')) { if ($cache[$theme]->get('favicon.use_default')) {
if (file_exists($favicon = $theme_object->getPath() . '/favicon.ico')) { if (file_exists($favicon = $theme_object->getPath() . '/favicon.ico')) {
$cache[$theme]->set('favicon.url', file_url_transform_relative(file_create_url($favicon))); $cache[$theme]->set('favicon.url', $file_url_generator->generateString($favicon));
} }
else { else {
$cache[$theme]->set('favicon.url', file_url_transform_relative(file_create_url('core/misc/favicon.ico'))); $cache[$theme]->set('favicon.url', $file_url_generator->generateString('core/misc/favicon.ico'));
} }
} }
elseif ($favicon_path) { elseif ($favicon_path) {
$cache[$theme]->set('favicon.url', file_url_transform_relative(file_create_url($favicon_path))); $cache[$theme]->set('favicon.url', $file_url_generator->generateString($favicon_path));
} }
else { else {
$cache[$theme]->set('features.favicon', FALSE); $cache[$theme]->set('features.favicon', FALSE);
@ -820,8 +823,11 @@ function template_preprocess_links(&$variables) {
* - http://www.whatwg.org/specs/web-apps/current-work/multipage/embedded-content.html#introduction-3:viewport-based-selection-2 * - http://www.whatwg.org/specs/web-apps/current-work/multipage/embedded-content.html#introduction-3:viewport-based-selection-2
*/ */
function template_preprocess_image(&$variables) { function template_preprocess_image(&$variables) {
/** @var \Drupal\Core\File\FileUrlGeneratorInterface $file_url_generator */
$file_url_generator = \Drupal::service('file_url_generator');
if (!empty($variables['uri'])) { if (!empty($variables['uri'])) {
$variables['attributes']['src'] = file_url_transform_relative(file_create_url($variables['uri'])); $variables['attributes']['src'] = $file_url_generator->generateString($variables['uri']);
} }
// Generate a srcset attribute conforming to the spec at // Generate a srcset attribute conforming to the spec at
// http://www.w3.org/html/wg/drafts/html/master/embedded-content.html#attr-img-srcset // http://www.w3.org/html/wg/drafts/html/master/embedded-content.html#attr-img-srcset
@ -829,7 +835,7 @@ function template_preprocess_image(&$variables) {
$srcset = []; $srcset = [];
foreach ($variables['srcset'] as $src) { foreach ($variables['srcset'] as $src) {
// URI is mandatory. // URI is mandatory.
$source = file_url_transform_relative(file_create_url($src['uri'])); $source = $file_url_generator->generateString($src['uri']);
if (isset($src['width']) && !empty($src['width'])) { if (isset($src['width']) && !empty($src['width'])) {
$source .= ' ' . $src['width']; $source .= ' ' . $src['width'];
} }

View File

@ -2,6 +2,7 @@
namespace Drupal\Core\Asset; namespace Drupal\Core\Asset;
use Drupal\Core\File\FileUrlGeneratorInterface;
use Drupal\Core\State\StateInterface; use Drupal\Core\State\StateInterface;
/** /**
@ -16,14 +17,28 @@ class CssCollectionRenderer implements AssetCollectionRendererInterface {
*/ */
protected $state; protected $state;
/**
* The file URL generator.
*
* @var \Drupal\Core\File\FileUrlGeneratorInterface
*/
protected $fileUrlGenerator;
/** /**
* Constructs a CssCollectionRenderer. * Constructs a CssCollectionRenderer.
* *
* @param \Drupal\Core\State\StateInterface $state * @param \Drupal\Core\State\StateInterface $state
* The state key/value store. * The state key/value store.
* @param \Drupal\Core\File\FileUrlGeneratorInterface $file_url_generator
* The file URL generator.
*/ */
public function __construct(StateInterface $state) { public function __construct(StateInterface $state, FileUrlGeneratorInterface $file_url_generator = NULL) {
$this->state = $state; $this->state = $state;
if (!$file_url_generator) {
@trigger_error('Calling CssCollectionRenderer::__construct() without the $file_url_generator argument is deprecated in drupal:9.3.0 and will be required before drupal:10.0.0. See https://www.drupal.org/node/2549139.', E_USER_DEPRECATED);
$file_url_generator = \Drupal::service('file_url_generator');
}
$this->fileUrlGenerator = $file_url_generator;
} }
/** /**
@ -55,7 +70,7 @@ class CssCollectionRenderer implements AssetCollectionRendererInterface {
switch ($css_asset['type']) { switch ($css_asset['type']) {
// For file items, output a LINK tag for file CSS assets. // For file items, output a LINK tag for file CSS assets.
case 'file': case 'file':
$element['#attributes']['href'] = file_url_transform_relative(file_create_url($css_asset['data'])); $element['#attributes']['href'] = $this->fileUrlGenerator->generateString($css_asset['data']);
// Only add the cache-busting query string if this isn't an aggregate // Only add the cache-busting query string if this isn't an aggregate
// file. // file.
if (!isset($css_asset['preprocessed'])) { if (!isset($css_asset['preprocessed'])) {

View File

@ -4,6 +4,7 @@ namespace Drupal\Core\Asset;
use Drupal\Component\Utility\Unicode; use Drupal\Component\Utility\Unicode;
use Drupal\Core\StreamWrapper\StreamWrapperManager; use Drupal\Core\StreamWrapper\StreamWrapperManager;
use Drupal\Core\File\FileUrlGeneratorInterface;
/** /**
* Optimizes a CSS asset. * Optimizes a CSS asset.
@ -17,6 +18,27 @@ class CssOptimizer implements AssetOptimizerInterface {
*/ */
public $rewriteFileURIBasePath; public $rewriteFileURIBasePath;
/**
* The file URL generator.
*
* @var \Drupal\Core\File\FileUrlGeneratorInterface
*/
protected $fileUrlGenerator;
/**
* Constructs a CssOptimizer.
*
* @param \Drupal\Core\File\FileUrlGeneratorInterface $file_url_generator
* The file URL generator.
*/
public function __construct(FileUrlGeneratorInterface $file_url_generator = NULL) {
if (!$file_url_generator) {
@trigger_error('Calling CssOptimizer::__construct() without the $file_url_generator argument is deprecated in drupal:9.3.0. The $file_url_generator argument will be required in drupal:10.0.0. See https://www.drupal.org/node/2940031', E_USER_DEPRECATED);
$file_url_generator = \Drupal::service('file_url_generator');
}
$this->fileUrlGenerator = $file_url_generator;
}
/** /**
* {@inheritdoc} * {@inheritdoc}
*/ */
@ -258,7 +280,27 @@ class CssOptimizer implements AssetOptimizerInterface {
$last = $path; $last = $path;
$path = preg_replace('`(^|/)(?!\.\./)([^/]+)/\.\./`', '$1', $path); $path = preg_replace('`(^|/)(?!\.\./)([^/]+)/\.\./`', '$1', $path);
} }
return 'url(' . file_url_transform_relative(file_create_url($path)) . ')'; return 'url(' . $this->getFileUrlGenerator()->generateString($path) . ')';
}
/**
* Returns the file URL generator.
*
* This is provided for BC as sub-classes may not call the parent constructor.
*
* @return \Drupal\Core\File\FileUrlGeneratorInterface
* The file URL generator.
*
* @internal
* This can be removed in Drupal 10.0.x when the constructor deprecation is
* removed.
*/
private function getFileUrlGenerator(): FileUrlGeneratorInterface {
if (!$this->fileUrlGenerator) {
@trigger_error('Calling CssOptimizer::__construct() without the $file_url_generator argument is deprecated in drupal:9.3.0. The $file_url_generator argument will be required in drupal:10.0.0. See https://www.drupal.org/node/2940031', E_USER_DEPRECATED);
$this->fileUrlGenerator = \Drupal::service('file_url_generator');
}
return $this->fileUrlGenerator;
} }
} }

View File

@ -3,6 +3,7 @@
namespace Drupal\Core\Asset; namespace Drupal\Core\Asset;
use Drupal\Component\Serialization\Json; use Drupal\Component\Serialization\Json;
use Drupal\Core\File\FileUrlGeneratorInterface;
use Drupal\Core\State\StateInterface; use Drupal\Core\State\StateInterface;
/** /**
@ -17,14 +18,28 @@ class JsCollectionRenderer implements AssetCollectionRendererInterface {
*/ */
protected $state; protected $state;
/**
* The file URL generator.
*
* @var \Drupal\Core\File\FileUrlGeneratorInterface
*/
protected $fileUrlGenerator;
/** /**
* Constructs a JsCollectionRenderer. * Constructs a JsCollectionRenderer.
* *
* @param \Drupal\Core\State\StateInterface $state * @param \Drupal\Core\State\StateInterface $state
* The state key/value store. * The state key/value store.
* @param \Drupal\Core\File\FileUrlGeneratorInterface $file_url_generator
* The file URL generator.
*/ */
public function __construct(StateInterface $state) { public function __construct(StateInterface $state, FileUrlGeneratorInterface $file_url_generator = NULL) {
$this->state = $state; $this->state = $state;
if (!$file_url_generator) {
@trigger_error('Calling JsCollectionRenderer::__construct() without the $file_url_generator argument is deprecated in drupal:9.3.0. The $file_url_generator argument will be required in drupal:10.0.0. See https://www.drupal.org/node/2940031', E_USER_DEPRECATED);
$file_url_generator = \Drupal::service('file_url_generator');
}
$this->fileUrlGenerator = $file_url_generator;
} }
/** /**
@ -74,7 +89,7 @@ class JsCollectionRenderer implements AssetCollectionRendererInterface {
case 'file': case 'file':
$query_string = $js_asset['version'] == -1 ? $default_query_string : 'v=' . $js_asset['version']; $query_string = $js_asset['version'] == -1 ? $default_query_string : 'v=' . $js_asset['version'];
$query_string_separator = (strpos($js_asset['data'], '?') !== FALSE) ? '&' : '?'; $query_string_separator = (strpos($js_asset['data'], '?') !== FALSE) ? '&' : '?';
$element['#attributes']['src'] = file_url_transform_relative(file_create_url($js_asset['data'])); $element['#attributes']['src'] = $this->fileUrlGenerator->generateString($js_asset['data']);
// Only add the cache-busting query string if this isn't an aggregate // Only add the cache-busting query string if this isn't an aggregate
// file. // file.
if (!isset($js_asset['preprocessed'])) { if (!isset($js_asset['preprocessed'])) {

View File

@ -0,0 +1,9 @@
<?php
namespace Drupal\Core\File\Exception;
/**
* Exception thrown when a file's stream wrapper is invalid.
*/
class InvalidStreamWrapperException extends FileException {
}

View File

@ -0,0 +1,229 @@
<?php
namespace Drupal\Core\File;
use Drupal\Component\Utility\UrlHelper;
use Drupal\Core\Extension\ModuleHandlerInterface;
use Drupal\Core\File\Exception\InvalidStreamWrapperException;
use Drupal\Core\StreamWrapper\StreamWrapperManager;
use Drupal\Core\StreamWrapper\StreamWrapperManagerInterface;
use Drupal\Core\Url;
use Symfony\Component\HttpFoundation\RequestStack;
/**
* Default implementation for the file URL generator service.
*/
class FileUrlGenerator implements FileUrlGeneratorInterface {
/**
* The stream wrapper manager.
*
* @var \Drupal\Core\StreamWrapper\StreamWrapperManagerInterface
*/
protected $streamWrapperManager;
/**
* The request stack.
*
* @var \Symfony\Component\HttpFoundation\RequestStack
*/
protected $requestStack;
/**
* The module handler.
*
* @var \Drupal\Core\Extension\ModuleHandlerInterface
*/
protected $moduleHandler;
/**
* Constructs a new file URL generator object.
*
* @param \Drupal\Core\StreamWrapper\StreamWrapperManagerInterface $stream_wrapper_manager
* The stream wrapper manager.
* @param \Symfony\Component\HttpFoundation\RequestStack $request_stack
* The request stack.
* @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler
* The module handler.
*/
public function __construct(StreamWrapperManagerInterface $stream_wrapper_manager, RequestStack $request_stack, ModuleHandlerInterface $module_handler) {
$this->streamWrapperManager = $stream_wrapper_manager;
$this->requestStack = $request_stack;
$this->moduleHandler = $module_handler;
}
/**
* {@inheritdoc}
*/
public function generateString(string $uri): string {
return $this->doGenerateString($uri, TRUE);
}
/**
* {@inheritdoc}
*/
public function generateAbsoluteString(string $uri): string {
return $this->doGenerateString($uri, FALSE);
}
/**
* Creates an absolute web-accessible URL string.
*
* @param string $uri
* The URI to a file for which we need an external URL, or the path to a
* shipped file.
* @param bool $relative
* Whether to return an relative or absolute URL.
*
* @return string
* An absolute string containing a URL that may be used to access the
* file.
*
* @throws \Drupal\Core\File\Exception\InvalidStreamWrapperException
* If a stream wrapper could not be found to generate an external URL.
*/
protected function doGenerateString(string $uri, bool $relative): string {
// Allow the URI to be altered, e.g. to serve a file from a CDN or static
// file server.
$this->moduleHandler->alter('file_url', $uri);
$scheme = StreamWrapperManager::getScheme($uri);
if (!$scheme) {
$baseUrl = $relative ? base_path() : $this->requestStack->getCurrentRequest()->getSchemeAndHttpHost() . base_path();
return $this->generatePath($baseUrl, $uri);
}
elseif ($scheme == 'http' || $scheme == 'https' || $scheme == 'data') {
// Check for HTTP and data URI-encoded URLs so that we don't have to
// implement getExternalUrl() for the HTTP and data schemes.
return $relative ? $this->transformRelative($uri) : $uri;
}
elseif ($wrapper = $this->streamWrapperManager->getViaUri($uri)) {
// Attempt to return an external URL using the appropriate wrapper.
$externalUrl = $wrapper->getExternalUrl();
return $relative ? $this->transformRelative($externalUrl) : $externalUrl;
}
throw new InvalidStreamWrapperException();
}
/**
* Generate a URL path.
*
* @param string $base_url
* The base URL.
* @param string $uri
* The URI.
*
* @return string
* The URL path.
*/
protected function generatePath(string $base_url, string $uri): string {
// Allow for:
// - root-relative URIs (e.g. /foo.jpg in http://example.com/foo.jpg)
// - protocol-relative URIs (e.g. //bar.jpg, which is expanded to
// http://example.com/bar.jpg by the browser when viewing a page over
// HTTP and to https://example.com/bar.jpg when viewing a HTTPS page)
// Both types of relative URIs are characterized by a leading slash, hence
// we can use a single check.
if (mb_substr($uri, 0, 1) == '/') {
return $uri;
}
else {
// If this is not a properly formatted stream, then it is a shipped
// file. Therefore, return the urlencoded URI with the base URL
// prepended.
$options = UrlHelper::parse($uri);
$path = $base_url . UrlHelper::encodePath($options['path']);
// Append the query.
if ($options['query']) {
$path .= '?' . UrlHelper::buildQuery($options['query']);
}
// Append fragment.
if ($options['fragment']) {
$path .= '#' . $options['fragment'];
}
return $path;
}
}
/**
* {@inheritdoc}
*/
public function generate(string $uri): Url {
// Allow the URI to be altered, e.g. to serve a file from a CDN or static
// file server.
$this->moduleHandler->alter('file_url', $uri);
$scheme = StreamWrapperManager::getScheme($uri);
if (!$scheme) {
// Allow for:
// - root-relative URIs (e.g. /foo.jpg in http://example.com/foo.jpg)
// - protocol-relative URIs (e.g. //bar.jpg, which is expanded to
// http://example.com/bar.jpg by the browser when viewing a page over
// HTTP and to https://example.com/bar.jpg when viewing a HTTPS page)
// Both types of relative URIs are characterized by a leading slash, hence
// we can use a single check.
if (mb_substr($uri, 0, 2) == '//') {
return Url::fromUri($uri);
}
elseif (mb_substr($uri, 0, 1) == '/') {
return Url::fromUri('base:' . str_replace($this->requestStack->getCurrentRequest()->getBasePath(), '', $uri));
}
else {
// If this is not a properly formatted stream, then it is a shipped
// file. Therefore, return the urlencoded URI.
$options = UrlHelper::parse($uri);
return Url::fromUri('base:' . UrlHelper::encodePath($options['path']), $options);
}
}
elseif ($scheme == 'http' || $scheme == 'https' || $scheme == 'data') {
// Check for HTTP and data URI-encoded URLs so that we don't have to
// implement getExternalUrl() for the HTTP and data schemes.
$options = UrlHelper::parse($uri);
return Url::fromUri(urldecode($options['path']), $options);
}
elseif ($wrapper = $this->streamWrapperManager->getViaUri($uri)) {
// Attempt to return an external URL using the appropriate wrapper.
return Url::fromUri('base:' . $this->transformRelative(urldecode($wrapper->getExternalUrl()), FALSE));
}
throw new InvalidStreamWrapperException();
}
/**
* {@inheritdoc}
*/
public function transformRelative(string $file_url, bool $root_relative = TRUE): string {
// Unfortunately, we pretty much have to duplicate Symfony's
// Request::getHttpHost() method because Request::getPort() may return NULL
// instead of a port number.
$request = $this->requestStack->getCurrentRequest();
$host = $request->getHost();
$scheme = $request->getScheme();
$port = $request->getPort() ?: 80;
// Files may be accessible on a different port than the web request.
$file_url_port = parse_url($file_url, PHP_URL_PORT) ?? $port;
if ($file_url_port != $port) {
return $file_url;
}
if (('http' == $scheme && $port == 80) || ('https' == $scheme && $port == 443)) {
$http_host = $host;
}
else {
$http_host = $host . ':' . $port;
}
// If this should not be a root-relative path but relative to the drupal
// base path, add it to the host to be removed from the URL as well.
if (!$root_relative) {
$http_host .= $request->getBasePath();
}
return preg_replace('|^https?://' . preg_quote($http_host, '|') . '|', '', $file_url);
}
}

View File

@ -0,0 +1,97 @@
<?php
namespace Drupal\Core\File;
use Drupal\Core\Url;
/**
* Generates file URLs for a stream to an external or local file.
*
* Compatibility: normal paths and stream wrappers.
*
* There are two kinds of local files:
* - "managed files", i.e. those stored by a Drupal-compatible stream wrapper.
* These are files that have either been uploaded by users or were generated
* automatically (for example through CSS aggregation).
* - "shipped files", i.e. those outside of the files directory, which ship as
* part of Drupal core or contributed modules or themes.
*
* Separate methods are provided to provide absolute and relative URLs as well
* as plain strings or Url objects, depending on the requirements. In general,
* it is recommended to always use relative URLs unless absolute URL's are
* required.
*/
interface FileUrlGeneratorInterface {
/**
* Creates a root-relative web-accessible URL string.
*
* @param string $uri
* The URI to a file for which we need an external URL, or the path to a
* shipped file.
*
* @return string
* For a local URL (matching domain), a root-relative string containing a
* URL that may be used to access the file. An absolute URL may be returned
* when using a CDN or a remote stream wrapper.
*
* @throws \Drupal\Core\File\Exception\InvalidStreamWrapperException
* If a stream wrapper could not be found to generate an external URL.
*/
public function generateString(string $uri): string;
/**
* Creates an absolute web-accessible URL string.
*
* @param string $uri
* The URI to a file for which we need an external URL, or the path to a
* shipped file.
*
* @return string
* An absolute string containing a URL that may be used to access the
* file.
*
* @throws \Drupal\Core\File\Exception\InvalidStreamWrapperException
* If a stream wrapper could not be found to generate an external URL.
*/
public function generateAbsoluteString(string $uri): string;
/**
* Creates a root-relative web-accessible URL object.
*
* @param string $uri
* The URI to a file for which we need an external URL, or the path to a
* shipped file.
*
* @return \Drupal\Core\Url
* For a local URL (matching domain), a base-relative Url object containing
* a URL that may be used to access the file. An Url object with absolute
* URL may be returned when using a CDN or a remote stream wrapper. Use
* setAbsolute() on the Url object to build an absolute URL.
*
* @throws \Drupal\Core\File\Exception\InvalidStreamWrapperException
* If a stream wrapper could not be found to generate an external URL.
*/
public function generate(string $uri): Url;
/**
* Transforms an absolute URL of a local file to a relative URL.
*
* May be useful to prevent problems on multisite set-ups and prevent mixed
* content errors when using HTTPS + HTTP.
*
* @param string $file_url
* A file URL of a local file as generated by
* \Drupal\Core\File\FileUrlGenerator::generate().
* @param bool $root_relative
* (optional) TRUE if the URL should be relative to the root path or FALSE
* if relative to the Drupal base path.
*
* @return string
* If the file URL indeed pointed to a local file and was indeed absolute,
* then the transformed, relative URL to the local file. Otherwise: the
* original value of $file_url.
*/
public function transformRelative(string $file_url, bool $root_relative = TRUE): string;
}

View File

@ -45,9 +45,9 @@ function hook_file_download($uri) {
/** /**
* Alter the URL to a file. * Alter the URL to a file.
* *
* This hook is called from file_create_url(), and is called fairly * This hook is called from \Drupal\Core\File\FileUrlGenerator::generate(),
* frequently (10+ times per page), depending on how many files there are in a * and is called fairly frequently (10+ times per page), depending on how many
* given page. * files there are in a given page.
* If CSS and JS aggregation are disabled, this can become very frequently * If CSS and JS aggregation are disabled, this can become very frequently
* (50+ times per page) so performance is critical. * (50+ times per page) so performance is critical.
* *

View File

@ -70,7 +70,7 @@ class ImageButton extends Submit {
$element['#attributes']['type'] = 'image'; $element['#attributes']['type'] = 'image';
Element::setAttributes($element, ['id', 'name', 'value']); Element::setAttributes($element, ['id', 'name', 'value']);
$element['#attributes']['src'] = file_url_transform_relative(file_create_url($element['#src'])); $element['#attributes']['src'] = \Drupal::service('file_url_generator')->generateString($element['#src']);
if (!empty($element['#title'])) { if (!empty($element['#title'])) {
$element['#attributes']['alt'] = $element['#title']; $element['#attributes']['alt'] = $element['#title'];
$element['#attributes']['title'] = $element['#title']; $element['#attributes']['title'] = $element['#title'];

View File

@ -6,6 +6,7 @@ use Drupal\Component\Utility\Html;
use Drupal\Component\Render\MarkupInterface; use Drupal\Component\Render\MarkupInterface;
use Drupal\Core\Cache\CacheableDependencyInterface; use Drupal\Core\Cache\CacheableDependencyInterface;
use Drupal\Core\Datetime\DateFormatterInterface; use Drupal\Core\Datetime\DateFormatterInterface;
use Drupal\Core\File\FileUrlGeneratorInterface;
use Drupal\Core\Render\AttachmentsInterface; use Drupal\Core\Render\AttachmentsInterface;
use Drupal\Core\Render\BubbleableMetadata; use Drupal\Core\Render\BubbleableMetadata;
use Drupal\Core\Render\Markup; use Drupal\Core\Render\Markup;
@ -61,6 +62,13 @@ class TwigExtension extends AbstractExtension {
*/ */
protected $dateFormatter; protected $dateFormatter;
/**
* The file URL generator.
*
* @var \Drupal\Core\File\FileUrlGeneratorInterface
*/
protected $fileUrlGenerator;
/** /**
* Constructs \Drupal\Core\Template\TwigExtension. * Constructs \Drupal\Core\Template\TwigExtension.
* *
@ -72,12 +80,19 @@ class TwigExtension extends AbstractExtension {
* The theme manager. * The theme manager.
* @param \Drupal\Core\Datetime\DateFormatterInterface $date_formatter * @param \Drupal\Core\Datetime\DateFormatterInterface $date_formatter
* The date formatter. * The date formatter.
* @param \Drupal\Core\File\FileUrlGeneratorInterface $file_url_generator
* The file URL generator.
*/ */
public function __construct(RendererInterface $renderer, UrlGeneratorInterface $url_generator, ThemeManagerInterface $theme_manager, DateFormatterInterface $date_formatter) { public function __construct(RendererInterface $renderer, UrlGeneratorInterface $url_generator, ThemeManagerInterface $theme_manager, DateFormatterInterface $date_formatter, FileUrlGeneratorInterface $file_url_generator = NULL) {
$this->renderer = $renderer; $this->renderer = $renderer;
$this->urlGenerator = $url_generator; $this->urlGenerator = $url_generator;
$this->themeManager = $theme_manager; $this->themeManager = $theme_manager;
$this->dateFormatter = $date_formatter; $this->dateFormatter = $date_formatter;
if (!$file_url_generator) {
@trigger_error('Calling TwigExtension::__construct() without the $file_url_generator argument is deprecated in drupal:9.3.0 and will be required in drupal:10.0.0. See https://www.drupal.org/node/2940031.', E_USER_DEPRECATED);
$file_url_generator = \Drupal::service('file_url_generator');
}
$this->fileUrlGenerator = $file_url_generator;
} }
/** /**
@ -92,9 +107,7 @@ class TwigExtension extends AbstractExtension {
new TwigFunction('url', [$this, 'getUrl'], ['is_safe_callback' => [$this, 'isUrlGenerationSafe']]), new TwigFunction('url', [$this, 'getUrl'], ['is_safe_callback' => [$this, 'isUrlGenerationSafe']]),
new TwigFunction('path', [$this, 'getPath'], ['is_safe_callback' => [$this, 'isUrlGenerationSafe']]), new TwigFunction('path', [$this, 'getPath'], ['is_safe_callback' => [$this, 'isUrlGenerationSafe']]),
new TwigFunction('link', [$this, 'getLink']), new TwigFunction('link', [$this, 'getLink']),
new TwigFunction('file_url', function ($uri) { new TwigFunction('file_url', [$this->fileUrlGenerator, 'generateString']),
return file_url_transform_relative(file_create_url($uri));
}),
new TwigFunction('attach_library', [$this, 'attachLibrary']), new TwigFunction('attach_library', [$this, 'attachLibrary']),
new TwigFunction('active_theme_path', [$this, 'getActiveThemePath']), new TwigFunction('active_theme_path', [$this, 'getActiveThemePath']),
new TwigFunction('active_theme', [$this, 'getActiveTheme']), new TwigFunction('active_theme', [$this, 'getActiveTheme']),

View File

@ -68,7 +68,7 @@ class ImportOpmlTest extends AggregatorTestBase {
$path = $this->getEmptyOpml(); $path = $this->getEmptyOpml();
$edit = [ $edit = [
'files[upload]' => $path, 'files[upload]' => $path,
'remote' => file_create_url($path), 'remote' => \Drupal::service('file_url_generator')->generateAbsoluteString($path),
]; ];
$this->drupalGet('admin/config/services/aggregator/add/opml'); $this->drupalGet('admin/config/services/aggregator/add/opml');
$this->submitForm($edit, 'Import'); $this->submitForm($edit, 'Import');
@ -98,7 +98,7 @@ class ImportOpmlTest extends AggregatorTestBase {
$this->assertSession()->pageTextContains('No new feed has been added.'); $this->assertSession()->pageTextContains('No new feed has been added.');
// Attempting to load empty OPML from remote URL // Attempting to load empty OPML from remote URL
$edit = ['remote' => file_create_url($this->getEmptyOpml())]; $edit = ['remote' => \Drupal::service('file_url_generator')->generateAbsoluteString($this->getEmptyOpml())];
$this->drupalGet('admin/config/services/aggregator/add/opml'); $this->drupalGet('admin/config/services/aggregator/add/opml');
$this->submitForm($edit, 'Import'); $this->submitForm($edit, 'Import');
$this->assertSession()->pageTextContains('No new feed has been added.'); $this->assertSession()->pageTextContains('No new feed has been added.');

View File

@ -4,6 +4,7 @@ namespace Drupal\ckeditor\Plugin\Editor;
use Drupal\Core\Extension\ModuleHandlerInterface; use Drupal\Core\Extension\ModuleHandlerInterface;
use Drupal\ckeditor\CKEditorPluginManager; use Drupal\ckeditor\CKEditorPluginManager;
use Drupal\Core\File\FileUrlGeneratorInterface;
use Drupal\Core\Form\FormStateInterface; use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Language\LanguageManagerInterface; use Drupal\Core\Language\LanguageManagerInterface;
use Drupal\Core\Render\Element; use Drupal\Core\Render\Element;
@ -58,6 +59,13 @@ class CKEditor extends EditorBase implements ContainerFactoryPluginInterface {
*/ */
protected $renderer; protected $renderer;
/**
* The file URL generator.
*
* @var \Drupal\Core\File\FileUrlGeneratorInterface
*/
protected $fileUrlGenerator;
/** /**
* The state key/value store. * The state key/value store.
* *
@ -84,14 +92,21 @@ class CKEditor extends EditorBase implements ContainerFactoryPluginInterface {
* The renderer. * The renderer.
* @param \Drupal\Core\State\StateInterface $state * @param \Drupal\Core\State\StateInterface $state
* The state key/value store. * The state key/value store.
* @param \Drupal\Core\File\FileUrlGeneratorInterface $file_url_generator
* The file URL generator.
*/ */
public function __construct(array $configuration, $plugin_id, $plugin_definition, CKEditorPluginManager $ckeditor_plugin_manager, ModuleHandlerInterface $module_handler, LanguageManagerInterface $language_manager, RendererInterface $renderer, StateInterface $state) { public function __construct(array $configuration, $plugin_id, $plugin_definition, CKEditorPluginManager $ckeditor_plugin_manager, ModuleHandlerInterface $module_handler, LanguageManagerInterface $language_manager, RendererInterface $renderer, StateInterface $state, FileUrlGeneratorInterface $file_url_generator = NULL) {
parent::__construct($configuration, $plugin_id, $plugin_definition); parent::__construct($configuration, $plugin_id, $plugin_definition);
$this->ckeditorPluginManager = $ckeditor_plugin_manager; $this->ckeditorPluginManager = $ckeditor_plugin_manager;
$this->moduleHandler = $module_handler; $this->moduleHandler = $module_handler;
$this->languageManager = $language_manager; $this->languageManager = $language_manager;
$this->renderer = $renderer; $this->renderer = $renderer;
$this->state = $state; $this->state = $state;
if (!$file_url_generator) {
@trigger_error('Calling CKEditor::__construct() without the $file_url_generator argument is deprecated in drupal:9.3.0 and will be required before drupal:10.0.0. See https://www.drupal.org/node/2940031', E_USER_DEPRECATED);
$file_url_generator = \Drupal::service('file_url_generator');
}
$this->fileUrlGenerator = $file_url_generator;
} }
/** /**
@ -106,7 +121,8 @@ class CKEditor extends EditorBase implements ContainerFactoryPluginInterface {
$container->get('module_handler'), $container->get('module_handler'),
$container->get('language_manager'), $container->get('language_manager'),
$container->get('renderer'), $container->get('renderer'),
$container->get('state') $container->get('state'),
$container->get('file_url_generator')
); );
} }
@ -307,11 +323,8 @@ class CKEditor extends EditorBase implements ContainerFactoryPluginInterface {
]; ];
// Finally, set Drupal-specific CKEditor settings. // Finally, set Drupal-specific CKEditor settings.
$root_relative_file_url = function ($uri) {
return file_url_transform_relative(file_create_url($uri));
};
$settings += [ $settings += [
'drupalExternalPlugins' => array_map($root_relative_file_url, $external_plugin_files), 'drupalExternalPlugins' => array_map([$this->fileUrlGenerator, 'generateString'], $external_plugin_files),
]; ];
// Parse all CKEditor plugin JavaScript files for translations. // Parse all CKEditor plugin JavaScript files for translations.
@ -444,8 +457,7 @@ class CKEditor extends EditorBase implements ContainerFactoryPluginInterface {
$query_string_separator = (strpos($item, '?') !== FALSE) ? '&' : '?'; $query_string_separator = (strpos($item, '?') !== FALSE) ? '&' : '?';
return $item . $query_string_separator . $query_string; return $item . $query_string_separator . $query_string;
}, $css); }, $css);
$css = array_map('file_create_url', $css); $css = array_map([$this->fileUrlGenerator, 'generateString'], $css);
$css = array_map('file_url_transform_relative', $css);
return array_values($css); return array_values($css);
} }

View File

@ -4,8 +4,10 @@ namespace Drupal\ckeditor_test\Form;
use Drupal\ckeditor\Ajax\AddStyleSheetCommand; use Drupal\ckeditor\Ajax\AddStyleSheetCommand;
use Drupal\Core\Ajax\AjaxResponse; use Drupal\Core\Ajax\AjaxResponse;
use Drupal\Core\File\FileUrlGeneratorInterface;
use Drupal\Core\Form\FormBase; use Drupal\Core\Form\FormBase;
use Drupal\Core\Form\FormStateInterface; use Drupal\Core\Form\FormStateInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
/** /**
* A form for testing delivery of CSS to CKEditor via AJAX. * A form for testing delivery of CSS to CKEditor via AJAX.
@ -14,6 +16,30 @@ use Drupal\Core\Form\FormStateInterface;
*/ */
class AjaxCssForm extends FormBase { class AjaxCssForm extends FormBase {
/**
* The file URL generator.
*
* @var \Drupal\Core\File\FileUrlGeneratorInterface
*/
protected $fileUrlGenerator;
/**
* Constructs an AjaxCssForm.
*
* @param \Drupal\Core\File\FileUrlGeneratorInterface $file_url_generator
* The file URL generator.
*/
public function __construct(FileUrlGeneratorInterface $file_url_generator) {
$this->fileUrlGenerator = $file_url_generator;
}
/**
* @inheritDoc
*/
public static function create(ContainerInterface $container) {
return new static($container->get('file_url_generator'));
}
/** /**
* {@inheritdoc} * {@inheritdoc}
*/ */
@ -82,8 +108,7 @@ class AjaxCssForm extends FormBase {
protected function generateResponse($editor_id) { protected function generateResponse($editor_id) {
// Build a URL to the style sheet that will be added. // Build a URL to the style sheet that will be added.
$url = drupal_get_path('module', 'ckeditor_test') . '/css/test.css'; $url = drupal_get_path('module', 'ckeditor_test') . '/css/test.css';
$url = file_create_url($url); $url = $this->fileUrlGenerator->generateString($url);
$url = file_url_transform_relative($url);
$response = new AjaxResponse(); $response = new AjaxResponse();
return $response return $response

View File

@ -76,7 +76,9 @@ class CKEditorToolbarButtonTest extends BrowserTestBase {
$json_encode = function ($html) { $json_encode = function ($html) {
return trim(Json::encode($html), '"'); return trim(Json::encode($html), '"');
}; };
$markup = $json_encode(file_url_transform_relative(file_create_url('core/modules/ckeditor/js/plugins/drupalimage/icons/drupalimage.png'))); /** @var \Drupal\Core\File\FileUrlGeneratorInterface $file_url_generator */
$file_url_generator = \Drupal::service('file_url_generator');
$markup = $json_encode($file_url_generator->generateString('core/modules/ckeditor/js/plugins/drupalimage/icons/drupalimage.png'));
$this->assertRaw($markup); $this->assertRaw($markup);
} }

View File

@ -35,6 +35,13 @@ class CKEditorTest extends KernelTestBase {
*/ */
protected $ckeditor; protected $ckeditor;
/**
* The file URL generator.
*
* @var \Drupal\Core\File\FileUrlGeneratorInterface
*/
protected $fileUrlGenerator;
/** /**
* The Editor Plugin Manager. * The Editor Plugin Manager.
* *
@ -44,6 +51,7 @@ class CKEditorTest extends KernelTestBase {
protected function setUp(): void { protected function setUp(): void {
parent::setUp(); parent::setUp();
$this->fileUrlGenerator = $this->container->get('file_url_generator');
// Install the Filter module. // Install the Filter module.
@ -93,8 +101,8 @@ class CKEditorTest extends KernelTestBase {
'language' => 'en', 'language' => 'en',
'stylesSet' => FALSE, 'stylesSet' => FALSE,
'drupalExternalPlugins' => [ 'drupalExternalPlugins' => [
'drupalimage' => file_url_transform_relative(file_create_url('core/modules/ckeditor/js/plugins/drupalimage/plugin.js')), 'drupalimage' => $this->fileUrlGenerator->generateString('core/modules/ckeditor/js/plugins/drupalimage/plugin.js'),
'drupallink' => file_url_transform_relative(file_create_url('core/modules/ckeditor/js/plugins/drupallink/plugin.js')), 'drupallink' => $this->fileUrlGenerator->generateString('core/modules/ckeditor/js/plugins/drupallink/plugin.js'),
], ],
]; ];
$this->assertEquals($expected_config, $this->ckeditor->getJSSettings($editor), 'Generated JS settings are correct for default configuration.'); $this->assertEquals($expected_config, $this->ckeditor->getJSSettings($editor), 'Generated JS settings are correct for default configuration.');
@ -114,9 +122,9 @@ class CKEditorTest extends KernelTestBase {
$expected_config['toolbar'][0]['items'][] = 'Format'; $expected_config['toolbar'][0]['items'][] = 'Format';
$expected_config['format_tags'] = 'p;h2;h3;h4;h5;h6'; $expected_config['format_tags'] = 'p;h2;h3;h4;h5;h6';
$expected_config['extraPlugins'] .= ',llama_contextual,llama_contextual_and_button'; $expected_config['extraPlugins'] .= ',llama_contextual,llama_contextual_and_button';
$expected_config['drupalExternalPlugins']['llama_contextual'] = file_url_transform_relative(file_create_url('core/modules/ckeditor/tests/modules/js/llama_contextual.js')); $expected_config['drupalExternalPlugins']['llama_contextual'] = $this->fileUrlGenerator->generateString('core/modules/ckeditor/tests/modules/js/llama_contextual.js');
$expected_config['drupalExternalPlugins']['llama_contextual_and_button'] = file_url_transform_relative(file_create_url('core/modules/ckeditor/tests/modules/js/llama_contextual_and_button.js')); $expected_config['drupalExternalPlugins']['llama_contextual_and_button'] = $this->fileUrlGenerator->generateString('core/modules/ckeditor/tests/modules/js/llama_contextual_and_button.js');
$expected_config['contentsCss'][] = file_url_transform_relative(file_create_url('core/modules/ckeditor/tests/modules/ckeditor_test.css')) . $query_string; $expected_config['contentsCss'][] = $this->fileUrlGenerator->generateString('core/modules/ckeditor/tests/modules/ckeditor_test.css') . $query_string;
$this->assertEquals($expected_config, $this->ckeditor->getJSSettings($editor), 'Generated JS settings are correct for customized configuration.'); $this->assertEquals($expected_config, $this->ckeditor->getJSSettings($editor), 'Generated JS settings are correct for customized configuration.');
// Change the allowed HTML tags; the "allowedContent" and "format_tags" // Change the allowed HTML tags; the "allowedContent" and "format_tags"
@ -257,7 +265,7 @@ class CKEditorTest extends KernelTestBase {
// Enable the editor_test module, which implements hook_ckeditor_css_alter(). // Enable the editor_test module, which implements hook_ckeditor_css_alter().
$this->enableModules(['ckeditor_test']); $this->enableModules(['ckeditor_test']);
$expected[] = file_url_transform_relative(file_create_url(drupal_get_path('module', 'ckeditor_test') . '/ckeditor_test.css')) . $query_string; $expected[] = $this->fileUrlGenerator->generateString(drupal_get_path('module', 'ckeditor_test') . '/ckeditor_test.css') . $query_string;
$this->assertSame($expected, $this->ckeditor->buildContentsCssJSSetting($editor), '"contentsCss" configuration part of JS settings built correctly while a hook_ckeditor_css_alter() implementation exists.'); $this->assertSame($expected, $this->ckeditor->buildContentsCssJSSetting($editor), '"contentsCss" configuration part of JS settings built correctly while a hook_ckeditor_css_alter() implementation exists.');
// Enable LlamaCss plugin, which adds an additional CKEditor stylesheet. // Enable LlamaCss plugin, which adds an additional CKEditor stylesheet.
@ -269,17 +277,17 @@ class CKEditorTest extends KernelTestBase {
$settings['toolbar']['rows'][0][0]['items'][] = 'LlamaCSS'; $settings['toolbar']['rows'][0][0]['items'][] = 'LlamaCSS';
$editor->setSettings($settings); $editor->setSettings($settings);
$editor->save(); $editor->save();
$expected[] = file_url_transform_relative(file_create_url(drupal_get_path('module', 'ckeditor_test') . '/css/llama.css')) . $query_string; $expected[] = $this->fileUrlGenerator->generateString(drupal_get_path('module', 'ckeditor_test') . '/css/llama.css') . $query_string;
$this->assertSame($expected, $this->ckeditor->buildContentsCssJSSetting($editor), '"contentsCss" configuration part of JS settings built correctly while a CKEditorPluginInterface implementation exists.'); $this->assertSame($expected, $this->ckeditor->buildContentsCssJSSetting($editor), '"contentsCss" configuration part of JS settings built correctly while a CKEditorPluginInterface implementation exists.');
// Enable the Bartik theme, which specifies a CKEditor stylesheet. // Enable the Bartik theme, which specifies a CKEditor stylesheet.
\Drupal::service('theme_installer')->install(['bartik']); \Drupal::service('theme_installer')->install(['bartik']);
$this->config('system.theme')->set('default', 'bartik')->save(); $this->config('system.theme')->set('default', 'bartik')->save();
$expected[] = file_url_transform_relative(file_create_url('core/themes/bartik/css/base/elements.css')) . $query_string; $expected[] = $this->fileUrlGenerator->generateString('core/themes/bartik/css/base/elements.css') . $query_string;
$expected[] = file_url_transform_relative(file_create_url('core/themes/bartik/css/components/captions.css')) . $query_string; $expected[] = $this->fileUrlGenerator->generateString('core/themes/bartik/css/components/captions.css') . $query_string;
$expected[] = file_url_transform_relative(file_create_url('core/themes/bartik/css/components/table.css')) . $query_string; $expected[] = $this->fileUrlGenerator->generateString('core/themes/bartik/css/components/table.css') . $query_string;
$expected[] = file_url_transform_relative(file_create_url('core/themes/bartik/css/components/text-formatted.css')) . $query_string; $expected[] = $this->fileUrlGenerator->generateString('core/themes/bartik/css/components/text-formatted.css') . $query_string;
$expected[] = file_url_transform_relative(file_create_url('core/themes/bartik/css/classy/components/media-embed-error.css')) . $query_string; $expected[] = $this->fileUrlGenerator->generateString('core/themes/bartik/css/classy/components/media-embed-error.css') . $query_string;
$this->assertSame($expected, $this->ckeditor->buildContentsCssJSSetting($editor), '"contentsCss" configuration part of JS settings built correctly while a theme providing a CKEditor stylesheet exists.'); $this->assertSame($expected, $this->ckeditor->buildContentsCssJSSetting($editor), '"contentsCss" configuration part of JS settings built correctly while a theme providing a CKEditor stylesheet exists.');
} }
@ -543,8 +551,8 @@ class CKEditorTest extends KernelTestBase {
protected function getDefaultContentsCssConfig() { protected function getDefaultContentsCssConfig() {
$query_string = '?0='; $query_string = '?0=';
return [ return [
file_url_transform_relative(file_create_url('core/modules/ckeditor/css/ckeditor-iframe.css')) . $query_string, $this->fileUrlGenerator->generateString('core/modules/ckeditor/css/ckeditor-iframe.css') . $query_string,
file_url_transform_relative(file_create_url('core/modules/system/css/components/align.module.css')) . $query_string, $this->fileUrlGenerator->generateString('core/modules/system/css/components/align.module.css') . $query_string,
]; ];
} }

View File

@ -474,7 +474,7 @@ function color_scheme_form_submit($form, FormStateInterface $form_state) {
} }
foreach ($files as $file) { foreach ($files as $file) {
$css_optimizer = new CssOptimizer(); $css_optimizer = new CssOptimizer(\Drupal::service('file_url_generator'));
// Aggregate @imports recursively for each configured top level CSS file // Aggregate @imports recursively for each configured top level CSS file
// without optimization. Aggregation and optimization will be // without optimization. Aggregation and optimization will be
// handled by drupal_build_css_cache() only. // handled by drupal_build_css_cache() only.

View File

@ -25,7 +25,7 @@ class ColorSystemBrandingBlockAlter implements RenderCallbackInterface {
// Override logo. // Override logo.
$logo = $config->get('logo'); $logo = $config->get('logo');
if ($logo && $build['content']['site_logo'] && preg_match('!' . $theme_key . '/logo.svg$!', $build['content']['site_logo']['#uri'])) { if ($logo && $build['content']['site_logo'] && preg_match('!' . $theme_key . '/logo.svg$!', $build['content']['site_logo']['#uri'])) {
$build['content']['site_logo']['#uri'] = file_url_transform_relative(file_create_url($logo)); $build['content']['site_logo']['#uri'] = \Drupal::service('file_url_generator')->generateString($logo);
} }
return $build; return $build;

View File

@ -121,9 +121,11 @@ class ColorTest extends BrowserTestBase {
$this->drupalGet('<front>'); $this->drupalGet('<front>');
$stylesheets = $this->config('color.theme.' . $theme)->get('stylesheets'); $stylesheets = $this->config('color.theme.' . $theme)->get('stylesheets');
/** @var \Drupal\Core\File\FileUrlGeneratorInterface $file_url_generator */
$file_url_generator = \Drupal::service('file_url_generator');
// Make sure the color stylesheet is included in the content. // Make sure the color stylesheet is included in the content.
foreach ($stylesheets as $stylesheet) { foreach ($stylesheets as $stylesheet) {
$this->assertSession()->responseMatches('|' . file_url_transform_relative(file_create_url($stylesheet)) . '|'); $this->assertSession()->responseMatches('|' . $file_url_generator->generateString($stylesheet) . '|');
$stylesheet_content = implode("\n", file($stylesheet)); $stylesheet_content = implode("\n", file($stylesheet));
$this->assertStringContainsString('color: #123456', $stylesheet_content, 'Make sure the color we changed is in the color stylesheet. (' . $theme . ')'); $this->assertStringContainsString('color: #123456', $stylesheet_content, 'Make sure the color we changed is in the color stylesheet. (' . $theme . ')');
} }

View File

@ -1224,9 +1224,9 @@ function file_tokens($type, $tokens, array $data, array $options, BubbleableMeta
break; break;
case 'url': case 'url':
// Ideally, this would use file_url_transform_relative(), but because // Ideally, this would use return a relative URL, but because tokens
// tokens are also often used in e-mails, it's better to keep absolute // are also often used in e-mails, it's better to keep absolute file
// file URLs. The 'url.site' cache context is associated to ensure the // URLs. The 'url.site' cache context is associated to ensure the
// correct absolute URL is used in case of a multisite setup. // correct absolute URL is used in case of a multisite setup.
$replacements[$original] = $file->createFileUrl(FALSE); $replacements[$original] = $file->createFileUrl(FALSE);
$bubbleable_metadata->addCacheContexts(['url.site']); $bubbleable_metadata->addCacheContexts(['url.site']);
@ -1482,13 +1482,9 @@ function template_preprocess_file_link(&$variables) {
$file = $variables['file']; $file = $variables['file'];
$options = []; $options = [];
// @todo Wrap in file_url_transform_relative(). This is currently /** @var \Drupal\Core\File\FileUrlGeneratorInterface $file_url_generator */
// impossible. As a work-around, we currently add the 'url.site' cache context $file_url_generator = \Drupal::service('file_url_generator');
// to ensure different file URLs are generated for different sites in a $url = $file_url_generator->generate($file->getFileUri());
// multisite setup, including HTTP and HTTPS versions of the same site.
// Fix in https://www.drupal.org/node/2646744.
$url = $file->createFileUrl(FALSE);
$variables['#cache']['contexts'][] = 'url.site';
$mime_type = $file->getMimeType(); $mime_type = $file->getMimeType();
$options['attributes']['type'] = $mime_type; $options['attributes']['type'] = $mime_type;
@ -1516,7 +1512,7 @@ function template_preprocess_file_link(&$variables) {
$variables['attributes']->addClass($classes); $variables['attributes']->addClass($classes);
$variables['file_size'] = format_size($file->getSize()); $variables['file_size'] = format_size($file->getSize());
$variables['link'] = Link::fromTextAndUrl($link_text, Url::fromUri($url, $options))->toRenderable(); $variables['link'] = Link::fromTextAndUrl($link_text, $url->setOptions($options))->toRenderable();
} }
/** /**

View File

@ -27,7 +27,9 @@ class ComputedFileUrl extends TypedData {
assert($this->getParent()->getEntity() instanceof FileInterface); assert($this->getParent()->getEntity() instanceof FileInterface);
$uri = $this->getParent()->getEntity()->getFileUri(); $uri = $this->getParent()->getEntity()->getFileUri();
$this->url = file_url_transform_relative(file_create_url($uri)); /** @var \Drupal\Core\File\FileUrlGeneratorInterface $file_url_generator */
$file_url_generator = \Drupal::service('file_url_generator');
$this->url = $file_url_generator->generateString($uri);
return $this->url; return $this->url;
} }

View File

@ -80,11 +80,9 @@ class File extends ContentEntityBase implements FileInterface {
* {@inheritdoc} * {@inheritdoc}
*/ */
public function createFileUrl($relative = TRUE) { public function createFileUrl($relative = TRUE) {
$url = file_create_url($this->getFileUri()); /** @var \Drupal\Core\File\FileUrlGeneratorInterface $file_url_generator */
if ($relative && $url) { $file_url_generator = \Drupal::service('file_url_generator');
$url = file_url_transform_relative($url); return $relative ? $file_url_generator->generateString($this->getFileUri()) : $file_url_generator->generateAbsoluteString($this->getFileUri());
}
return $url;
} }
/** /**

View File

@ -59,8 +59,7 @@ interface FileInterface extends ContentEntityInterface, EntityChangedInterface,
* @return string * @return string
* A string containing a URL that may be used to access the file. * A string containing a URL that may be used to access the file.
* *
* @see file_create_url() * @see \Drupal\Core\File\FileUrlGeneratorInterface
* @see file_url_transform_relative()
*/ */
public function createFileUrl($relative = TRUE); public function createFileUrl($relative = TRUE);

View File

@ -6,14 +6,67 @@ use Drupal\Core\Field\FieldDefinitionInterface;
use Drupal\Core\Field\FieldItemInterface; use Drupal\Core\Field\FieldItemInterface;
use Drupal\Core\Field\FieldItemListInterface; use Drupal\Core\Field\FieldItemListInterface;
use Drupal\Core\Field\FormatterBase; use Drupal\Core\Field\FormatterBase;
use Drupal\Core\File\FileUrlGeneratorInterface;
use Drupal\Core\Form\FormStateInterface; use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Url; use Symfony\Component\DependencyInjection\ContainerInterface;
/** /**
* Base class for file formatters, which allow to link to the file download URL. * Base class for file formatters, which allow to link to the file download URL.
*/ */
abstract class BaseFieldFileFormatterBase extends FormatterBase { abstract class BaseFieldFileFormatterBase extends FormatterBase {
/**
* The file URL generator.
*
* @var \Drupal\Core\File\FileUrlGeneratorInterface
*/
protected $fileUrlGenerator;
/**
* Constructs a BaseFieldFileFormatterBase object.
*
* @param string $plugin_id
* The plugin_id for the formatter.
* @param mixed $plugin_definition
* The plugin implementation definition.
* @param \Drupal\Core\Field\FieldDefinitionInterface $field_definition
* The definition of the field to which the formatter is associated.
* @param array $settings
* The formatter settings.
* @param string $label
* The formatter label display setting.
* @param string $view_mode
* The view mode.
* @param array $third_party_settings
* Any third party settings.
* @param \Drupal\Core\File\FileUrlGeneratorInterface $file_url_generator
* The file URL generator.
*/
public function __construct($plugin_id, $plugin_definition, FieldDefinitionInterface $field_definition, array $settings, $label, $view_mode, array $third_party_settings, FileUrlGeneratorInterface $file_url_generator = NULL) {
parent::__construct($plugin_id, $plugin_definition, $field_definition, $settings, $label, $view_mode, $third_party_settings);
if (!$file_url_generator) {
@trigger_error('Calling BaseFieldFileFormatterBase::__construct() without the $file_url_generator argument is deprecated in drupal:9.3.0 and the $file_url_generator argument will be required in drupal:10.0.0. See https://www.drupal.org/node/2940031', E_USER_DEPRECATED);
$file_url_generator = \Drupal::service('file_url_generator');
}
$this->fileUrlGenerator = $file_url_generator;
}
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
return new static(
$plugin_id,
$plugin_definition,
$configuration['field_definition'],
$configuration['settings'],
$configuration['label'],
$configuration['view_mode'],
$configuration['third_party_settings'],
$container->get('file_url_generator')
);
}
/** /**
* {@inheritdoc} * {@inheritdoc}
*/ */
@ -47,9 +100,7 @@ abstract class BaseFieldFileFormatterBase extends FormatterBase {
$url = NULL; $url = NULL;
// Add support to link to the entity itself. // Add support to link to the entity itself.
if ($this->getSetting('link_to_file')) { if ($this->getSetting('link_to_file')) {
// @todo Wrap in file_url_transform_relative(). This is currently $url = $this->fileUrlGenerator->generate($items->getEntity()->getFileUri());
// impossible. See below.
$url = file_create_url($items->getEntity()->uri->value);
} }
foreach ($items as $delta => $item) { foreach ($items as $delta => $item) {
@ -59,17 +110,7 @@ abstract class BaseFieldFileFormatterBase extends FormatterBase {
$elements[$delta] = [ $elements[$delta] = [
'#type' => 'link', '#type' => 'link',
'#title' => $view_value, '#title' => $view_value,
'#url' => Url::fromUri($url), '#url' => $url,
// @todo Remove the 'url.site' cache context by using a relative file
// URL (file_url_transform_relative()). This is currently impossible
// because #type => link requires a Url object, and Url objects do not
// support relative URLs: they require fully qualified URLs. Fix in
// https://www.drupal.org/node/2646744.
'#cache' => [
'contexts' => [
'url.site',
],
],
]; ];
} }
else { else {

View File

@ -51,10 +51,7 @@ class FileUriFormatter extends BaseFieldFileFormatterBase {
protected function viewValue(FieldItemInterface $item) { protected function viewValue(FieldItemInterface $item) {
$value = $item->value; $value = $item->value;
if ($this->getSetting('file_download_path')) { if ($this->getSetting('file_download_path')) {
// @todo Wrap in file_url_transform_relative(). This is currently $value = $this->fileUrlGenerator->generateString($value);
// impossible. See BaseFieldFileFormatterBase::viewElements(). Fix in
// https://www.drupal.org/node/2646744.
$value = file_create_url($value);
} }
return $value; return $value;
} }

View File

@ -2,11 +2,13 @@
namespace Drupal\file\Plugin\views\field; namespace Drupal\file\Plugin\views\field;
use Drupal\Core\File\FileUrlGeneratorInterface;
use Drupal\Core\Form\FormStateInterface; use Drupal\Core\Form\FormStateInterface;
use Drupal\views\ResultRow; use Drupal\views\ResultRow;
use Drupal\views\ViewExecutable; use Drupal\views\ViewExecutable;
use Drupal\views\Plugin\views\display\DisplayPluginBase; use Drupal\views\Plugin\views\display\DisplayPluginBase;
use Drupal\views\Plugin\views\field\FieldPluginBase; use Drupal\views\Plugin\views\field\FieldPluginBase;
use Symfony\Component\DependencyInjection\ContainerInterface;
/** /**
* Field handler to provide simple renderer that allows linking to a file. * Field handler to provide simple renderer that allows linking to a file.
@ -17,6 +19,41 @@ use Drupal\views\Plugin\views\field\FieldPluginBase;
*/ */
class File extends FieldPluginBase { class File extends FieldPluginBase {
/**
* The file URL generator.
*
* @var \Drupal\Core\File\FileUrlGeneratorInterface
*/
protected $fileUrlGenerator;
/**
* Constructs a File object.
*
* @param array $configuration
* A configuration array containing information about the plugin instance.
* @param string $plugin_id
* The plugin_id for the plugin instance.
* @param mixed $plugin_definition
* The plugin implementation definition.
* @param \Drupal\Core\File\FileUrlGeneratorInterface $file_url_generator
* The file URL generator.
*/
public function __construct(array $configuration, $plugin_id, $plugin_definition, FileUrlGeneratorInterface $file_url_generator = NULL) {
parent::__construct($configuration, $plugin_id, $plugin_definition);
if (!$file_url_generator) {
@trigger_error('Calling File::__construct() without the $file_url_generator argument is deprecated in drupal:9.3.0 and the $file_url_generator argument will be required in drupal:10.0.0. See https://www.drupal.org/node/2940031', E_USER_DEPRECATED);
$file_url_generator = \Drupal::service('file_url_generator');
}
$this->fileUrlGenerator = $file_url_generator;
}
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
return new static($configuration, $plugin_id, $plugin_definition, $container->get('file_url_generator'));
}
/** /**
* {@inheritdoc} * {@inheritdoc}
*/ */
@ -64,13 +101,7 @@ class File extends FieldPluginBase {
protected function renderLink($data, ResultRow $values) { protected function renderLink($data, ResultRow $values) {
if (!empty($this->options['link_to_file']) && $data !== NULL && $data !== '') { if (!empty($this->options['link_to_file']) && $data !== NULL && $data !== '') {
$this->options['alter']['make_link'] = TRUE; $this->options['alter']['make_link'] = TRUE;
// @todo Wrap in file_url_transform_relative(). This is currently $this->options['alter']['url'] = $this->fileUrlGenerator->generate($this->getValue($values, 'uri'));
// impossible. As a work-around, we could add the 'url.site' cache context
// to ensure different file URLs are generated for different sites in a
// multisite setup, including HTTP and HTTPS versions of the same site.
// But unfortunately it's impossible to bubble a cache context here.
// Fix in https://www.drupal.org/node/2646744.
$this->options['alter']['path'] = file_create_url($this->getValue($values, 'uri'));
} }
return $data; return $data;

View File

@ -16,8 +16,16 @@ class DownloadTest extends FileManagedTestBase {
*/ */
protected $defaultTheme = 'stark'; protected $defaultTheme = 'stark';
/**
* The file URL generator.
*
* @var \Drupal\Core\File\FileUrlGeneratorInterface
*/
protected $fileUrlGenerator;
protected function setUp(): void { protected function setUp(): void {
parent::setUp(); parent::setUp();
$this->fileUrlGenerator = $this->container->get('file_url_generator');
// Clear out any hook calls. // Clear out any hook calls.
file_test_reset(); file_test_reset();
} }
@ -28,7 +36,7 @@ class DownloadTest extends FileManagedTestBase {
public function testPublicFileTransfer() { public function testPublicFileTransfer() {
// Test generating a URL to a created file. // Test generating a URL to a created file.
$file = $this->createFile(); $file = $this->createFile();
$url = file_create_url($file->getFileUri()); $url = $this->fileUrlGenerator->generateAbsoluteString($file->getFileUri());
// URLs can't contain characters outside the ASCII set so $filename has to be // URLs can't contain characters outside the ASCII set so $filename has to be
// encoded. // encoded.
$filename = $GLOBALS['base_url'] . '/' . \Drupal::service('stream_wrapper_manager')->getViaScheme('public')->getDirectoryPath() . '/' . rawurlencode($file->getFilename()); $filename = $GLOBALS['base_url'] . '/' . \Drupal::service('stream_wrapper_manager')->getViaScheme('public')->getDirectoryPath() . '/' . rawurlencode($file->getFilename());
@ -40,7 +48,7 @@ class DownloadTest extends FileManagedTestBase {
// Test generating a URL to a shipped file (i.e. a file that is part of // Test generating a URL to a shipped file (i.e. a file that is part of
// Drupal core, a module or a theme, for example a JavaScript file). // Drupal core, a module or a theme, for example a JavaScript file).
$filepath = 'core/assets/vendor/jquery/jquery.min.js'; $filepath = 'core/assets/vendor/jquery/jquery.min.js';
$url = file_create_url($filepath); $url = $this->fileUrlGenerator->generateAbsoluteString($filepath);
$this->assertEquals($GLOBALS['base_url'] . '/' . $filepath, $url, 'Correctly generated a URL for a shipped file.'); $this->assertEquals($GLOBALS['base_url'] . '/' . $filepath, $url, 'Correctly generated a URL for a shipped file.');
$response = $http_client->head($url); $response = $http_client->head($url);
$this->assertEquals(200, $response->getStatusCode(), 'Confirmed that the generated URL is correct by downloading the shipped file.'); $this->assertEquals(200, $response->getStatusCode(), 'Confirmed that the generated URL is correct by downloading the shipped file.');
@ -69,7 +77,7 @@ class DownloadTest extends FileManagedTestBase {
$file->setPermanent(); $file->setPermanent();
$file->save(); $file->save();
$url = file_create_url($file->getFileUri()); $url = $this->fileUrlGenerator->generateAbsoluteString($file->getFileUri());
// Set file_test access header to allow the download. // Set file_test access header to allow the download.
file_test_reset(); file_test_reset();
@ -94,7 +102,7 @@ class DownloadTest extends FileManagedTestBase {
// Try non-existent file. // Try non-existent file.
file_test_reset(); file_test_reset();
$url = file_create_url('private://' . $this->randomMachineName()); $url = $this->fileUrlGenerator->generateAbsoluteString('private://' . $this->randomMachineName());
$response = $http_client->head($url, ['http_errors' => FALSE]); $response = $http_client->head($url, ['http_errors' => FALSE]);
$this->assertSame(404, $response->getStatusCode(), 'Correctly returned 404 response for a non-existent file.'); $this->assertSame(404, $response->getStatusCode(), 'Correctly returned 404 response for a non-existent file.');
// Assert that hook_file_download is not called. // Assert that hook_file_download is not called.
@ -109,7 +117,7 @@ class DownloadTest extends FileManagedTestBase {
} }
/** /**
* Tests file_create_url(). * Test FileUrlGeneratorInterface::generateString()
*/ */
public function testFileCreateUrl() { public function testFileCreateUrl() {
// "Special" ASCII characters. // "Special" ASCII characters.
@ -139,15 +147,16 @@ class DownloadTest extends FileManagedTestBase {
$this->checkUrl('public', '', $basename, $base_path . '/' . $public_directory_path . '/' . $basename_encoded); $this->checkUrl('public', '', $basename, $base_path . '/' . $public_directory_path . '/' . $basename_encoded);
$this->checkUrl('private', '', $basename, $base_path . '/' . $script_path . 'system/files/' . $basename_encoded); $this->checkUrl('private', '', $basename, $base_path . '/' . $script_path . 'system/files/' . $basename_encoded);
} }
$this->assertEquals('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg==', file_create_url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg=='), t('Generated URL matches expected URL.')); $this->assertEquals('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg==', $this->fileUrlGenerator->generateString('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg==', FALSE), t('Generated URL matches expected URL.'));
} }
/** /**
* Download a file from the URL generated by file_create_url(). * Download a file from the URL generated by generateString().
* *
* Create a file with the specified scheme, directory and filename; check that * Create a file with the specified scheme, directory and filename; check that
* the URL generated by file_create_url() for the specified file equals the * the URL generated by FileUrlGeneratorInterface::generateString() for the
* specified URL; fetch the URL and then compare the contents to the file. * specified file equals the specified URL; fetch the URL and then compare the
* contents to the file.
* *
* @param string $scheme * @param string $scheme
* A scheme, e.g. "public". * A scheme, e.g. "public".
@ -167,7 +176,7 @@ class DownloadTest extends FileManagedTestBase {
\Drupal::service('file_system')->prepareDirectory($directory_uri, FileSystemInterface::CREATE_DIRECTORY); \Drupal::service('file_system')->prepareDirectory($directory_uri, FileSystemInterface::CREATE_DIRECTORY);
$file = $this->createFile($filepath, NULL, $scheme); $file = $this->createFile($filepath, NULL, $scheme);
$url = file_create_url($file->getFileUri()); $url = $this->fileUrlGenerator->generateAbsoluteString($file->getFileUri());
$this->assertEquals($expected_url, $url); $this->assertEquals($expected_url, $url);
if ($scheme == 'private') { if ($scheme == 'private') {

View File

@ -228,7 +228,7 @@ class FileFieldDisplayTest extends FileFieldTestBase {
// Test default formatter. // Test default formatter.
$this->drupalGet('node/' . $nid); $this->drupalGet('node/' . $nid);
$this->assertSession()->elementTextContains('xpath', '//a[@href="' . $node->{$field_name}->entity->createFileUrl(FALSE) . '"]', $description); $this->assertSession()->elementTextContains('xpath', '//a[@href="' . $node->{$field_name}->entity->createFileUrl() . '"]', $description);
// Change formatter to "Table of files". // Change formatter to "Table of files".
$display = \Drupal::entityTypeManager()->getStorage('entity_view_display')->load('node.' . $type_name . '.default'); $display = \Drupal::entityTypeManager()->getStorage('entity_view_display')->load('node.' . $type_name . '.default');
@ -238,7 +238,7 @@ class FileFieldDisplayTest extends FileFieldTestBase {
])->save(); ])->save();
$this->drupalGet('node/' . $nid); $this->drupalGet('node/' . $nid);
$this->assertSession()->elementTextContains('xpath', '//a[@href="' . $node->{$field_name}->entity->createFileUrl(FALSE) . '"]', $description); $this->assertSession()->elementTextContains('xpath', '//a[@href="' . $node->{$field_name}->entity->createFileUrl() . '"]', $description);
} }
} }

View File

@ -65,7 +65,7 @@ class FileFieldRSSContentTest extends FileFieldTestBase {
$this->drupalGet('rss.xml'); $this->drupalGet('rss.xml');
$selector = sprintf( $selector = sprintf(
'//enclosure[@url="%s" and @length="%s" and @type="%s"]', '//enclosure[@url="%s" and @length="%s" and @type="%s"]',
file_create_url($node_file->getFileUri()), $node_file->createFileUrl(FALSE),
$node_file->getSize(), $node_file->getSize(),
$node_file->getMimeType() $node_file->getMimeType()
); );

View File

@ -116,7 +116,7 @@ class FileListingTest extends FileFieldTestBase {
foreach ($nodes as $node) { foreach ($nodes as $node) {
$file = File::load($node->file->target_id); $file = File::load($node->file->target_id);
$this->assertSession()->pageTextContains($file->getFilename()); $this->assertSession()->pageTextContains($file->getFilename());
$this->assertSession()->linkByHrefExists(file_create_url($file->getFileUri())); $this->assertSession()->linkByHrefExists($file->createFileUrl());
$this->assertSession()->linkByHrefExists('admin/content/files/usage/' . $file->id()); $this->assertSession()->linkByHrefExists('admin/content/files/usage/' . $file->id());
} }
$this->assertSession()->elementTextNotContains('css', 'table.views-table', 'Temporary'); $this->assertSession()->elementTextNotContains('css', 'table.views-table', 'Temporary');

View File

@ -57,11 +57,11 @@ class FilePrivateTest extends FileFieldTestBase {
$this->drupalGet('node/' . $node->id()); $this->drupalGet('node/' . $node->id());
$this->assertRaw($node_file->getFilename()); $this->assertRaw($node_file->getFilename());
// Ensure the file can be downloaded. // Ensure the file can be downloaded.
$this->drupalGet(file_create_url($node_file->getFileUri())); $this->drupalGet($node_file->createFileUrl(FALSE));
$this->assertSession()->statusCodeEquals(200); $this->assertSession()->statusCodeEquals(200);
$this->drupalLogOut(); $this->drupalLogOut();
// Ensure the file cannot be downloaded after logging out. // Ensure the file cannot be downloaded after logging out.
$this->drupalGet(file_create_url($node_file->getFileUri())); $this->drupalGet($node_file->createFileUrl(FALSE));
$this->assertSession()->statusCodeEquals(403); $this->assertSession()->statusCodeEquals(403);
// Create a field with no view access. See // Create a field with no view access. See
@ -76,7 +76,7 @@ class FilePrivateTest extends FileFieldTestBase {
$node_file = File::load($node->{$no_access_field_name}->target_id); $node_file = File::load($node->{$no_access_field_name}->target_id);
// Ensure the file cannot be downloaded. // Ensure the file cannot be downloaded.
$file_url = file_create_url($node_file->getFileUri()); $file_url = $node_file->createFileUrl(FALSE);
$this->drupalGet($file_url); $this->drupalGet($file_url);
$this->assertSession()->statusCodeEquals(403); $this->assertSession()->statusCodeEquals(403);
@ -151,7 +151,7 @@ class FilePrivateTest extends FileFieldTestBase {
$this->assertTrue($file->isTemporary(), 'File is temporary.'); $this->assertTrue($file->isTemporary(), 'File is temporary.');
$usage = $this->container->get('file.usage')->listUsage($file); $usage = $this->container->get('file.usage')->listUsage($file);
$this->assertEmpty($usage, 'No file usage found.'); $this->assertEmpty($usage, 'No file usage found.');
$file_url = file_create_url($file->getFileUri()); $file_url = $file->createFileUrl(FALSE);
// Ensure the anonymous uploader has access to the temporary file. // Ensure the anonymous uploader has access to the temporary file.
$this->drupalGet($file_url); $this->drupalGet($file_url);
$this->assertSession()->statusCodeEquals(200); $this->assertSession()->statusCodeEquals(200);
@ -181,7 +181,7 @@ class FilePrivateTest extends FileFieldTestBase {
$this->assertTrue($file->isTemporary(), 'File is temporary.'); $this->assertTrue($file->isTemporary(), 'File is temporary.');
$usage = $this->container->get('file.usage')->listUsage($file); $usage = $this->container->get('file.usage')->listUsage($file);
$this->assertEmpty($usage, 'No file usage found.'); $this->assertEmpty($usage, 'No file usage found.');
$file_url = file_create_url($file->getFileUri()); $file_url = $file->createFileUrl(FALSE);
// Ensure the anonymous uploader has access to the temporary file. // Ensure the anonymous uploader has access to the temporary file.
$this->drupalGet($file_url); $this->drupalGet($file_url);
$this->assertSession()->statusCodeEquals(200); $this->assertSession()->statusCodeEquals(200);
@ -204,7 +204,7 @@ class FilePrivateTest extends FileFieldTestBase {
$this->assertTrue($file->isPermanent(), 'File is permanent.'); $this->assertTrue($file->isPermanent(), 'File is permanent.');
$usage = $this->container->get('file.usage')->listUsage($file); $usage = $this->container->get('file.usage')->listUsage($file);
$this->assertCount(1, $usage, 'File usage found.'); $this->assertCount(1, $usage, 'File usage found.');
$file_url = file_create_url($file->getFileUri()); $file_url = $file->createFileUrl(FALSE);
// Ensure the anonymous uploader has access to the file. // Ensure the anonymous uploader has access to the file.
$this->drupalGet($file_url); $this->drupalGet($file_url);
$this->assertSession()->statusCodeEquals(200); $this->assertSession()->statusCodeEquals(200);
@ -231,7 +231,7 @@ class FilePrivateTest extends FileFieldTestBase {
$this->assertTrue($file->isPermanent(), 'File is permanent.'); $this->assertTrue($file->isPermanent(), 'File is permanent.');
$usage = $this->container->get('file.usage')->listUsage($file); $usage = $this->container->get('file.usage')->listUsage($file);
$this->assertCount(1, $usage, 'File usage found.'); $this->assertCount(1, $usage, 'File usage found.');
$file_url = file_create_url($file->getFileUri()); $file_url = $file->createFileUrl(FALSE);
// Ensure the anonymous uploader cannot access to the file. // Ensure the anonymous uploader cannot access to the file.
$this->drupalGet($file_url); $this->drupalGet($file_url);
$this->assertSession()->statusCodeEquals(403); $this->assertSession()->statusCodeEquals(403);

View File

@ -56,7 +56,7 @@ class FileTokenReplaceTest extends FileFieldTestBase {
$tests['[file:path]'] = Html::escape($file->getFileUri()); $tests['[file:path]'] = Html::escape($file->getFileUri());
$tests['[file:mime]'] = Html::escape($file->getMimeType()); $tests['[file:mime]'] = Html::escape($file->getMimeType());
$tests['[file:size]'] = format_size($file->getSize()); $tests['[file:size]'] = format_size($file->getSize());
$tests['[file:url]'] = Html::escape(file_create_url($file->getFileUri())); $tests['[file:url]'] = Html::escape($file->createFileUrl(FALSE));
$tests['[file:created]'] = $date_formatter->format($file->getCreatedTime(), 'medium', '', NULL, $language_interface->getId()); $tests['[file:created]'] = $date_formatter->format($file->getCreatedTime(), 'medium', '', NULL, $language_interface->getId());
$tests['[file:created:short]'] = $date_formatter->format($file->getCreatedTime(), 'short', '', NULL, $language_interface->getId()); $tests['[file:created:short]'] = $date_formatter->format($file->getCreatedTime(), 'short', '', NULL, $language_interface->getId());
$tests['[file:changed]'] = $date_formatter->format($file->getChangedTime(), 'medium', '', NULL, $language_interface->getId()); $tests['[file:changed]'] = $date_formatter->format($file->getChangedTime(), 'medium', '', NULL, $language_interface->getId());

View File

@ -92,7 +92,7 @@ class FileVideoFormatterTest extends FileMediaFormatterTestBase {
$this->drupalGet($entity->toUrl()); $this->drupalGet($entity->toUrl());
$file_url = file_url_transform_relative(file_create_url($file->getFileUri())); $file_url = \Drupal::service('file_url_generator')->generateString($file->getFileUri());
$assert_session = $this->assertSession(); $assert_session = $this->assertSession();
$assert_session->elementExists('css', "video[autoplay='autoplay'] > source[src='$file_url'][type='video/mp4']"); $assert_session->elementExists('css', "video[autoplay='autoplay'] > source[src='$file_url'][type='video/mp4']");

View File

@ -42,7 +42,7 @@ class FileHalJsonAnonTest extends FileResourceTestBase {
$normalization = $this->applyHalFieldNormalization($default_normalization); $normalization = $this->applyHalFieldNormalization($default_normalization);
$url = file_create_url($this->entity->getFileUri()); $url = $this->entity->createFileUrl(FALSE);
if ($this->config('hal.settings')->get('bc_file_uri_as_url_normalizer')) { if ($this->config('hal.settings')->get('bc_file_uri_as_url_normalizer')) {
$normalization['uri'][0]['value'] = $url; $normalization['uri'][0]['value'] = $url;
} }

View File

@ -48,7 +48,7 @@ abstract class FileUploadHalJsonTestBase extends FileUploadResourceTestBase {
// https://www.drupal.org/project/drupal/issues/2907402 is complete. // https://www.drupal.org/project/drupal/issues/2907402 is complete.
// This link matches what is generated from File::url(), a resource // This link matches what is generated from File::url(), a resource
// URL is currently not available. // URL is currently not available.
'href' => file_create_url($normalization['uri'][0]['value']), 'href' => \Drupal::service('file_url_generator')->generateAbsoluteString($normalization['uri'][0]['value']),
], ],
'type' => [ 'type' => [
'href' => $this->baseUrl . '/rest/type/file/file', 'href' => $this->baseUrl . '/rest/type/file/file',

View File

@ -98,7 +98,7 @@ class PrivateFileOnTranslatedEntityTest extends FileFieldTestBase {
\Drupal::entityTypeManager()->getStorage('node')->resetCache([$default_language_node->id()]); \Drupal::entityTypeManager()->getStorage('node')->resetCache([$default_language_node->id()]);
$node = Node::load($default_language_node->id()); $node = Node::load($default_language_node->id());
$node_file = File::load($node->{$this->fieldName}->target_id); $node_file = File::load($node->{$this->fieldName}->target_id);
$this->drupalGet(file_create_url($node_file->getFileUri())); $this->drupalGet($node_file->createFileUrl(FALSE));
$this->assertSession()->statusCodeEquals(200); $this->assertSession()->statusCodeEquals(200);
// Translate the node into French. // Translate the node into French.
@ -126,7 +126,7 @@ class PrivateFileOnTranslatedEntityTest extends FileFieldTestBase {
// Ensure the file attached to the translated node can be downloaded. // Ensure the file attached to the translated node can be downloaded.
$french_node = $default_language_node->getTranslation('fr'); $french_node = $default_language_node->getTranslation('fr');
$node_file = File::load($french_node->{$this->fieldName}->target_id); $node_file = File::load($french_node->{$this->fieldName}->target_id);
$this->drupalGet(file_create_url($node_file->getFileUri())); $this->drupalGet($node_file->createFileUrl(FALSE));
$this->assertSession()->statusCodeEquals(200); $this->assertSession()->statusCodeEquals(200);
} }

View File

@ -21,7 +21,7 @@ class FileUrlTest extends FileManagedUnitTestBase {
$directory_uri = 'public://' . dirname($filepath); $directory_uri = 'public://' . dirname($filepath);
\Drupal::service('file_system')->prepareDirectory($directory_uri, FileSystemInterface::CREATE_DIRECTORY); \Drupal::service('file_system')->prepareDirectory($directory_uri, FileSystemInterface::CREATE_DIRECTORY);
$file = $this->createFile($filepath, NULL, 'public'); $file = $this->createFile($filepath, NULL, 'public');
$url = file_create_url($file->getFileUri()); $url = $file->createFileUrl(FALSE);
$expected_url = $test_base_url . '/' . basename($filepath); $expected_url = $test_base_url . '/' . basename($filepath);
$this->assertSame($url, $expected_url); $this->assertSame($url, $expected_url);
} }

View File

@ -3,7 +3,6 @@
namespace Drupal\Tests\file\Kernel\Formatter; namespace Drupal\Tests\file\Kernel\Formatter;
use Drupal\Core\Entity\Entity\EntityViewDisplay; use Drupal\Core\Entity\Entity\EntityViewDisplay;
use Drupal\Core\Url;
use Drupal\file\Entity\File; use Drupal\file\Entity\File;
use Drupal\KernelTests\KernelTestBase; use Drupal\KernelTests\KernelTestBase;
@ -26,12 +25,19 @@ class FileEntityFormatterTest extends KernelTestBase {
*/ */
protected $files; protected $files;
/**
* The file URL generator.
*
* @var \Drupal\Core\File\FileUrlGeneratorInterface
*/
protected $fileUrlGenerator;
/** /**
* {@inheritdoc} * {@inheritdoc}
*/ */
protected function setUp(): void { protected function setUp(): void {
parent::setUp(); parent::setUp();
$this->fileUrlGenerator = $this->container->get('file_url_generator');
$this->installEntitySchema('file'); $this->installEntitySchema('file');
$this->files = []; $this->files = [];
@ -80,7 +86,7 @@ class FileEntityFormatterTest extends KernelTestBase {
$build = $entity_display->buildMultiple($this->files)[0]['filename'][0]; $build = $entity_display->buildMultiple($this->files)[0]['filename'][0];
$this->assertEquals('file.png', $build['#title']); $this->assertEquals('file.png', $build['#title']);
$this->assertEquals(Url::fromUri(file_create_url('public://file.png')), $build['#url']); $this->assertEquals($this->fileUrlGenerator->generate('public://file.png'), $build['#url']);
} }
/** /**
@ -98,12 +104,12 @@ class FileEntityFormatterTest extends KernelTestBase {
$entity_display->setComponent('uri', ['type' => 'file_uri', 'settings' => ['file_download_path' => TRUE]]); $entity_display->setComponent('uri', ['type' => 'file_uri', 'settings' => ['file_download_path' => TRUE]]);
$build = $entity_display->buildMultiple($this->files)[0]['uri'][0]; $build = $entity_display->buildMultiple($this->files)[0]['uri'][0];
$this->assertEquals(file_create_url('public://file.png'), $build['#markup']); $this->assertEquals($this->fileUrlGenerator->generateString('public://file.png'), $build['#markup']);
$entity_display->setComponent('uri', ['type' => 'file_uri', 'settings' => ['file_download_path' => TRUE, 'link_to_file' => TRUE]]); $entity_display->setComponent('uri', ['type' => 'file_uri', 'settings' => ['file_download_path' => TRUE, 'link_to_file' => TRUE]]);
$build = $entity_display->buildMultiple($this->files)[0]['uri'][0]; $build = $entity_display->buildMultiple($this->files)[0]['uri'][0];
$this->assertEquals(file_create_url('public://file.png'), $build['#title']); $this->assertEquals($this->fileUrlGenerator->generateString('public://file.png'), $build['#title']);
$this->assertEquals(Url::fromUri(file_create_url('public://file.png')), $build['#url']); $this->assertEquals($this->fileUrlGenerator->generate('public://file.png'), $build['#url']);
} }
/** /**

View File

@ -768,11 +768,14 @@ function _filter_html_image_secure_process($text) {
$html_dom = Html::load($text); $html_dom = Html::load($text);
$images = $html_dom->getElementsByTagName('img'); $images = $html_dom->getElementsByTagName('img');
/** @var \Drupal\Core\File\FileUrlGeneratorInterface $file_url_generator */
$file_url_generator = \Drupal::service('file_url_generator');
foreach ($images as $image) { foreach ($images as $image) {
$src = $image->getAttribute('src'); $src = $image->getAttribute('src');
// Transform absolute image URLs to relative image URLs: prevent problems on // Transform absolute image URLs to relative image URLs: prevent problems on
// multisite set-ups and prevent mixed content errors. // multisite set-ups and prevent mixed content errors.
$image->setAttribute('src', file_url_transform_relative($src)); $image->setAttribute('src', $file_url_generator->transformRelative($src));
// Verify that $src starts with $base_path. // Verify that $src starts with $base_path.
// This also ensures that external images cannot be referenced. // This also ensures that external images cannot be referenced.

View File

@ -17,6 +17,10 @@ use Drupal\Core\Render\Element;
* - style: \Drupal\image\ImageStyleInterface image style being previewed. * - style: \Drupal\image\ImageStyleInterface image style being previewed.
*/ */
function template_preprocess_image_style_preview(&$variables) { function template_preprocess_image_style_preview(&$variables) {
/** @var \Drupal\Core\File\FileUrlGeneratorInterface $file_url_generator */
$file_url_generator = \Drupal::service('file_url_generator');
// Style information. // Style information.
$style = $variables['style']; $style = $variables['style'];
$variables['style_id'] = $style->id(); $variables['style_id'] = $style->id();
@ -34,7 +38,7 @@ function template_preprocess_image_style_preview(&$variables) {
$original_path = \Drupal::config('image.settings')->get('preview_image'); $original_path = \Drupal::config('image.settings')->get('preview_image');
$original_image = $image_factory->get($original_path); $original_image = $image_factory->get($original_path);
$variables['original'] = [ $variables['original'] = [
'url' => file_url_transform_relative(file_create_url($original_path)), 'url' => $file_url_generator->generateString($original_path),
'width' => $original_image->getWidth(), 'width' => $original_image->getWidth(),
'height' => $original_image->getHeight(), 'height' => $original_image->getHeight(),
]; ];
@ -55,7 +59,7 @@ function template_preprocess_image_style_preview(&$variables) {
} }
$preview_image = $image_factory->get($preview_file); $preview_image = $image_factory->get($preview_file);
$variables['derivative'] = [ $variables['derivative'] = [
'url' => file_url_transform_relative(file_create_url($preview_file)), 'url' => $file_url_generator->generateString($preview_file),
'width' => $preview_image->getWidth(), 'width' => $preview_image->getWidth(),
'height' => $preview_image->getHeight(), 'height' => $preview_image->getHeight(),
]; ];

View File

@ -247,7 +247,9 @@ class ImageStyle extends ConfigEntityBase implements ImageStyleInterface, Entity
return Url::fromUri('base:' . $directory_path . '/' . $stream_wrapper_manager::getTarget($uri), ['absolute' => TRUE, 'query' => $token_query])->toString(); return Url::fromUri('base:' . $directory_path . '/' . $stream_wrapper_manager::getTarget($uri), ['absolute' => TRUE, 'query' => $token_query])->toString();
} }
$file_url = file_create_url($uri); /** @var \Drupal\Core\File\FileUrlGeneratorInterface $file_url_generator */
$file_url_generator = \Drupal::service('file_url_generator');
$file_url = $file_url_generator->generateAbsoluteString($uri);
// Append the query string with the token, if necessary. // Append the query string with the token, if necessary.
if ($token_query) { if ($token_query) {
$file_url .= (strpos($file_url, '?') !== FALSE ? '&' : '?') . UrlHelper::buildQuery($token_query); $file_url .= (strpos($file_url, '?') !== FALSE ? '&' : '?') . UrlHelper::buildQuery($token_query);

View File

@ -56,7 +56,7 @@ interface ImageStyleInterface extends ConfigEntityInterface {
* in an <img> tag. Requesting the URL will cause the image to be created. * in an <img> tag. Requesting the URL will cause the image to be created.
* *
* @see \Drupal\image\Controller\ImageStyleDownloadController::deliver() * @see \Drupal\image\Controller\ImageStyleDownloadController::deliver()
* @see file_url_transform_relative() * @see \Drupal\Core\File\FileUrlGeneratorInterface::transformRelative()
*/ */
public function buildUrl($path, $clean_urls = NULL); public function buildUrl($path, $clean_urls = NULL);

View File

@ -5,6 +5,7 @@ namespace Drupal\image\Plugin\Field\FieldFormatter;
use Drupal\Core\Entity\EntityStorageInterface; use Drupal\Core\Entity\EntityStorageInterface;
use Drupal\Core\Field\FieldItemListInterface; use Drupal\Core\Field\FieldItemListInterface;
use Drupal\Core\Field\FieldDefinitionInterface; use Drupal\Core\Field\FieldDefinitionInterface;
use Drupal\Core\File\FileUrlGeneratorInterface;
use Drupal\Core\Link; use Drupal\Core\Link;
use Drupal\Core\Session\AccountInterface; use Drupal\Core\Session\AccountInterface;
use Drupal\Core\Url; use Drupal\Core\Url;
@ -43,6 +44,13 @@ class ImageFormatter extends ImageFormatterBase {
*/ */
protected $imageStyleStorage; protected $imageStyleStorage;
/**
* The file URL generator.
*
* @var \Drupal\Core\File\FileUrlGeneratorInterface
*/
protected $fileUrlGenerator;
/** /**
* Constructs an ImageFormatter object. * Constructs an ImageFormatter object.
* *
@ -64,11 +72,18 @@ class ImageFormatter extends ImageFormatterBase {
* The current user. * The current user.
* @param \Drupal\Core\Entity\EntityStorageInterface $image_style_storage * @param \Drupal\Core\Entity\EntityStorageInterface $image_style_storage
* The image style storage. * The image style storage.
* @param \Drupal\Core\File\FileUrlGeneratorInterface $file_url_generator
* The file URL generator.
*/ */
public function __construct($plugin_id, $plugin_definition, FieldDefinitionInterface $field_definition, array $settings, $label, $view_mode, array $third_party_settings, AccountInterface $current_user, EntityStorageInterface $image_style_storage) { public function __construct($plugin_id, $plugin_definition, FieldDefinitionInterface $field_definition, array $settings, $label, $view_mode, array $third_party_settings, AccountInterface $current_user, EntityStorageInterface $image_style_storage, FileUrlGeneratorInterface $file_url_generator = NULL) {
parent::__construct($plugin_id, $plugin_definition, $field_definition, $settings, $label, $view_mode, $third_party_settings); parent::__construct($plugin_id, $plugin_definition, $field_definition, $settings, $label, $view_mode, $third_party_settings);
$this->currentUser = $current_user; $this->currentUser = $current_user;
$this->imageStyleStorage = $image_style_storage; $this->imageStyleStorage = $image_style_storage;
if (!$file_url_generator) {
@trigger_error('Calling ImageFormatter::__construct() without the $file_url_generator argument is deprecated in drupal:9.3.0 and the $file_url_generator argument will be required in drupal:10.0.0. See https://www.drupal.org/node/2940031', E_USER_DEPRECATED);
$file_url_generator = \Drupal::service('file_url_generator');
}
$this->fileUrlGenerator = $file_url_generator;
} }
/** /**
@ -84,7 +99,8 @@ class ImageFormatter extends ImageFormatterBase {
$configuration['view_mode'], $configuration['view_mode'],
$configuration['third_party_settings'], $configuration['third_party_settings'],
$container->get('current_user'), $container->get('current_user'),
$container->get('entity_type.manager')->getStorage('image_style') $container->get('entity_type.manager')->getStorage('image_style'),
$container->get('file_url_generator')
); );
} }
@ -199,16 +215,9 @@ class ImageFormatter extends ImageFormatterBase {
} }
foreach ($files as $delta => $file) { foreach ($files as $delta => $file) {
$cache_contexts = [];
if (isset($link_file)) { if (isset($link_file)) {
$image_uri = $file->getFileUri(); $image_uri = $file->getFileUri();
// @todo Wrap in file_url_transform_relative(). This is currently $url = $this->fileUrlGenerator->generate($image_uri);
// impossible. As a work-around, we currently add the 'url.site' cache
// context to ensure different file URLs are generated for different
// sites in a multisite setup, including HTTP and HTTPS versions of the
// same site. Fix in https://www.drupal.org/node/2646744.
$url = Url::fromUri(file_create_url($image_uri));
$cache_contexts[] = 'url.site';
} }
$cache_tags = Cache::mergeTags($base_cache_tags, $file->getCacheTags()); $cache_tags = Cache::mergeTags($base_cache_tags, $file->getCacheTags());
@ -226,7 +235,6 @@ class ImageFormatter extends ImageFormatterBase {
'#url' => $url, '#url' => $url,
'#cache' => [ '#cache' => [
'tags' => $cache_tags, 'tags' => $cache_tags,
'contexts' => $cache_contexts,
], ],
]; ];
} }

View File

@ -61,11 +61,12 @@ class ImageUrlFormatter extends ImageFormatter {
/** @var \Drupal\image\ImageStyleInterface $image_style */ /** @var \Drupal\image\ImageStyleInterface $image_style */
$image_style = $this->imageStyleStorage->load($this->getSetting('image_style')); $image_style = $this->imageStyleStorage->load($this->getSetting('image_style'));
/** @var \Drupal\Core\File\FileUrlGeneratorInterface $file_url_generator */
$file_url_generator = \Drupal::service('file_url_generator');
/** @var \Drupal\file\FileInterface[] $images */ /** @var \Drupal\file\FileInterface[] $images */
foreach ($images as $delta => $image) { foreach ($images as $delta => $image) {
$image_uri = $image->getFileUri(); $image_uri = $image->getFileUri();
$url = $image_style ? $image_style->buildUrl($image_uri) : file_create_url($image_uri); $url = $image_style ? $file_url_generator->transformRelative($image_style->buildUrl($image_uri)) : $file_url_generator->generateString($image_uri);
$url = file_url_transform_relative($url);
// Add cacheability metadata from the image and image style. // Add cacheability metadata from the image and image style.
$cacheability = CacheableMetadata::createFromObject($image); $cacheability = CacheableMetadata::createFromObject($image);

View File

@ -2,13 +2,13 @@
namespace Drupal\Tests\image\Functional; namespace Drupal\Tests\image\Functional;
use Drupal\Core\Url;
use Drupal\Component\Render\FormattableMarkup; use Drupal\Component\Render\FormattableMarkup;
use Drupal\Core\Entity\Entity\EntityViewDisplay; use Drupal\Core\Entity\Entity\EntityViewDisplay;
use Drupal\Core\Url;
use Drupal\file\Entity\File;
use Drupal\image\Entity\ImageStyle; use Drupal\image\Entity\ImageStyle;
use Drupal\image\ImageStyleInterface; use Drupal\image\ImageStyleInterface;
use Drupal\node\Entity\Node; use Drupal\node\Entity\Node;
use Drupal\file\Entity\File;
use Drupal\Tests\TestFileCreationTrait; use Drupal\Tests\TestFileCreationTrait;
/** /**
@ -343,8 +343,11 @@ class ImageAdminStylesTest extends ImageFieldTestBase {
$original_uri = File::load($fid)->getFileUri(); $original_uri = File::load($fid)->getFileUri();
// Test that image is displayed using newly created style. // Test that image is displayed using newly created style.
/** @var \Drupal\Core\File\FileUrlGeneratorInterface $file_url_generator */
$file_url_generator = \Drupal::service('file_url_generator');
$this->drupalGet('node/' . $nid); $this->drupalGet('node/' . $nid);
$this->assertRaw(file_url_transform_relative($style->buildUrl($original_uri))); $this->assertSession()->responseContains($file_url_generator->transformRelative($style->buildUrl($original_uri)));
// Rename the style and make sure the image field is updated. // Rename the style and make sure the image field is updated.
$new_style_name = strtolower($this->randomMachineName(10)); $new_style_name = strtolower($this->randomMachineName(10));
@ -360,7 +363,7 @@ class ImageAdminStylesTest extends ImageFieldTestBase {
// Reload the image style using the new name. // Reload the image style using the new name.
$style = ImageStyle::load($new_style_name); $style = ImageStyle::load($new_style_name);
$this->assertRaw(file_url_transform_relative($style->buildUrl($original_uri))); $this->assertSession()->responseContains($file_url_generator->transformRelative($style->buildUrl($original_uri)));
// Delete the style and choose a replacement style. // Delete the style and choose a replacement style.
$edit = [ $edit = [
@ -373,7 +376,7 @@ class ImageAdminStylesTest extends ImageFieldTestBase {
$replacement_style = ImageStyle::load('thumbnail'); $replacement_style = ImageStyle::load('thumbnail');
$this->drupalGet('node/' . $nid); $this->drupalGet('node/' . $nid);
$this->assertRaw(file_url_transform_relative($replacement_style->buildUrl($original_uri))); $this->assertSession()->responseContains($file_url_generator->transformRelative($replacement_style->buildUrl($original_uri)));
} }
/** /**
@ -493,7 +496,7 @@ class ImageAdminStylesTest extends ImageFieldTestBase {
// Test that image is displayed using newly created style. // Test that image is displayed using newly created style.
$this->drupalGet('node/' . $nid); $this->drupalGet('node/' . $nid);
$this->assertRaw(file_url_transform_relative($style->buildUrl($original_uri))); $this->assertRaw(\Drupal::service('file_url_generator')->transformRelative($style->buildUrl($original_uri)));
// Copy config to sync, and delete the image style. // Copy config to sync, and delete the image style.
$sync = $this->container->get('config.storage.sync'); $sync = $this->container->get('config.storage.sync');

View File

@ -50,7 +50,9 @@ class ImageDimensionsTest extends BrowserTestBase {
$style = ImageStyle::create(['name' => 'test', 'label' => 'Test']); $style = ImageStyle::create(['name' => 'test', 'label' => 'Test']);
$style->save(); $style->save();
$generated_uri = 'public://styles/test/public/' . $file_system->basename($original_uri); $generated_uri = 'public://styles/test/public/' . $file_system->basename($original_uri);
$url = file_url_transform_relative($style->buildUrl($original_uri)); /** @var \Drupal\Core\File\FileUrlGeneratorInterface $file_url_generator */
$file_url_generator = \Drupal::service('file_url_generator');
$url = $file_url_generator->transformRelative($style->buildUrl($original_uri));
$variables = [ $variables = [
'#theme' => 'image_style', '#theme' => 'image_style',
@ -266,7 +268,7 @@ class ImageDimensionsTest extends BrowserTestBase {
]; ];
// PNG original image. Should be resized to 100x100. // PNG original image. Should be resized to 100x100.
$generated_uri = 'public://styles/test_uri/public/' . $file_system->basename($original_uri); $generated_uri = 'public://styles/test_uri/public/' . $file_system->basename($original_uri);
$url = file_url_transform_relative($style->buildUrl($original_uri)); $url = \Drupal::service('file_url_generator')->transformRelative($style->buildUrl($original_uri));
$this->assertEquals('<img src="' . $url . '" width="100" height="100" alt="" loading="lazy" class="image-style-test-uri" />', $this->getImageTag($variables)); $this->assertEquals('<img src="' . $url . '" width="100" height="100" alt="" loading="lazy" class="image-style-test-uri" />', $this->getImageTag($variables));
$this->assertFileDoesNotExist($generated_uri); $this->assertFileDoesNotExist($generated_uri);
$this->drupalGet($this->getAbsoluteUrl($url)); $this->drupalGet($this->getAbsoluteUrl($url));
@ -279,7 +281,7 @@ class ImageDimensionsTest extends BrowserTestBase {
$file = $files[1]; $file = $files[1];
$original_uri = $file_system->copy($file->uri, 'public://', FileSystemInterface::EXISTS_RENAME); $original_uri = $file_system->copy($file->uri, 'public://', FileSystemInterface::EXISTS_RENAME);
$generated_uri = 'public://styles/test_uri/public/' . $file_system->basename($original_uri); $generated_uri = 'public://styles/test_uri/public/' . $file_system->basename($original_uri);
$url = file_url_transform_relative($style->buildUrl($original_uri)); $url = $file_url_generator->transformRelative($style->buildUrl($original_uri));
$variables['#uri'] = $original_uri; $variables['#uri'] = $original_uri;
$this->assertEquals('<img src="' . $url . '" width="50" height="50" alt="" loading="lazy" class="image-style-test-uri" />', $this->getImageTag($variables)); $this->assertEquals('<img src="' . $url . '" width="50" height="50" alt="" loading="lazy" class="image-style-test-uri" />', $this->getImageTag($variables));
$this->assertFileDoesNotExist($generated_uri); $this->assertFileDoesNotExist($generated_uri);

View File

@ -49,7 +49,7 @@ class ConvertTest extends BrowserTestBase {
// Execute the image style on the test image via a GET request. // Execute the image style on the test image via a GET request.
$derivative_uri = 'public://styles/image_effect_test/public/image-test-do.png.jpeg'; $derivative_uri = 'public://styles/image_effect_test/public/image-test-do.png.jpeg';
$this->assertFileDoesNotExist($derivative_uri); $this->assertFileDoesNotExist($derivative_uri);
$url = file_url_transform_relative($image_style->buildUrl($test_uri)); $url = \Drupal::service('file_url_generator')->transformRelative($image_style->buildUrl($test_uri));
$this->drupalGet($this->getAbsoluteUrl($url)); $this->drupalGet($this->getAbsoluteUrl($url));
$this->assertSession()->statusCodeEquals(200); $this->assertSession()->statusCodeEquals(200);
$this->assertFileExists($derivative_uri); $this->assertFileExists($derivative_uri);

View File

@ -106,6 +106,7 @@ class ImageFieldDisplayTest extends ImageFieldTestBase {
$node = $node_storage->load($nid); $node = $node_storage->load($nid);
// Test that the default formatter is being used. // Test that the default formatter is being used.
/** @var \Drupal\file\FileInterface $file */
$file = $node->{$field_name}->entity; $file = $node->{$field_name}->entity;
$image_uri = $file->getFileUri(); $image_uri = $file->getFileUri();
$image = [ $image = [
@ -135,7 +136,7 @@ class ImageFieldDisplayTest extends ImageFieldTestBase {
'#height' => 20, '#height' => 20,
'#alt' => $alt, '#alt' => $alt,
]; ];
$default_output = '<a href="' . file_create_url($image_uri) . '">' . $renderer->renderRoot($image) . '</a>'; $default_output = '<a href="' . $file->createFileUrl() . '">' . $renderer->renderRoot($image) . '</a>';
$this->drupalGet('node/' . $nid); $this->drupalGet('node/' . $nid);
$this->assertSession()->responseHeaderContains('X-Drupal-Cache-Tags', $file->getCacheTags()[0]); $this->assertSession()->responseHeaderContains('X-Drupal-Cache-Tags', $file->getCacheTags()[0]);
// @todo Remove in https://www.drupal.org/node/2646744. // @todo Remove in https://www.drupal.org/node/2646744.
@ -144,7 +145,7 @@ class ImageFieldDisplayTest extends ImageFieldTestBase {
$this->assertSession()->responseHeaderNotContains('X-Drupal-Cache-Tags', 'image_style:'); $this->assertSession()->responseHeaderNotContains('X-Drupal-Cache-Tags', 'image_style:');
$this->assertRaw($default_output); $this->assertRaw($default_output);
// Verify that the image can be downloaded. // Verify that the image can be downloaded.
$this->assertEquals(file_get_contents($test_image->uri), $this->drupalGet(file_create_url($image_uri)), 'File was downloaded successfully.'); $this->assertEquals(file_get_contents($test_image->uri), $this->drupalGet($file->createFileUrl(FALSE)), 'File was downloaded successfully.');
if ($scheme == 'private') { if ($scheme == 'private') {
// Only verify HTTP headers when using private scheme and the headers are // Only verify HTTP headers when using private scheme and the headers are
// sent by Drupal. // sent by Drupal.
@ -153,7 +154,7 @@ class ImageFieldDisplayTest extends ImageFieldTestBase {
// Log out and ensure the file cannot be accessed. // Log out and ensure the file cannot be accessed.
$this->drupalLogout(); $this->drupalLogout();
$this->drupalGet(file_create_url($image_uri)); $this->drupalGet($file->createFileUrl(FALSE));
$this->assertSession()->statusCodeEquals(403); $this->assertSession()->statusCodeEquals(403);
// Log in again. // Log in again.
@ -178,7 +179,7 @@ class ImageFieldDisplayTest extends ImageFieldTestBase {
'//a[@href=:path]/img[@src=:url and @alt=:alt and @width=:width and @height=:height]', '//a[@href=:path]/img[@src=:url and @alt=:alt and @width=:width and @height=:height]',
[ [
':path' => $node->toUrl()->toString(), ':path' => $node->toUrl()->toString(),
':url' => file_url_transform_relative(file_create_url($image['#uri'])), ':url' => $file->createFileUrl(),
':width' => $image['#width'], ':width' => $image['#width'],
':height' => $image['#height'], ':height' => $image['#height'],
':alt' => $alt, ':alt' => $alt,
@ -221,12 +222,12 @@ class ImageFieldDisplayTest extends ImageFieldTestBase {
'type' => 'image_url', 'type' => 'image_url',
'settings' => ['image_style' => ''], 'settings' => ['image_style' => ''],
]; ];
$expected_url = file_url_transform_relative(file_create_url($image_uri)); $expected_url = $file->createFileUrl();
$this->assertEquals($expected_url, $node->{$field_name}->view($display_options)[0]['#markup']); $this->assertEquals($expected_url, $node->{$field_name}->view($display_options)[0]['#markup']);
// Test the image URL formatter with an image style. // Test the image URL formatter with an image style.
$display_options['settings']['image_style'] = 'thumbnail'; $display_options['settings']['image_style'] = 'thumbnail';
$expected_url = file_url_transform_relative(ImageStyle::load('thumbnail')->buildUrl($image_uri)); $expected_url = \Drupal::service('file_url_generator')->transformRelative(ImageStyle::load('thumbnail')->buildUrl($image_uri));
$this->assertEquals($expected_url, $node->{$field_name}->view($display_options)[0]['#markup']); $this->assertEquals($expected_url, $node->{$field_name}->view($display_options)[0]['#markup']);
} }
@ -285,7 +286,8 @@ class ImageFieldDisplayTest extends ImageFieldTestBase {
$node = $node_storage->load($nid); $node = $node_storage->load($nid);
$file = $node->{$field_name}->entity; $file = $node->{$field_name}->entity;
$url = file_url_transform_relative(ImageStyle::load('medium')->buildUrl($file->getFileUri())); $file_url_generator = \Drupal::service('file_url_generator');
$url = $file_url_generator->transformRelative(ImageStyle::load('medium')->buildUrl($file->getFileUri()));
$this->assertSession()->elementExists('css', 'img[width=40][height=20][class=image-style-medium][src="' . $url . '"]'); $this->assertSession()->elementExists('css', 'img[width=40][height=20][class=image-style-medium][src="' . $url . '"]');
// Add alt/title fields to the image and verify that they are displayed. // Add alt/title fields to the image and verify that they are displayed.

View File

@ -322,7 +322,7 @@ class ImageStylesPathAndUrlTest extends BrowserTestBase {
// Check that requesting a nonexistent image does not create any new // Check that requesting a nonexistent image does not create any new
// directories in the file system. // directories in the file system.
$directory = $scheme . '://styles/' . $this->style->id() . '/' . $scheme . '/' . $this->randomMachineName(); $directory = $scheme . '://styles/' . $this->style->id() . '/' . $scheme . '/' . $this->randomMachineName();
$this->drupalGet(file_create_url($directory . '/' . $this->randomString())); $this->drupalGet(\Drupal::service('file_url_generator')->generateAbsoluteString($directory . '/' . $this->randomString()));
$this->assertDirectoryDoesNotExist($directory); $this->assertDirectoryDoesNotExist($directory);
} }

View File

@ -93,7 +93,7 @@ class ImageThemeFunctionTest extends KernelTestBase {
// Create a style. // Create a style.
$style = ImageStyle::create(['name' => 'test', 'label' => 'Test']); $style = ImageStyle::create(['name' => 'test', 'label' => 'Test']);
$style->save(); $style->save();
$url = file_url_transform_relative($style->buildUrl($original_uri)); $url = \Drupal::service('file_url_generator')->transformRelative($style->buildUrl($original_uri));
// Create a test entity with the image field set. // Create a test entity with the image field set.
$entity = EntityTest::create(); $entity = EntityTest::create();
@ -155,7 +155,7 @@ class ImageThemeFunctionTest extends KernelTestBase {
// Create a style. // Create a style.
$style = ImageStyle::create(['name' => 'image_test', 'label' => 'Test']); $style = ImageStyle::create(['name' => 'image_test', 'label' => 'Test']);
$style->save(); $style->save();
$url = file_url_transform_relative($style->buildUrl($original_uri)); $url = \Drupal::service('file_url_generator')->transformRelative($style->buildUrl($original_uri));
// Create the base element that we'll use in the tests below. // Create the base element that we'll use in the tests below.
$base_element = [ $base_element = [

View File

@ -286,7 +286,7 @@ abstract class JsonApiFunctionalTestBase extends BrowserTestBase {
} }
if ($article_has_image) { if ($article_has_image) {
$file = File::create([ $file = File::create([
'uri' => 'vfs://' . $random->name() . '.png', 'uri' => 'public://' . $random->name() . '.png',
]); ]);
$file->setPermanent(); $file->setPermanent();
$file->save(); $file->save();

View File

@ -178,7 +178,7 @@ class MediaHalJsonAnonTest extends MediaResourceTestBase {
// https://www.drupal.org/project/drupal/issues/2907402 is complete. // https://www.drupal.org/project/drupal/issues/2907402 is complete.
// This link matches what is generated from File::url(), a resource // This link matches what is generated from File::url(), a resource
// URL is currently not available. // URL is currently not available.
'href' => file_create_url($normalization['uri'][0]['value']), 'href' => \Drupal::service('file_url_generator')->generateAbsoluteString($normalization['uri'][0]['value']),
], ],
'type' => [ 'type' => [
'href' => $this->baseUrl . '/rest/type/file/file', 'href' => $this->baseUrl . '/rest/type/file/file',

View File

@ -869,7 +869,9 @@ class CKEditorIntegrationTest extends WebDriverTestBase {
if ($drupalimage_is_enabled) { if ($drupalimage_is_enabled) {
// Add an image with a link wrapped around it. // Add an image with a link wrapped around it.
$uri = $this->media->field_media_image->entity->getFileUri(); $uri = $this->media->field_media_image->entity->getFileUri();
$src = file_url_transform_relative(file_create_url($uri)); /** @var \Drupal\Core\File\FileUrlGeneratorInterface $file_url_generator */
$file_url_generator = \Drupal::service('file_url_generator');
$src = $file_url_generator->generateString($uri);
$this->host->body->value .= '<a href="http://www.drupal.org/association"><img alt="drupalimage test image" data-entity-type="" data-entity-uuid="" src="' . $src . '" /></a></p>'; $this->host->body->value .= '<a href="http://www.drupal.org/association"><img alt="drupalimage test image" data-entity-type="" data-entity-uuid="" src="' . $src . '" /></a></p>';
} }
$this->host->save(); $this->host->save();

View File

@ -110,7 +110,9 @@ class MediaDisplayTest extends MediaJavascriptTestBase {
// Assert that the image src uses the large image style, the label is // Assert that the image src uses the large image style, the label is
// visually hidden, and there is no link to the image file. // visually hidden, and there is no link to the image file.
$media_image = $assert_session->elementExists('css', '.media--type-image img'); $media_image = $assert_session->elementExists('css', '.media--type-image img');
$expected_image_src = file_url_transform_relative(file_create_url(\Drupal::token()->replace('public://styles/large/public/[date:custom:Y]-[date:custom:m]/example_1.jpeg'))); /** @var \Drupal\Core\File\FileUrlGeneratorInterface $file_url_generator */
$file_url_generator = \Drupal::service('file_url_generator');
$expected_image_src = $file_url_generator->generateString(\Drupal::token()->replace('public://styles/large/public/[date:custom:Y]-[date:custom:m]/example_1.jpeg'));
$this->assertStringContainsString($expected_image_src, $media_image->getAttribute('src')); $this->assertStringContainsString($expected_image_src, $media_image->getAttribute('src'));
$field = $assert_session->elementExists('css', '.field--name-field-media-image'); $field = $assert_session->elementExists('css', '.field--name-field-media-image');
$assert_session->elementExists('css', '.field__label.visually-hidden', $field); $assert_session->elementExists('css', '.field__label.visually-hidden', $field);

View File

@ -72,7 +72,9 @@ class MediaSourceImageTest extends MediaSourceTestBase {
// src attribute uses the large image style, the label is visually hidden, // src attribute uses the large image style, the label is visually hidden,
// and there is no link to the image file. // and there is no link to the image file.
$image_element = $assert_session->elementExists('css', '.field--name-field-media-image img'); $image_element = $assert_session->elementExists('css', '.field--name-field-media-image img');
$expected_image_src = file_url_transform_relative(file_create_url(\Drupal::token()->replace('public://styles/large/public/[date:custom:Y]-[date:custom:m]/example_1.jpeg'))); /** @var \Drupal\Core\File\FileUrlGeneratorInterface $file_url_generator */
$file_url_generator = \Drupal::service('file_url_generator');
$expected_image_src = $file_url_generator->generateString(\Drupal::token()->replace('public://styles/large/public/[date:custom:Y]-[date:custom:m]/example_1.jpeg'));
$this->assertStringContainsString($expected_image_src, $image_element->getAttribute('src')); $this->assertStringContainsString($expected_image_src, $image_element->getAttribute('src'));
$field = $assert_session->elementExists('css', '.field--name-field-media-image'); $field = $assert_session->elementExists('css', '.field--name-field-media-image');
$assert_session->elementExists('css', '.field__label.visually-hidden', $field); $assert_session->elementExists('css', '.field__label.visually-hidden', $field);

View File

@ -153,7 +153,9 @@ class MediaStandardProfileTest extends MediaJavascriptTestBase {
// Assert the audio file is present inside the media element and that its // Assert the audio file is present inside the media element and that its
// src attribute matches the audio file. // src attribute matches the audio file.
$audio_element = $assert_session->elementExists('css', 'article.media--type-audio .field--name-field-media-audio-file audio > source'); $audio_element = $assert_session->elementExists('css', 'article.media--type-audio .field--name-field-media-audio-file audio > source');
$expected_audio_src = file_url_transform_relative(file_create_url(\Drupal::token()->replace('public://[date:custom:Y]-[date:custom:m]/' . $test_filename))); /** @var \Drupal\Core\File\FileUrlGeneratorInterface $file_url_generator */
$file_url_generator = \Drupal::service('file_url_generator');
$expected_audio_src = $file_url_generator->generateString(\Drupal::token()->replace('public://[date:custom:Y]-[date:custom:m]/' . $test_filename));
$this->assertSame($expected_audio_src, $audio_element->getAttribute('src')); $this->assertSame($expected_audio_src, $audio_element->getAttribute('src'));
// Assert the media name is updated through the field mapping when changing // Assert the media name is updated through the field mapping when changing
@ -180,7 +182,7 @@ class MediaStandardProfileTest extends MediaJavascriptTestBase {
// Assert the audio file is present inside the media element and that its // Assert the audio file is present inside the media element and that its
// src attribute matches the updated audio file. // src attribute matches the updated audio file.
$audio_element = $assert_session->elementExists('css', 'article.media--type-audio .field--name-field-media-audio-file audio > source'); $audio_element = $assert_session->elementExists('css', 'article.media--type-audio .field--name-field-media-audio-file audio > source');
$expected_audio_src = file_url_transform_relative(file_create_url(\Drupal::token()->replace('public://[date:custom:Y]-[date:custom:m]/' . $test_filename_updated))); $expected_audio_src = $file_url_generator->generateString(\Drupal::token()->replace('public://[date:custom:Y]-[date:custom:m]/' . $test_filename_updated));
$this->assertSame($expected_audio_src, $audio_element->getAttribute('src')); $this->assertSame($expected_audio_src, $audio_element->getAttribute('src'));
} }
@ -240,7 +242,10 @@ class MediaStandardProfileTest extends MediaJavascriptTestBase {
// src attribute uses the large image style, the label is visually hidden, // src attribute uses the large image style, the label is visually hidden,
// and there is no link to the image file. // and there is no link to the image file.
$image_element = $assert_session->elementExists('css', 'article.media--type-image img'); $image_element = $assert_session->elementExists('css', 'article.media--type-image img');
$expected_image_src = file_url_transform_relative(file_create_url(\Drupal::token()->replace('public://styles/large/public/[date:custom:Y]-[date:custom:m]/' . $image_media_name))); /** @var \Drupal\Core\File\FileUrlGeneratorInterface $file_url_generator */
$file_url_generator = \Drupal::service('file_url_generator');
$expected_image_src = $file_url_generator->generateString(\Drupal::token()->replace('public://styles/large/public/[date:custom:Y]-[date:custom:m]/' . $image_media_name));
$this->assertStringContainsString($expected_image_src, $image_element->getAttribute('src')); $this->assertStringContainsString($expected_image_src, $image_element->getAttribute('src'));
$assert_session->elementExists('css', '.field--name-field-media-image .field__label.visually-hidden'); $assert_session->elementExists('css', '.field--name-field-media-image .field__label.visually-hidden');
$assert_session->elementNotExists('css', '.field--name-field-media-image a'); $assert_session->elementNotExists('css', '.field--name-field-media-image a');
@ -272,7 +277,7 @@ class MediaStandardProfileTest extends MediaJavascriptTestBase {
// src attribute uses the large image style, the label is visually hidden, // src attribute uses the large image style, the label is visually hidden,
// and there is no link to the image file. // and there is no link to the image file.
$image_element = $assert_session->elementExists('css', 'article.media--type-image img'); $image_element = $assert_session->elementExists('css', 'article.media--type-image img');
$expected_image_src = file_url_transform_relative(file_create_url(\Drupal::token()->replace('public://styles/large/public/[date:custom:Y]-[date:custom:m]/' . $image_media_name_updated))); $expected_image_src = $file_url_generator->generateString(\Drupal::token()->replace('public://styles/large/public/[date:custom:Y]-[date:custom:m]/' . $image_media_name_updated));
$this->assertStringContainsString($expected_image_src, $image_element->getAttribute('src')); $this->assertStringContainsString($expected_image_src, $image_element->getAttribute('src'));
$assert_session->elementExists('css', '.field--name-field-media-image .field__label.visually-hidden'); $assert_session->elementExists('css', '.field--name-field-media-image .field__label.visually-hidden');
$assert_session->elementNotExists('css', '.field--name-field-media-image a'); $assert_session->elementNotExists('css', '.field--name-field-media-image a');
@ -524,7 +529,9 @@ class MediaStandardProfileTest extends MediaJavascriptTestBase {
// Assert the video element is present inside the media element and that its // Assert the video element is present inside the media element and that its
// src attribute matches the video file. // src attribute matches the video file.
$video_element = $assert_session->elementExists('css', 'article.media--type-video .field--name-field-media-video-file video > source'); $video_element = $assert_session->elementExists('css', 'article.media--type-video .field--name-field-media-video-file video > source');
$expected_video_src = file_url_transform_relative(file_create_url(\Drupal::token()->replace('public://[date:custom:Y]-[date:custom:m]/' . $test_filename))); /** @var \Drupal\Core\File\FileUrlGeneratorInterface $file_url_generator */
$file_url_generator = \Drupal::service('file_url_generator');
$expected_video_src = $file_url_generator->generateString(\Drupal::token()->replace('public://[date:custom:Y]-[date:custom:m]/' . $test_filename));
$this->assertSame($expected_video_src, $video_element->getAttribute('src')); $this->assertSame($expected_video_src, $video_element->getAttribute('src'));
// Assert the media name is updated through the field mapping when changing // Assert the media name is updated through the field mapping when changing
@ -551,7 +558,7 @@ class MediaStandardProfileTest extends MediaJavascriptTestBase {
// Assert the video element is present inside the media element and that its // Assert the video element is present inside the media element and that its
// src attribute matches the updated video file. // src attribute matches the updated video file.
$video_element = $assert_session->elementExists('css', 'article.media--type-video .field--name-field-media-video-file video > source'); $video_element = $assert_session->elementExists('css', 'article.media--type-video .field--name-field-media-video-file video > source');
$expected_video_src = file_url_transform_relative(file_create_url(\Drupal::token()->replace('public://[date:custom:Y]-[date:custom:m]/' . $test_filename_updated))); $expected_video_src = $file_url_generator->generateString(\Drupal::token()->replace('public://[date:custom:Y]-[date:custom:m]/' . $test_filename_updated));
$this->assertSame($expected_video_src, $video_element->getAttribute('src')); $this->assertSame($expected_video_src, $video_element->getAttribute('src'));
} }

View File

@ -104,7 +104,7 @@ class MediaEmbedFilterTest extends MediaEmbedFilterTestBase {
'media:1', 'media:1',
'media_view', 'media_view',
]) ])
->setCacheContexts(['url.site', 'user.permissions']) ->setCacheContexts(['user.permissions'])
->setCacheMaxAge(Cache::PERMANENT), ->setCacheMaxAge(Cache::PERMANENT),
], ],
'custom attributes are retained' => [ 'custom attributes are retained' => [

View File

@ -87,20 +87,22 @@ class NodeRSSContentTest extends NodeTestBase {
'type' => 'article', 'type' => 'article',
'promote' => 1, 'promote' => 1,
]; ];
/** @var \Drupal\Core\File\FileUrlGeneratorInterface $file_url_generator */
$file_url_generator = \Drupal::service('file_url_generator');
$this->drupalCreateNode($defaults + [ $this->drupalCreateNode($defaults + [
'body' => [ 'body' => [
'value' => '<p><a href="' . file_url_transform_relative(file_create_url('public://root-relative')) . '">Root-relative URL</a></p>', 'value' => '<p><a href="' . $file_url_generator->generateString('public://root-relative') . '">Root-relative URL</a></p>',
'format' => 'full_html', 'format' => 'full_html',
], ],
]); ]);
$protocol_relative_url = substr(file_create_url('public://protocol-relative'), strlen(\Drupal::request()->getScheme() . ':')); $protocol_relative_url = substr($file_url_generator->generateAbsoluteString('public://protocol-relative'), strlen(\Drupal::request()->getScheme() . ':'));
$this->drupalCreateNode($defaults + [ $this->drupalCreateNode($defaults + [
'body' => [ 'body' => [
'value' => '<p><a href="' . $protocol_relative_url . '">Protocol-relative URL</a></p>', 'value' => '<p><a href="' . $protocol_relative_url . '">Protocol-relative URL</a></p>',
'format' => 'full_html', 'format' => 'full_html',
], ],
]); ]);
$absolute_url = file_create_url('public://absolute'); $absolute_url = $file_url_generator->generateAbsoluteString('public://absolute');
$this->drupalCreateNode($defaults + [ $this->drupalCreateNode($defaults + [
'body' => [ 'body' => [
'value' => '<p><a href="' . $absolute_url . '">Absolute URL</a></p>', 'value' => '<p><a href="' . $absolute_url . '">Absolute URL</a></p>',
@ -110,7 +112,7 @@ class NodeRSSContentTest extends NodeTestBase {
$this->drupalGet('rss.xml'); $this->drupalGet('rss.xml');
// Verify that root-relative URL is transformed to absolute. // Verify that root-relative URL is transformed to absolute.
$this->assertRaw(file_create_url('public://root-relative')); $this->assertSession()->responseContains($file_url_generator->generateAbsoluteString('public://root-relative'));
// Verify that protocol-relative URL is left untouched. // Verify that protocol-relative URL is left untouched.
$this->assertRaw($protocol_relative_url); $this->assertRaw($protocol_relative_url);
// Verify that absolute URL is left untouched. // Verify that absolute URL is left untouched.

View File

@ -101,7 +101,7 @@ class FileFieldAttributesTest extends FileFieldTestBase {
$html = \Drupal::service('renderer')->renderRoot($node_render_array); $html = \Drupal::service('renderer')->renderRoot($node_render_array);
$node_uri = $this->node->toUrl('canonical', ['absolute' => TRUE])->toString(); $node_uri = $this->node->toUrl('canonical', ['absolute' => TRUE])->toString();
$file_uri = file_create_url($this->file->getFileUri()); $file_uri = $this->file->createFileUrl(FALSE);
// Node relation to attached file. // Node relation to attached file.
$expected_value = [ $expected_value = [

View File

@ -495,11 +495,15 @@ function _responsive_image_image_style_url($style_name, $path) {
// http://probablyprogramming.com/2009/03/15/the-tiniest-gif-ever // http://probablyprogramming.com/2009/03/15/the-tiniest-gif-ever
return 'data:image/gif;base64,R0lGODlhAQABAIABAP///wAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw=='; return 'data:image/gif;base64,R0lGODlhAQABAIABAP///wAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==';
} }
/** @var \Drupal\Core\File\FileUrlGeneratorInterface $file_url_generator */
$file_url_generator = \Drupal::service('file_url_generator');
$entity = ImageStyle::load($style_name); $entity = ImageStyle::load($style_name);
if ($entity instanceof ImageStyle) { if ($entity instanceof ImageStyle) {
return file_url_transform_relative($entity->buildUrl($path)); return $file_url_generator->transformRelative($entity->buildUrl($path));
} }
return file_url_transform_relative(file_create_url($path)); return $file_url_generator->generateString($path);
} }
/** /**

View File

@ -35,6 +35,13 @@ class ResponsiveImageFieldDisplayTest extends ImageFieldTestBase {
*/ */
protected $responsiveImgStyle; protected $responsiveImgStyle;
/**
* The file URL generator.
*
* @var \Drupal\Core\File\FileUrlGeneratorInterface
*/
protected $fileUrlGenerator;
/** /**
* Modules to enable. * Modules to enable.
* *
@ -52,6 +59,8 @@ class ResponsiveImageFieldDisplayTest extends ImageFieldTestBase {
protected function setUp(): void { protected function setUp(): void {
parent::setUp(); parent::setUp();
$this->fileUrlGenerator = $this->container->get('file_url_generator');
// Create user. // Create user.
$this->adminUser = $this->drupalCreateUser([ $this->adminUser = $this->drupalCreateUser([
'administer responsive images', 'administer responsive images',
@ -259,9 +268,9 @@ class ResponsiveImageFieldDisplayTest extends ImageFieldTestBase {
// No image style cache tag should be found. // No image style cache tag should be found.
$this->assertSession()->responseHeaderNotContains('X-Drupal-Cache-Tags', 'image_style:'); $this->assertSession()->responseHeaderNotContains('X-Drupal-Cache-Tags', 'image_style:');
$this->assertSession()->responseMatches('/<a(.*?)href="' . preg_quote(file_url_transform_relative(file_create_url($image_uri)), '/') . '"(.*?)>\s*<picture/'); $this->assertSession()->responseMatches('/<a(.*?)href="' . preg_quote($this->fileUrlGenerator->generateString($image_uri), '/') . '"(.*?)>\s*<picture/');
// Verify that the image can be downloaded. // Verify that the image can be downloaded.
$this->assertEquals(file_get_contents($test_image->uri), $this->drupalGet(file_create_url($image_uri)), 'File was downloaded successfully.'); $this->assertEquals(file_get_contents($test_image->uri), $this->drupalGet($this->fileUrlGenerator->generateAbsoluteString($image_uri)), 'File was downloaded successfully.');
if ($scheme == 'private') { if ($scheme == 'private') {
// Only verify HTTP headers when using private scheme and the headers are // Only verify HTTP headers when using private scheme and the headers are
// sent by Drupal. // sent by Drupal.
@ -270,7 +279,7 @@ class ResponsiveImageFieldDisplayTest extends ImageFieldTestBase {
// Log out and ensure the file cannot be accessed. // Log out and ensure the file cannot be accessed.
$this->drupalLogout(); $this->drupalLogout();
$this->drupalGet(file_create_url($image_uri)); $this->drupalGet($this->fileUrlGenerator->generateAbsoluteString($image_uri));
$this->assertSession()->statusCodeEquals(403); $this->assertSession()->statusCodeEquals(403);
// Log in again. // Log in again.
@ -295,10 +304,10 @@ class ResponsiveImageFieldDisplayTest extends ImageFieldTestBase {
$this->assertRaw('data:image/gif;base64,R0lGODlhAQABAIABAP///wAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw=='); $this->assertRaw('data:image/gif;base64,R0lGODlhAQABAIABAP///wAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==');
$thumbnail_style = ImageStyle::load('thumbnail'); $thumbnail_style = ImageStyle::load('thumbnail');
// Assert the output of the 'srcset' attribute (small multipliers first). // Assert the output of the 'srcset' attribute (small multipliers first).
$this->assertRaw('data:image/gif;base64,R0lGODlhAQABAIABAP///wAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw== 1x, ' . file_url_transform_relative($thumbnail_style->buildUrl($image_uri)) . ' 1.5x'); $this->assertSession()->responseContains('data:image/gif;base64,R0lGODlhAQABAIABAP///wAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw== 1x, ' . $this->fileUrlGenerator->transformRelative($thumbnail_style->buildUrl($image_uri)) . ' 1.5x');
$this->assertRaw('/styles/medium/'); $this->assertRaw('/styles/medium/');
// Assert the output of the original image. // Assert the output of the original image.
$this->assertRaw(file_url_transform_relative(file_create_url($image_uri)) . ' 3x'); $this->assertSession()->responseContains($this->fileUrlGenerator->generateString($image_uri) . ' 3x');
// Assert the output of the breakpoints. // Assert the output of the breakpoints.
$this->assertRaw('media="(min-width: 0px)"'); $this->assertRaw('media="(min-width: 0px)"');
$this->assertRaw('media="(min-width: 560px)"'); $this->assertRaw('media="(min-width: 560px)"');
@ -307,7 +316,7 @@ class ResponsiveImageFieldDisplayTest extends ImageFieldTestBase {
$this->assertSession()->responseMatches('/media="\(min-width: 560px\)".+?sizes="\(min-width: 700px\) 700px, 100vw"/'); $this->assertSession()->responseMatches('/media="\(min-width: 560px\)".+?sizes="\(min-width: 700px\) 700px, 100vw"/');
// Assert the output of the 'srcset' attribute (small images first). // Assert the output of the 'srcset' attribute (small images first).
$medium_style = ImageStyle::load('medium'); $medium_style = ImageStyle::load('medium');
$this->assertRaw(file_url_transform_relative($medium_style->buildUrl($image_uri)) . ' 220w, ' . file_url_transform_relative($large_style->buildUrl($image_uri)) . ' 360w'); $this->assertSession()->responseContains($this->fileUrlGenerator->transformRelative($medium_style->buildUrl($image_uri)) . ' 220w, ' . $this->fileUrlGenerator->transformRelative($large_style->buildUrl($image_uri)) . ' 360w');
$this->assertRaw('media="(min-width: 851px)"'); $this->assertRaw('media="(min-width: 851px)"');
} }
$this->assertRaw('/styles/large/'); $this->assertRaw('/styles/large/');
@ -324,7 +333,7 @@ class ResponsiveImageFieldDisplayTest extends ImageFieldTestBase {
$fallback_image = [ $fallback_image = [
'#theme' => 'image', '#theme' => 'image',
'#alt' => $alt, '#alt' => $alt,
'#uri' => file_url_transform_relative($large_style->buildUrl($image->getSource())), '#uri' => $this->fileUrlGenerator->transformRelative($large_style->buildUrl($image->getSource())),
]; ];
// The image.html.twig template has a newline after the <img> tag but // The image.html.twig template has a newline after the <img> tag but
// responsive-image.html.twig doesn't have one after the fallback image, so // responsive-image.html.twig doesn't have one after the fallback image, so
@ -404,7 +413,7 @@ class ResponsiveImageFieldDisplayTest extends ImageFieldTestBase {
$thumbnail_style = ImageStyle::load('thumbnail'); $thumbnail_style = ImageStyle::load('thumbnail');
$node = $node_storage->load($nid); $node = $node_storage->load($nid);
$image_uri = File::load($node->{$field_name}->target_id)->getFileUri(); $image_uri = File::load($node->{$field_name}->target_id)->getFileUri();
$this->assertSession()->responseMatches('/srcset="' . preg_quote(file_url_transform_relative($thumbnail_style->buildUrl($image_uri)), '/') . ' 1x".+?media="\(min-width: 0px\)"/'); $this->assertSession()->responseMatches('/srcset="' . preg_quote($this->fileUrlGenerator->transformRelative($thumbnail_style->buildUrl($image_uri)), '/') . ' 1x".+?media="\(min-width: 0px\)"/');
} }
/** /**
@ -451,7 +460,7 @@ class ResponsiveImageFieldDisplayTest extends ImageFieldTestBase {
$medium_style = ImageStyle::load('medium'); $medium_style = ImageStyle::load('medium');
$node = $node_storage->load($nid); $node = $node_storage->load($nid);
$image_uri = File::load($node->{$field_name}->target_id)->getFileUri(); $image_uri = File::load($node->{$field_name}->target_id)->getFileUri();
$this->assertRaw('<img srcset="' . file_url_transform_relative($medium_style->buildUrl($image_uri)) . ' 1x, ' . file_url_transform_relative($large_style->buildUrl($image_uri)) . ' 2x"'); $this->assertSession()->responseContains('<img srcset="' . $this->fileUrlGenerator->transformRelative($medium_style->buildUrl($image_uri)) . ' 1x, ' . $this->fileUrlGenerator->transformRelative($large_style->buildUrl($image_uri)) . ' 2x"');
} }
/** /**
@ -513,7 +522,7 @@ class ResponsiveImageFieldDisplayTest extends ImageFieldTestBase {
switch ($link_type) { switch ($link_type) {
case 'file': case 'file':
// Make sure the link to the file is present. // Make sure the link to the file is present.
$this->assertSession()->responseMatches('/<a(.*?)href="' . preg_quote(file_url_transform_relative(file_create_url($image_uri)), '/') . '"(.*?)>\s*<picture/'); $this->assertSession()->responseMatches('/<a(.*?)href="' . preg_quote($this->fileUrlGenerator->generateString($image_uri), '/') . '"(.*?)>\s*<picture/');
break; break;
case 'content': case 'content':

View File

@ -4,6 +4,7 @@ namespace Drupal\serialization\Normalizer;
use Drupal\Core\Entity\EntityRepositoryInterface; use Drupal\Core\Entity\EntityRepositoryInterface;
use Drupal\Core\Field\Plugin\Field\FieldType\EntityReferenceItem; use Drupal\Core\Field\Plugin\Field\FieldType\EntityReferenceItem;
use Drupal\file\FileInterface;
use Symfony\Component\Serializer\Exception\InvalidArgumentException; use Symfony\Component\Serializer\Exception\InvalidArgumentException;
use Symfony\Component\Serializer\Exception\UnexpectedValueException; use Symfony\Component\Serializer\Exception\UnexpectedValueException;
@ -59,8 +60,8 @@ class EntityReferenceFieldItemNormalizer extends FieldItemNormalizer {
} }
// @todo Remove in https://www.drupal.org/project/drupal/issues/2925520 // @todo Remove in https://www.drupal.org/project/drupal/issues/2925520
// @see \Drupal\hal\Normalizer\FileEntityNormalizer // @see \Drupal\hal\Normalizer\FileEntityNormalizer
elseif ($entity->getEntityTypeId() === 'file') { elseif ($entity instanceof FileInterface) {
$values['url'] = file_create_url($entity->getFileUri()); $values['url'] = $entity->createFileUrl(FALSE);
} }
} }

View File

@ -209,6 +209,7 @@ class FormTestDisabledElementsForm extends FormBase {
$form['image_button'] = [ $form['image_button'] = [
'#type' => 'image_button', '#type' => 'image_button',
'#value' => 'Image button', '#value' => 'Image button',
'#src' => '',
'#disabled' => TRUE, '#disabled' => TRUE,
]; ];
$form['button'] = [ $form['button'] = [

View File

@ -26,7 +26,7 @@ class RetrieveFileTest extends BrowserTestBase {
$file_system->mkdir($sourcedir = 'public://' . $this->randomMachineName()); $file_system->mkdir($sourcedir = 'public://' . $this->randomMachineName());
// cSpell:disable-next-line // cSpell:disable-next-line
$filename = 'Файл для тестирования ' . $this->randomMachineName(); $filename = 'Файл для тестирования ' . $this->randomMachineName();
$url = file_create_url($sourcedir . '/' . $filename); $url = \Drupal::service('file_url_generator')->generateAbsoluteString($sourcedir . '/' . $filename);
$retrieved_file = system_retrieve_file($url); $retrieved_file = system_retrieve_file($url);
$this->assertFalse($retrieved_file, 'Non-existent file not fetched.'); $this->assertFalse($retrieved_file, 'Non-existent file not fetched.');

View File

@ -75,21 +75,23 @@ class ThemeTest extends BrowserTestBase {
$file_relative = strtr($file->uri, ['public:/' => PublicStream::basePath()]); $file_relative = strtr($file->uri, ['public:/' => PublicStream::basePath()]);
$default_theme_path = 'core/themes/classy'; $default_theme_path = 'core/themes/classy';
/** @var \Drupal\Core\File\FileUrlGeneratorInterface $file_url_generator */
$file_url_generator = \Drupal::service('file_url_generator');
$supported_paths = [ $supported_paths = [
// Raw stream wrapper URI. // Raw stream wrapper URI.
$file->uri => [ $file->uri => [
'form' => StreamWrapperManager::getTarget($file->uri), 'form' => StreamWrapperManager::getTarget($file->uri),
'src' => file_url_transform_relative(file_create_url($file->uri)), 'src' => $file_url_generator->generateString($file->uri),
], ],
// Relative path within the public filesystem. // Relative path within the public filesystem.
StreamWrapperManager::getTarget($file->uri) => [ StreamWrapperManager::getTarget($file->uri) => [
'form' => StreamWrapperManager::getTarget($file->uri), 'form' => StreamWrapperManager::getTarget($file->uri),
'src' => file_url_transform_relative(file_create_url($file->uri)), 'src' => $file_url_generator->generateString($file->uri),
], ],
// Relative path to a public file. // Relative path to a public file.
$file_relative => [ $file_relative => [
'form' => $file_relative, 'form' => $file_relative,
'src' => file_url_transform_relative(file_create_url($file->uri)), 'src' => $file_url_generator->generateString($file->uri),
], ],
// Relative path to an arbitrary file. // Relative path to an arbitrary file.
'core/misc/druplicon.png' => [ 'core/misc/druplicon.png' => [
@ -198,7 +200,7 @@ class ThemeTest extends BrowserTestBase {
':rel' => 'home', ':rel' => 'home',
] ]
); );
$this->assertEquals(file_url_transform_relative(file_create_url($uploaded_filename)), $elements[0]->getAttribute('src')); $this->assertEquals($file_url_generator->generateString($uploaded_filename), $elements[0]->getAttribute('src'));
$this->container->get('theme_installer')->install(['bartik']); $this->container->get('theme_installer')->install(['bartik']);

View File

@ -133,7 +133,9 @@ class EngineTwigTest extends BrowserTestBase {
*/ */
public function testTwigFileUrls() { public function testTwigFileUrls() {
$this->drupalGet('/twig-theme-test/file-url'); $this->drupalGet('/twig-theme-test/file-url');
$filepath = file_url_transform_relative(file_create_url('core/modules/system/tests/modules/twig_theme_test/twig_theme_test.js')); /** @var \Drupal\Core\File\FileUrlGeneratorInterface $file_url_generator */
$file_url_generator = \Drupal::service('file_url_generator');
$filepath = $file_url_generator->generateString('core/modules/system/tests/modules/twig_theme_test/twig_theme_test.js');
$this->assertRaw('<div>file_url: ' . $filepath . '</div>'); $this->assertRaw('<div>file_url: ' . $filepath . '</div>');
} }

View File

@ -106,13 +106,13 @@ class TaxonomyImageTest extends TaxonomyTestBase {
// Ensure a user that should be able to access the file can access it. // Ensure a user that should be able to access the file can access it.
$this->drupalLogin($access_user); $this->drupalLogin($access_user);
$this->drupalGet(file_create_url($image->getFileUri())); $this->drupalGet($image->createFileUrl(FALSE));
$this->assertSession()->statusCodeEquals(200); $this->assertSession()->statusCodeEquals(200);
// Ensure a user that should not be able to access the file cannot access // Ensure a user that should not be able to access the file cannot access
// it. // it.
$this->drupalLogin($no_access_user); $this->drupalLogin($no_access_user);
$this->drupalGet(file_create_url($image->getFileUri())); $this->drupalGet($image->createFileUrl(FALSE));
$this->assertSession()->statusCodeEquals(403); $this->assertSession()->statusCodeEquals(403);
} }

View File

@ -116,7 +116,7 @@ class UserPictureTest extends BrowserTestBase {
$image_style_id = $this->config('core.entity_view_display.user.user.compact')->get('content.user_picture.settings.image_style'); $image_style_id = $this->config('core.entity_view_display.user.user.compact')->get('content.user_picture.settings.image_style');
$style = ImageStyle::load($image_style_id); $style = ImageStyle::load($image_style_id);
$image_url = file_url_transform_relative($style->buildUrl($file->getfileUri())); $image_url = \Drupal::service('file_url_generator')->transformRelative($style->buildUrl($file->getfileUri()));
$alt_text = 'Profile picture for user ' . $this->webUser->getAccountName(); $alt_text = 'Profile picture for user ' . $this->webUser->getAccountName();
// Verify that the image is displayed on the node page. // Verify that the image is displayed on the node page.

View File

@ -35,6 +35,13 @@ class AttachedAssetsTest extends KernelTestBase {
*/ */
protected $renderer; protected $renderer;
/**
* The file URL generator.
*
* @var \Drupal\Core\File\FileUrlGeneratorInterface
*/
protected $fileUrlGenerator;
/** /**
* {@inheritdoc} * {@inheritdoc}
*/ */
@ -48,6 +55,7 @@ class AttachedAssetsTest extends KernelTestBase {
$this->assetResolver = $this->container->get('asset.resolver'); $this->assetResolver = $this->container->get('asset.resolver');
$this->renderer = $this->container->get('renderer'); $this->renderer = $this->container->get('renderer');
$this->fileUrlGenerator = $this->container->get('file_url_generator');
} }
/** /**
@ -88,8 +96,8 @@ class AttachedAssetsTest extends KernelTestBase {
$rendered_css = $this->renderer->renderPlain($css_render_array); $rendered_css = $this->renderer->renderPlain($css_render_array);
$rendered_js = $this->renderer->renderPlain($js_render_array); $rendered_js = $this->renderer->renderPlain($js_render_array);
$query_string = $this->container->get('state')->get('system.css_js_query_string') ?: '0'; $query_string = $this->container->get('state')->get('system.css_js_query_string') ?: '0';
$this->assertStringContainsString('<link rel="stylesheet" media="all" href="' . file_url_transform_relative(file_create_url('core/modules/system/tests/modules/common_test/bar.css')) . '?' . $query_string . '" />', $rendered_css, 'Rendering an external CSS file.'); $this->assertStringContainsString('<link rel="stylesheet" media="all" href="' . $this->fileUrlGenerator->generateString('core/modules/system/tests/modules/common_test/bar.css') . '?' . $query_string . '" />', $rendered_css, 'Rendering an external CSS file.');
$this->assertStringContainsString('<script src="' . file_url_transform_relative(file_create_url('core/modules/system/tests/modules/common_test/foo.js')) . '?' . $query_string . '"></script>', $rendered_js, 'Rendering an external JavaScript file.'); $this->assertStringContainsString('<script src="' . $this->fileUrlGenerator->generateString('core/modules/system/tests/modules/common_test/foo.js') . '?' . $query_string . '"></script>', $rendered_js, 'Rendering an external JavaScript file.');
} }
/** /**
@ -142,7 +150,7 @@ class AttachedAssetsTest extends KernelTestBase {
$js_render_array = \Drupal::service('asset.js.collection_renderer')->render($js); $js_render_array = \Drupal::service('asset.js.collection_renderer')->render($js);
$rendered_js = $this->renderer->renderPlain($js_render_array); $rendered_js = $this->renderer->renderPlain($js_render_array);
$expected_1 = '<script src="http://example.com/deferred-external.js" foo="bar" defer></script>'; $expected_1 = '<script src="http://example.com/deferred-external.js" foo="bar" defer></script>';
$expected_2 = '<script src="' . file_url_transform_relative(file_create_url('core/modules/system/tests/modules/common_test/deferred-internal.js')) . '?v=1" defer bar="foo"></script>'; $expected_2 = '<script src="' . $this->fileUrlGenerator->generateString('core/modules/system/tests/modules/common_test/deferred-internal.js') . '?v=1" defer bar="foo"></script>';
$this->assertStringContainsString($expected_1, $rendered_js, 'Rendered external JavaScript with correct defer and random attributes.'); $this->assertStringContainsString($expected_1, $rendered_js, 'Rendered external JavaScript with correct defer and random attributes.');
$this->assertStringContainsString($expected_2, $rendered_js, 'Rendered internal JavaScript with correct defer and random attributes.'); $this->assertStringContainsString($expected_2, $rendered_js, 'Rendered internal JavaScript with correct defer and random attributes.');
} }
@ -158,7 +166,7 @@ class AttachedAssetsTest extends KernelTestBase {
$js_render_array = \Drupal::service('asset.js.collection_renderer')->render($js); $js_render_array = \Drupal::service('asset.js.collection_renderer')->render($js);
$rendered_js = $this->renderer->renderPlain($js_render_array); $rendered_js = $this->renderer->renderPlain($js_render_array);
$expected_1 = '<script src="http://example.com/deferred-external.js" foo="bar" defer></script>'; $expected_1 = '<script src="http://example.com/deferred-external.js" foo="bar" defer></script>';
$expected_2 = '<script src="' . file_url_transform_relative(file_create_url('core/modules/system/tests/modules/common_test/deferred-internal.js')) . '?v=1" defer bar="foo"></script>'; $expected_2 = '<script src="' . $this->fileUrlGenerator->generateString('core/modules/system/tests/modules/common_test/deferred-internal.js') . '?v=1" defer bar="foo"></script>';
$this->assertStringContainsString($expected_1, $rendered_js, 'Rendered external JavaScript with correct defer and random attributes.'); $this->assertStringContainsString($expected_1, $rendered_js, 'Rendered external JavaScript with correct defer and random attributes.');
$this->assertStringContainsString($expected_2, $rendered_js, 'Rendered internal JavaScript with correct defer and random attributes.'); $this->assertStringContainsString($expected_2, $rendered_js, 'Rendered internal JavaScript with correct defer and random attributes.');
} }
@ -232,9 +240,9 @@ class AttachedAssetsTest extends KernelTestBase {
$js_render_array = \Drupal::service('asset.js.collection_renderer')->render($js); $js_render_array = \Drupal::service('asset.js.collection_renderer')->render($js);
$rendered_js = $this->renderer->renderPlain($js_render_array); $rendered_js = $this->renderer->renderPlain($js_render_array);
$query_string = $this->container->get('state')->get('system.css_js_query_string') ?: '0'; $query_string = $this->container->get('state')->get('system.css_js_query_string') ?: '0';
$this->assertStringContainsString('<script src="' . file_url_transform_relative(file_create_url('core/modules/system/tests/modules/common_test/header.js')) . '?' . $query_string . '"></script>', $rendered_js, 'The JS asset in common_test/js-header appears in the header.'); $this->assertStringContainsString('<script src="' . $this->fileUrlGenerator->generateString('core/modules/system/tests/modules/common_test/header.js') . '?' . $query_string . '"></script>', $rendered_js, 'The JS asset in common_test/js-header appears in the header.');
$this->assertStringContainsString('<script src="' . file_url_transform_relative(file_create_url('core/misc/drupal.js')), $rendered_js, 'The JS asset of the direct dependency (core/drupal) of common_test/js-header appears in the header.'); $this->assertStringContainsString('<script src="' . $this->fileUrlGenerator->generateString('core/misc/drupal.js'), $rendered_js, 'The JS asset of the direct dependency (core/drupal) of common_test/js-header appears in the header.');
$this->assertStringContainsString('<script src="' . file_url_transform_relative(file_create_url('core/misc/drupalSettingsLoader.js')), $rendered_js, 'The JS asset of the indirect dependency (core/drupalSettings) of common_test/js-header appears in the header.'); $this->assertStringContainsString('<script src="' . $this->fileUrlGenerator->generateString('core/misc/drupalSettingsLoader.js'), $rendered_js, 'The JS asset of the indirect dependency (core/drupalSettings) of common_test/js-header appears in the header.');
} }
/** /**
@ -264,8 +272,8 @@ class AttachedAssetsTest extends KernelTestBase {
$js = $this->assetResolver->getJsAssets($assets, FALSE)[1]; $js = $this->assetResolver->getJsAssets($assets, FALSE)[1];
$js_render_array = \Drupal::service('asset.js.collection_renderer')->render($js); $js_render_array = \Drupal::service('asset.js.collection_renderer')->render($js);
$rendered_js = $this->renderer->renderPlain($js_render_array); $rendered_js = $this->renderer->renderPlain($js_render_array);
$expected_1 = "<!--[if lte IE 8]>\n" . '<script src="' . file_url_transform_relative(file_create_url('core/modules/system/tests/modules/common_test/old-ie.js')) . '?' . $default_query_string . '"></script>' . "\n<![endif]-->"; $expected_1 = "<!--[if lte IE 8]>\n" . '<script src="' . $this->fileUrlGenerator->generateString('core/modules/system/tests/modules/common_test/old-ie.js') . '?' . $default_query_string . '"></script>' . "\n<![endif]-->";
$expected_2 = "<!--[if !IE]><!-->\n" . '<script src="' . file_url_transform_relative(file_create_url('core/modules/system/tests/modules/common_test/no-ie.js')) . '?' . $default_query_string . '"></script>' . "\n<!--<![endif]-->"; $expected_2 = "<!--[if !IE]><!-->\n" . '<script src="' . $this->fileUrlGenerator->generateString('core/modules/system/tests/modules/common_test/no-ie.js') . '?' . $default_query_string . '"></script>' . "\n<!--<![endif]-->";
$this->assertStringContainsString($expected_1, $rendered_js, 'Rendered JavaScript within downlevel-hidden conditional comments.'); $this->assertStringContainsString($expected_1, $rendered_js, 'Rendered JavaScript within downlevel-hidden conditional comments.');
$this->assertStringContainsString($expected_2, $rendered_js, 'Rendered JavaScript within downlevel-revealed conditional comments.'); $this->assertStringContainsString($expected_2, $rendered_js, 'Rendered JavaScript within downlevel-revealed conditional comments.');
@ -475,8 +483,8 @@ class AttachedAssetsTest extends KernelTestBase {
$js_render_array = \Drupal::service('asset.js.collection_renderer')->render($js); $js_render_array = \Drupal::service('asset.js.collection_renderer')->render($js);
$rendered_js = $this->renderer->renderPlain($js_render_array); $rendered_js = $this->renderer->renderPlain($js_render_array);
$query_string = $this->container->get('state')->get('system.css_js_query_string') ?: '0'; $query_string = $this->container->get('state')->get('system.css_js_query_string') ?: '0';
$this->assertStringContainsString('<link rel="stylesheet" media="all" href="' . str_replace('&', '&amp;', file_url_transform_relative(file_create_url('core/modules/system/tests/modules/common_test/querystring.css?arg1=value1&arg2=value2'))) . '&amp;' . $query_string . '" />', $rendered_css, 'CSS file with query string gets version query string correctly appended..'); $this->assertStringContainsString('<link rel="stylesheet" media="all" href="' . str_replace('&', '&amp;', $this->fileUrlGenerator->generateString('core/modules/system/tests/modules/common_test/querystring.css?arg1=value1&arg2=value2')) . '&amp;' . $query_string . '" />', $rendered_css, 'CSS file with query string gets version query string correctly appended..');
$this->assertStringContainsString('<script src="' . str_replace('&', '&amp;', file_url_transform_relative(file_create_url('core/modules/system/tests/modules/common_test/querystring.js?arg1=value1&arg2=value2'))) . '&amp;' . $query_string . '"></script>', $rendered_js, 'JavaScript file with query string gets version query string correctly appended.'); $this->assertStringContainsString('<script src="' . str_replace('&', '&amp;', $this->fileUrlGenerator->generateString('core/modules/system/tests/modules/common_test/querystring.js?arg1=value1&arg2=value2')) . '&amp;' . $query_string . '"></script>', $rendered_js, 'JavaScript file with query string gets version query string correctly appended.');
} }
} }

View File

@ -0,0 +1,26 @@
<?php
namespace Drupal\KernelTests\Core\File;
use Drupal\KernelTests\KernelTestBase;
/**
* Tests deprecations in file.inc.
*
* @group File
* @group legacy
*/
class FileSystemDeprecationTest extends KernelTestBase {
/**
* Tests deprecated FileCreateUrl.
*/
public function testDeprecatedFileCreateUrl() {
$this->expectDeprecation('file_create_url() is deprecated in drupal:9.3.0 and is removed from drupal:10.0.0. Use the appropriate method on \Drupal\Core\File\FileUrlGeneratorInterface instead. See https://www.drupal.org/node/2940031');
$this->expectDeprecation('file_url_transform_relative() is deprecated in drupal:9.3.0 and is removed from drupal:10.0.0. Use \Drupal\Core\File\FileUrlGenerator::transformRelative() instead. See https://www.drupal.org/node/2940031');
$filepath = 'core/assets/vendor/jquery/jquery.min.js';
$url = file_url_transform_relative(file_create_url($filepath));
$this->assertNotEmpty($url);
}
}

View File

@ -0,0 +1,257 @@
<?php
namespace Drupal\KernelTests\Core\File;
use Drupal\Core\File\Exception\InvalidStreamWrapperException;
use Symfony\Component\HttpFoundation\Request;
/**
* @coversDefaultClass \Drupal\Core\File\FileUrlGenerator
*
* @group File
*/
class FileUrlGeneratorTest extends FileTestBase {
/**
* {@inheritdoc}
*/
protected static $modules = ['system', 'file_test'];
/**
* The file URL generator under test.
*
* @var \Drupal\Core\File\FileUrlGenerator
*/
protected $fileUrlGenerator;
/**
* {@inheritdoc}
*/
protected function setUp(): void {
parent::setUp();
$this->fileUrlGenerator = $this->container->get('file_url_generator');
}
/**
* Tests missing stream handler.
*
* @covers ::generate
*/
public function testGenerateMissingStreamWrapper() {
$this->expectException(InvalidStreamWrapperException::class);
$result = $this->fileUrlGenerator->generate("foo://bar");
}
/**
* Tests missing stream handler.
*
* @covers ::generateString
*/
public function testGenerateStringMissingStreamWrapper() {
$this->expectException(InvalidStreamWrapperException::class);
$result = $this->fileUrlGenerator->generateString("foo://bar");
}
/**
* Tests missing stream handler.
*
* @covers ::generateAbsoluteString
*/
public function testGenerateAbsoluteStringMissingStreamWrapper() {
$this->expectException(InvalidStreamWrapperException::class);
$result = $this->fileUrlGenerator->generateAbsoluteString("foo://bar");
}
/**
* Tests the rewriting of shipped file URLs by hook_file_url_alter().
*
* @covers ::generateAbsoluteString
*/
public function testShippedFileURL() {
// Test generating a URL to a shipped file (i.e. a file that is part of
// Drupal core, a module or a theme, for example a JavaScript file).
// Test alteration of file URLs to use a CDN.
\Drupal::state()->set('file_test.hook_file_url_alter', 'cdn');
$filepath = 'core/assets/vendor/jquery/jquery.min.js';
$url = $this->fileUrlGenerator->generateAbsoluteString($filepath);
$this->assertEquals(FILE_URL_TEST_CDN_1 . '/' . $filepath, $url, 'Correctly generated a CDN URL for a shipped file.');
$filepath = 'core/misc/favicon.ico';
$url = $this->fileUrlGenerator->generateAbsoluteString($filepath);
$this->assertEquals(FILE_URL_TEST_CDN_2 . '/' . $filepath, $url, 'Correctly generated a CDN URL for a shipped file.');
// Test alteration of file URLs to use root-relative URLs.
\Drupal::state()->set('file_test.hook_file_url_alter', 'root-relative');
$filepath = 'core/assets/vendor/jquery/jquery.min.js';
$url = $this->fileUrlGenerator->generateAbsoluteString($filepath);
$this->assertEquals(base_path() . '/' . $filepath, $url, 'Correctly generated a root-relative URL for a shipped file.');
$filepath = 'core/misc/favicon.ico';
$url = $this->fileUrlGenerator->generateAbsoluteString($filepath);
$this->assertEquals(base_path() . '/' . $filepath, $url, 'Correctly generated a root-relative URL for a shipped file.');
// Test alteration of file URLs to use protocol-relative URLs.
\Drupal::state()->set('file_test.hook_file_url_alter', 'protocol-relative');
$filepath = 'core/assets/vendor/jquery/jquery.min.js';
$url = $this->fileUrlGenerator->generateAbsoluteString($filepath);
$this->assertEquals('/' . base_path() . '/' . $filepath, $url, 'Correctly generated a protocol-relative URL for a shipped file.');
$filepath = 'core/misc/favicon.ico';
$url = $this->fileUrlGenerator->generateAbsoluteString($filepath);
$this->assertEquals('/' . base_path() . '/' . $filepath, $url, 'Correctly generated a protocol-relative URL for a shipped file.');
// Test alteration of file URLs with query strings and/or fragment.
\Drupal::state()->delete('file_test.hook_file_url_alter');
$filepath = 'core/misc/favicon.ico';
$url = $this->fileUrlGenerator->generateAbsoluteString($filepath . '?foo');
$this->assertEquals($GLOBALS['base_url'] . '/' . $filepath . '?foo=', $url, 'Correctly generated URL. The query string is present.');
$url = $this->fileUrlGenerator->generateAbsoluteString($filepath . '?foo=bar');
$this->assertEquals($GLOBALS['base_url'] . '/' . $filepath . '?foo=bar', $url, 'Correctly generated URL. The query string is present.');
$url = $this->fileUrlGenerator->generateAbsoluteString($filepath . '#v1.2');
$this->assertEquals($GLOBALS['base_url'] . '/' . $filepath . '#v1.2', $url, 'Correctly generated URL. The fragment is present.');
$url = $this->fileUrlGenerator->generateAbsoluteString($filepath . '?foo=bar#v1.2');
$this->assertEquals($GLOBALS['base_url'] . '/' . $filepath . '?foo=bar#v1.2', $url, 'Correctly generated URL. The query string amd fragment is present.');
}
/**
* Tests the rewriting of public managed file URLs by hook_file_url_alter().
*
* @covers ::generateAbsoluteString
*/
public function testPublicManagedFileURL() {
// Test generating a URL to a managed file.
// Test alteration of file URLs to use a CDN.
\Drupal::state()->set('file_test.hook_file_url_alter', 'cdn');
$uri = $this->createUri();
$url = $this->fileUrlGenerator->generateAbsoluteString($uri);
$public_directory_path = \Drupal::service('stream_wrapper_manager')
->getViaScheme('public')
->getDirectoryPath();
/** @var \Drupal\Core\File\FileSystemInterface $file_system */
$file_system = \Drupal::service('file_system');
$this->assertEquals(FILE_URL_TEST_CDN_2 . '/' . $public_directory_path . '/' . $file_system->basename($uri), $url, 'Correctly generated a CDN URL for a created file.');
// Test alteration of file URLs to use root-relative URLs.
\Drupal::state()->set('file_test.hook_file_url_alter', 'root-relative');
$uri = $this->createUri();
$url = $this->fileUrlGenerator->generateAbsoluteString($uri);
$this->assertEquals(base_path() . '/' . $public_directory_path . '/' . $file_system->basename($uri), $url, 'Correctly generated a root-relative URL for a created file.');
// Test alteration of file URLs to use a protocol-relative URLs.
\Drupal::state()->set('file_test.hook_file_url_alter', 'protocol-relative');
$uri = $this->createUri();
$url = $this->fileUrlGenerator->generateAbsoluteString($uri);
$this->assertEquals('/' . base_path() . '/' . $public_directory_path . '/' . $file_system->basename($uri), $url, 'Correctly generated a protocol-relative URL for a created file.');
}
/**
* Tests generate absolute string with relative URL.
*
* @covers ::generateAbsoluteString
*/
public function testRelativeFileURL() {
// Disable file_test.module's hook_file_url_alter() implementation.
\Drupal::state()->set('file_test.hook_file_url_alter', NULL);
// Create a mock Request for transformRelative().
$request = Request::create($GLOBALS['base_url']);
$this->container->get('request_stack')->push($request);
\Drupal::setContainer($this->container);
// Shipped file.
$filepath = 'core/assets/vendor/jquery/jquery.min.js';
$url = $this->fileUrlGenerator->generateAbsoluteString($filepath);
$this->assertSame(base_path() . $filepath, $this->fileUrlGenerator->transformRelative($url));
// Managed file.
$uri = $this->createUri();
$url = $this->fileUrlGenerator->generateAbsoluteString($uri);
$public_directory_path = \Drupal::service('stream_wrapper_manager')
->getViaScheme('public')
->getDirectoryPath();
$this->assertSame(base_path() . $public_directory_path . '/' . rawurlencode(\Drupal::service('file_system')
->basename($uri)), $this->fileUrlGenerator->transformRelative($url));
}
/**
* @covers ::generate
*
* @dataProvider providerGenerateURI
*/
public function testGenerateURI($filepath, $expected) {
// Disable file_test.module's hook_file_url_alter() implementation.
\Drupal::state()->set('file_test.hook_file_url_alter', NULL);
// Create a mock Request for transformRelative().
$request = Request::create($GLOBALS['base_url']);
$this->container->get('request_stack')->push($request);
\Drupal::setContainer($this->container);
// No schema file.
$url = $this->fileUrlGenerator->generate($filepath);
$this->assertEquals($expected, $url->getUri());
}
/**
* @covers ::generate
*/
public function testGenerateURIWithSchema() {
// Disable file_test.module's hook_file_url_alter() implementation.
\Drupal::state()->set('file_test.hook_file_url_alter', NULL);
// Create a mock Request for transformRelative().
$request = Request::create($GLOBALS['base_url']);
$this->container->get('request_stack')->push($request);
\Drupal::setContainer($this->container);
$public_directory_path = \Drupal::service('stream_wrapper_manager')
->getViaScheme('public')
->getDirectoryPath();
$url = $this->fileUrlGenerator->generate('public://path/to/file.png');
$this->assertEquals('base:/' . $public_directory_path . '/path/to/file.png', $url->getUri());
}
/**
* Data provider.
*/
public function providerGenerateURI() {
return [
'schemaless' =>
[
'//core/assets/vendor/jquery/jquery.min.js',
'//core/assets/vendor/jquery/jquery.min.js',
],
'query string' =>
[
'//core/assets/vendor/jquery/jquery.min.js?foo',
'//core/assets/vendor/jquery/jquery.min.js?foo',
],
'query string and hashes' =>
[
'//core/assets/vendor/jquery/jquery.min.js?foo=bar#whizz',
'//core/assets/vendor/jquery/jquery.min.js?foo=bar#whizz',
],
'hashes' =>
[
'//core/assets/vendor/jquery/jquery.min.js#whizz',
'//core/assets/vendor/jquery/jquery.min.js#whizz',
],
'root-relative' =>
[
'/core/assets/vendor/jquery/jquery.min.js',
'base:/core/assets/vendor/jquery/jquery.min.js',
],
'relative' =>
[
'core/assets/vendor/jquery/jquery.min.js',
'base:core/assets/vendor/jquery/jquery.min.js',
],
'external' =>
[
'https://www.example.com/core/assets/vendor/jquery/jquery.min.js',
'https://www.example.com/core/assets/vendor/jquery/jquery.min.js',
],
];
}
}

View File

@ -4,6 +4,7 @@ namespace Drupal\KernelTests\Core\File;
use Drupal\Core\DrupalKernel; use Drupal\Core\DrupalKernel;
use Drupal\Core\File\FileSystemInterface; use Drupal\Core\File\FileSystemInterface;
use Drupal\Core\File\FileUrlGeneratorInterface;
use Drupal\Core\Site\Settings; use Drupal\Core\Site\Settings;
use Drupal\Core\StreamWrapper\PublicStream; use Drupal\Core\StreamWrapper\PublicStream;
use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Request;
@ -98,12 +99,14 @@ class StreamWrapperTest extends FileTestBase {
$config->set('default_scheme', 'private')->save(); $config->set('default_scheme', 'private')->save();
$this->assertEquals('private://foo/bar.txt', file_build_uri('foo/bar.txt'), 'Got a valid URI from foo/bar.txt.'); $this->assertEquals('private://foo/bar.txt', file_build_uri('foo/bar.txt'), 'Got a valid URI from foo/bar.txt.');
// Test file_create_url() // Test FileUrlGeneratorInterface::generateString()
// TemporaryStream::getExternalUrl() uses Url::fromRoute(), which needs // TemporaryStream::getExternalUrl() uses Url::fromRoute(), which needs
// route information to work. // route information to work.
$this->assertStringContainsString('system/temporary?file=test.txt', file_create_url('temporary://test.txt'), 'Temporary external URL correctly built.'); $file_url_generator = $this->container->get('file_url_generator');
$this->assertStringContainsString(Settings::get('file_public_path') . '/test.txt', file_create_url('public://test.txt'), 'Public external URL correctly built.'); assert($file_url_generator instanceof FileUrlGeneratorInterface);
$this->assertStringContainsString('system/files/test.txt', file_create_url('private://test.txt'), 'Private external URL correctly built.'); $this->assertStringContainsString('system/temporary?file=test.txt', $file_url_generator->generateString('temporary://test.txt'), 'Temporary external URL correctly built.');
$this->assertStringContainsString(Settings::get('file_public_path') . '/test.txt', $file_url_generator->generateString('public://test.txt'), 'Public external URL correctly built.');
$this->assertStringContainsString('system/files/test.txt', $file_url_generator->generateString('private://test.txt'), 'Private external URL correctly built.');
} }
/** /**

View File

@ -1,120 +0,0 @@
<?php
namespace Drupal\KernelTests\Core\File;
use Symfony\Component\HttpFoundation\Request;
/**
* Tests for file URL rewriting.
*
* @group File
*/
class UrlRewritingTest extends FileTestBase {
/**
* Modules to enable.
*
* @var array
*/
protected static $modules = ['file_test'];
/**
* Tests the rewriting of shipped file URLs by hook_file_url_alter().
*/
public function testShippedFileURL() {
// Test generating a URL to a shipped file (i.e. a file that is part of
// Drupal core, a module or a theme, for example a JavaScript file).
// Test alteration of file URLs to use a CDN.
\Drupal::state()->set('file_test.hook_file_url_alter', 'cdn');
$filepath = 'core/assets/vendor/jquery/jquery.min.js';
$url = file_create_url($filepath);
$this->assertEquals(FILE_URL_TEST_CDN_1 . '/' . $filepath, $url, 'Correctly generated a CDN URL for a shipped file.');
$filepath = 'core/misc/favicon.ico';
$url = file_create_url($filepath);
$this->assertEquals(FILE_URL_TEST_CDN_2 . '/' . $filepath, $url, 'Correctly generated a CDN URL for a shipped file.');
// Test alteration of file URLs to use root-relative URLs.
\Drupal::state()->set('file_test.hook_file_url_alter', 'root-relative');
$filepath = 'core/assets/vendor/jquery/jquery.min.js';
$url = file_create_url($filepath);
$this->assertEquals(base_path() . '/' . $filepath, $url, 'Correctly generated a root-relative URL for a shipped file.');
$filepath = 'core/misc/favicon.ico';
$url = file_create_url($filepath);
$this->assertEquals(base_path() . '/' . $filepath, $url, 'Correctly generated a root-relative URL for a shipped file.');
// Test alteration of file URLs to use protocol-relative URLs.
\Drupal::state()->set('file_test.hook_file_url_alter', 'protocol-relative');
$filepath = 'core/assets/vendor/jquery/jquery.min.js';
$url = file_create_url($filepath);
$this->assertEquals('/' . base_path() . '/' . $filepath, $url, 'Correctly generated a protocol-relative URL for a shipped file.');
$filepath = 'core/misc/favicon.ico';
$url = file_create_url($filepath);
$this->assertEquals('/' . base_path() . '/' . $filepath, $url, 'Correctly generated a protocol-relative URL for a shipped file.');
// Test alteration of file URLs with query strings and/or fragment.
\Drupal::state()->delete('file_test.hook_file_url_alter');
$filepath = 'core/misc/favicon.ico';
$url = file_create_url($filepath . '?foo');
$this->assertEquals($GLOBALS['base_url'] . '/' . $filepath . '?foo=', $url, 'Correctly generated URL. The query string is present.');
$url = file_create_url($filepath . '?foo=bar');
$this->assertEquals($GLOBALS['base_url'] . '/' . $filepath . '?foo=bar', $url, 'Correctly generated URL. The query string is present.');
$url = file_create_url($filepath . '#v1.2');
$this->assertEquals($GLOBALS['base_url'] . '/' . $filepath . '#v1.2', $url, 'Correctly generated URL. The fragment is present.');
$url = file_create_url($filepath . '?foo=bar#v1.2');
$this->assertEquals($GLOBALS['base_url'] . '/' . $filepath . '?foo=bar#v1.2', $url, 'Correctly generated URL. The query string amd fragment is present.');
}
/**
* Tests the rewriting of public managed file URLs by hook_file_url_alter().
*/
public function testPublicManagedFileURL() {
// Test generating a URL to a managed file.
// Test alteration of file URLs to use a CDN.
\Drupal::state()->set('file_test.hook_file_url_alter', 'cdn');
$uri = $this->createUri();
$url = file_create_url($uri);
$public_directory_path = \Drupal::service('stream_wrapper_manager')->getViaScheme('public')->getDirectoryPath();
/** @var \Drupal\Core\File\FileSystemInterface $file_system */
$file_system = \Drupal::service('file_system');
$this->assertEquals(FILE_URL_TEST_CDN_2 . '/' . $public_directory_path . '/' . $file_system->basename($uri), $url, 'Correctly generated a CDN URL for a created file.');
// Test alteration of file URLs to use root-relative URLs.
\Drupal::state()->set('file_test.hook_file_url_alter', 'root-relative');
$uri = $this->createUri();
$url = file_create_url($uri);
$this->assertEquals(base_path() . '/' . $public_directory_path . '/' . $file_system->basename($uri), $url, 'Correctly generated a root-relative URL for a created file.');
// Test alteration of file URLs to use a protocol-relative URLs.
\Drupal::state()->set('file_test.hook_file_url_alter', 'protocol-relative');
$uri = $this->createUri();
$url = file_create_url($uri);
$this->assertEquals('/' . base_path() . '/' . $public_directory_path . '/' . $file_system->basename($uri), $url, 'Correctly generated a protocol-relative URL for a created file.');
}
/**
* Tests file_url_transform_relative().
*/
public function testRelativeFileURL() {
// Disable file_test.module's hook_file_url_alter() implementation.
\Drupal::state()->set('file_test.hook_file_url_alter', NULL);
// Create a mock Request for file_url_transform_relative().
$request = Request::create($GLOBALS['base_url']);
$this->container->get('request_stack')->push($request);
\Drupal::setContainer($this->container);
// Shipped file.
$filepath = 'core/assets/vendor/jquery/jquery.min.js';
$url = file_create_url($filepath);
$this->assertSame(base_path() . $filepath, file_url_transform_relative($url));
// Managed file.
$uri = $this->createUri();
$url = file_create_url($uri);
$public_directory_path = \Drupal::service('stream_wrapper_manager')->getViaScheme('public')->getDirectoryPath();
$this->assertSame(base_path() . $public_directory_path . '/' . rawurlencode(\Drupal::service('file_system')->basename($uri)), file_url_transform_relative($url));
}
}

View File

@ -15,7 +15,7 @@ class UrlTransformRelativeTest extends KernelTestBase {
protected static $modules = ['file_test']; protected static $modules = ['file_test'];
/** /**
* Tests file_url_transform_relative function. * Tests transformRelative() function.
* *
* @dataProvider providerFileUrlTransformRelative * @dataProvider providerFileUrlTransformRelative
*/ */
@ -37,7 +37,7 @@ class UrlTransformRelativeTest extends KernelTestBase {
$request = Request::createFromGlobals(); $request = Request::createFromGlobals();
\Drupal::requestStack()->push($request); \Drupal::requestStack()->push($request);
$this->assertSame($expected, file_url_transform_relative($url)); $this->assertSame($expected, \Drupal::service('file_url_generator')->transformRelative($url));
} }
public function providerFileUrlTransformRelative() { public function providerFileUrlTransformRelative() {

View File

@ -19,6 +19,13 @@ class ImageTest extends KernelTestBase {
*/ */
protected static $modules = ['system']; protected static $modules = ['system'];
/**
* The file URL generator.
*
* @var \Drupal\Core\File\FileUrlGeneratorInterface
*/
protected $fileUrlGenerator;
/** /**
* The images to test with. * The images to test with.
* *
@ -29,13 +36,15 @@ class ImageTest extends KernelTestBase {
protected function setUp(): void { protected function setUp(): void {
parent::setUp(); parent::setUp();
// The code under test uses file_url_transform_relative(), which relies on // The code under test uses transformRelative(), which relies on
// the Request containing the correct hostname. KernelTestBase doesn't set // the Request containing the correct hostname. KernelTestBase doesn't set
// it, so push another request onto the stack to ensure it's correct. // it, so push another request onto the stack to ensure it's correct.
$request = Request::create('/', 'GET', [], [], [], $_SERVER); $request = Request::create('/', 'GET', [], [], [], $_SERVER);
$this->container = \Drupal::service('kernel')->getContainer(); $this->container = \Drupal::service('kernel')->getContainer();
$this->container->get('request_stack')->push($request); $this->container->get('request_stack')->push($request);
$this->fileUrlGenerator = $this->container->get('file_url_generator');
$this->testImages = [ $this->testImages = [
'core/misc/druplicon.png', 'core/misc/druplicon.png',
'core/misc/loading.gif', 'core/misc/loading.gif',
@ -79,7 +88,9 @@ class ImageTest extends KernelTestBase {
$this->render($image); $this->render($image);
// Make sure the src attribute has the correct value. // Make sure the src attribute has the correct value.
$this->assertRaw(file_url_transform_relative(file_create_url($image['#uri'])), 'Correct output for an image with the src attribute.'); /** @var \Drupal\Core\File\FileUrlGeneratorInterface $this->fileUrlGenerator */
$this->fileUrlGenerator = $this->fileUrlGenerator;
$this->assertRaw($this->fileUrlGenerator->generateString($image['#uri']), 'Correct output for an image with the src attribute.');
} }
/** /**
@ -107,7 +118,7 @@ class ImageTest extends KernelTestBase {
$this->render($image); $this->render($image);
// Make sure the srcset attribute has the correct value. // Make sure the srcset attribute has the correct value.
$this->assertRaw(file_url_transform_relative(file_create_url($this->testImages[0])) . ' 1x, ' . file_url_transform_relative(file_create_url($this->testImages[1])) . ' 2x', 'Correct output for image with srcset attribute and multipliers.'); $this->assertRaw($this->fileUrlGenerator->transformRelative($this->fileUrlGenerator->generateString($this->testImages[0])) . ' 1x, ' . $this->fileUrlGenerator->transformRelative($this->fileUrlGenerator->generateString($this->testImages[1])) . ' 2x', 'Correct output for image with srcset attribute and multipliers.');
} }
/** /**
@ -139,7 +150,7 @@ class ImageTest extends KernelTestBase {
$this->render($image); $this->render($image);
// Make sure the srcset attribute has the correct value. // Make sure the srcset attribute has the correct value.
$this->assertRaw(file_url_transform_relative(file_create_url($this->testImages[0])) . ' ' . $widths[0] . ', ' . file_url_transform_relative(file_create_url($this->testImages[1])) . ' ' . $widths[1], 'Correct output for image with srcset attribute and width descriptors.'); $this->assertRaw($this->fileUrlGenerator->generateString($this->testImages[0]) . ' ' . $widths[0] . ', ' . $this->fileUrlGenerator->transformRelative($this->fileUrlGenerator->generateString($this->testImages[1])) . ' ' . $widths[1], 'Correct output for image with srcset attribute and width descriptors.');
} }
} }

View File

@ -85,7 +85,9 @@ class ThemeSettingsTest extends KernelTestBase {
theme_settings_convert_to_config($values, $config)->save(); theme_settings_convert_to_config($values, $config)->save();
// Tests logo path with scheme. // Tests logo path with scheme.
$expected = file_url_transform_relative(file_create_url('public://logo_with_scheme.png')); /** @var \Drupal\Core\File\FileUrlGeneratorInterface $file_url_generator */
$file_url_generator = \Drupal::service('file_url_generator');
$expected = $file_url_generator->generateString('public://logo_with_scheme.png');
$this->assertEquals($expected, theme_get_setting('logo.url', 'stark')); $this->assertEquals($expected, theme_get_setting('logo.url', 'stark'));
$values = [ $values = [

View File

@ -123,8 +123,8 @@ trait BrowserHtmlDebugTrait {
$html_output_filename = $this->htmlOutputClassName . '-' . $this->htmlOutputCounter . '-' . $this->htmlOutputTestId . '.html'; $html_output_filename = $this->htmlOutputClassName . '-' . $this->htmlOutputCounter . '-' . $this->htmlOutputTestId . '.html';
file_put_contents($this->htmlOutputDirectory . '/' . $html_output_filename, $message); file_put_contents($this->htmlOutputDirectory . '/' . $html_output_filename, $message);
file_put_contents($this->htmlOutputCounterStorage, $this->htmlOutputCounter++); file_put_contents($this->htmlOutputCounterStorage, $this->htmlOutputCounter++);
// Do not use file_create_url() as the module_handler service might not be // Do not use the file_url_generator service as the module_handler service
// available. // might not be available.
$uri = $this->htmlOutputBaseUrl . '/sites/simpletest/browser_output/' . $html_output_filename; $uri = $this->htmlOutputBaseUrl . '/sites/simpletest/browser_output/' . $html_output_filename;
file_put_contents($this->htmlOutputFile, $uri . "\n", FILE_APPEND); file_put_contents($this->htmlOutputFile, $uri . "\n", FILE_APPEND);
} }

View File

@ -3,6 +3,7 @@
namespace Drupal\Tests\Core\Asset; namespace Drupal\Tests\Core\Asset;
use Drupal\Core\Asset\CssCollectionRenderer; use Drupal\Core\Asset\CssCollectionRenderer;
use Drupal\Core\File\FileUrlGeneratorInterface;
use Drupal\Tests\UnitTestCase; use Drupal\Tests\UnitTestCase;
use Drupal\Core\State\StateInterface; use Drupal\Core\State\StateInterface;
@ -33,8 +34,15 @@ class CssCollectionRendererUnitTest extends UnitTestCase {
protected function setUp(): void { protected function setUp(): void {
parent::setUp(); parent::setUp();
$state = $this->prophesize(StateInterface::class); $state = $this->prophesize(StateInterface::class);
$file_url_generator = $this->createMock(FileUrlGeneratorInterface::class);
$file_url_generator->expects($this->any())
->method('generateString')
->with($this->isType('string'))
->willReturnCallback(function ($uri) {
return 'generated-relative-url:' . $uri;
});
$state->get('system.css_js_query_string', '0')->shouldBeCalledOnce()->willReturn(NULL); $state->get('system.css_js_query_string', '0')->shouldBeCalledOnce()->willReturn(NULL);
$this->renderer = new CssCollectionRenderer($state->reveal()); $this->renderer = new CssCollectionRenderer($state->reveal(), $file_url_generator);
$this->fileCssGroup = [ $this->fileCssGroup = [
'group' => -100, 'group' => -100,
'type' => 'file', 'type' => 'file',
@ -110,7 +118,7 @@ class CssCollectionRendererUnitTest extends UnitTestCase {
0 => ['group' => 0, 'type' => 'file', 'media' => 'all', 'preprocess' => TRUE, 'data' => 'public://css/file-all', 'browsers' => []], 0 => ['group' => 0, 'type' => 'file', 'media' => 'all', 'preprocess' => TRUE, 'data' => 'public://css/file-all', 'browsers' => []],
], ],
[ [
0 => $create_link_element(file_url_transform_relative(file_create_url('public://css/file-all')) . '?', 'all'), 0 => $create_link_element('generated-relative-url:public://css/file-all' . '?', 'all'),
], ],
], ],
// Single file CSS asset with custom attributes. // Single file CSS asset with custom attributes.
@ -119,7 +127,7 @@ class CssCollectionRendererUnitTest extends UnitTestCase {
0 => ['group' => 0, 'type' => 'file', 'media' => 'all', 'preprocess' => TRUE, 'data' => 'public://css/file-all', 'browsers' => [], 'attributes' => $custom_attributes], 0 => ['group' => 0, 'type' => 'file', 'media' => 'all', 'preprocess' => TRUE, 'data' => 'public://css/file-all', 'browsers' => [], 'attributes' => $custom_attributes],
], ],
[ [
0 => $create_link_element(file_url_transform_relative(file_create_url('public://css/file-all')) . '?', 'all', [], $custom_attributes), 0 => $create_link_element('generated-relative-url:public://css/file-all' . '?', 'all', [], $custom_attributes),
], ],
], ],
// 31 file CSS assets: expect 31 link elements. // 31 file CSS assets: expect 31 link elements.
@ -158,37 +166,37 @@ class CssCollectionRendererUnitTest extends UnitTestCase {
30 => $create_file_css_asset('public://css/31.css'), 30 => $create_file_css_asset('public://css/31.css'),
], ],
[ [
0 => $create_link_element(file_url_transform_relative(file_create_url('public://css/1.css')) . '?'), 0 => $create_link_element('generated-relative-url:public://css/1.css' . '?'),
1 => $create_link_element(file_url_transform_relative(file_create_url('public://css/2.css')) . '?'), 1 => $create_link_element('generated-relative-url:public://css/2.css' . '?'),
2 => $create_link_element(file_url_transform_relative(file_create_url('public://css/3.css')) . '?'), 2 => $create_link_element('generated-relative-url:public://css/3.css' . '?'),
3 => $create_link_element(file_url_transform_relative(file_create_url('public://css/4.css')) . '?'), 3 => $create_link_element('generated-relative-url:public://css/4.css' . '?'),
4 => $create_link_element(file_url_transform_relative(file_create_url('public://css/5.css')) . '?'), 4 => $create_link_element('generated-relative-url:public://css/5.css' . '?'),
5 => $create_link_element(file_url_transform_relative(file_create_url('public://css/6.css')) . '?'), 5 => $create_link_element('generated-relative-url:public://css/6.css' . '?'),
6 => $create_link_element(file_url_transform_relative(file_create_url('public://css/7.css')) . '?'), 6 => $create_link_element('generated-relative-url:public://css/7.css' . '?'),
7 => $create_link_element(file_url_transform_relative(file_create_url('public://css/8.css')) . '?'), 7 => $create_link_element('generated-relative-url:public://css/8.css' . '?'),
8 => $create_link_element(file_url_transform_relative(file_create_url('public://css/9.css')) . '?'), 8 => $create_link_element('generated-relative-url:public://css/9.css' . '?'),
9 => $create_link_element(file_url_transform_relative(file_create_url('public://css/10.css')) . '?'), 9 => $create_link_element('generated-relative-url:public://css/10.css' . '?'),
10 => $create_link_element(file_url_transform_relative(file_create_url('public://css/11.css')) . '?'), 10 => $create_link_element('generated-relative-url:public://css/11.css' . '?'),
11 => $create_link_element(file_url_transform_relative(file_create_url('public://css/12.css')) . '?'), 11 => $create_link_element('generated-relative-url:public://css/12.css' . '?'),
12 => $create_link_element(file_url_transform_relative(file_create_url('public://css/13.css')) . '?'), 12 => $create_link_element('generated-relative-url:public://css/13.css' . '?'),
13 => $create_link_element(file_url_transform_relative(file_create_url('public://css/14.css')) . '?'), 13 => $create_link_element('generated-relative-url:public://css/14.css' . '?'),
14 => $create_link_element(file_url_transform_relative(file_create_url('public://css/15.css')) . '?'), 14 => $create_link_element('generated-relative-url:public://css/15.css' . '?'),
15 => $create_link_element(file_url_transform_relative(file_create_url('public://css/16.css')) . '?'), 15 => $create_link_element('generated-relative-url:public://css/16.css' . '?'),
16 => $create_link_element(file_url_transform_relative(file_create_url('public://css/17.css')) . '?'), 16 => $create_link_element('generated-relative-url:public://css/17.css' . '?'),
17 => $create_link_element(file_url_transform_relative(file_create_url('public://css/18.css')) . '?'), 17 => $create_link_element('generated-relative-url:public://css/18.css' . '?'),
18 => $create_link_element(file_url_transform_relative(file_create_url('public://css/19.css')) . '?'), 18 => $create_link_element('generated-relative-url:public://css/19.css' . '?'),
19 => $create_link_element(file_url_transform_relative(file_create_url('public://css/20.css')) . '?'), 19 => $create_link_element('generated-relative-url:public://css/20.css' . '?'),
20 => $create_link_element(file_url_transform_relative(file_create_url('public://css/21.css')) . '?'), 20 => $create_link_element('generated-relative-url:public://css/21.css' . '?'),
21 => $create_link_element(file_url_transform_relative(file_create_url('public://css/22.css')) . '?'), 21 => $create_link_element('generated-relative-url:public://css/22.css' . '?'),
22 => $create_link_element(file_url_transform_relative(file_create_url('public://css/23.css')) . '?'), 22 => $create_link_element('generated-relative-url:public://css/23.css' . '?'),
23 => $create_link_element(file_url_transform_relative(file_create_url('public://css/24.css')) . '?'), 23 => $create_link_element('generated-relative-url:public://css/24.css' . '?'),
24 => $create_link_element(file_url_transform_relative(file_create_url('public://css/25.css')) . '?'), 24 => $create_link_element('generated-relative-url:public://css/25.css' . '?'),
25 => $create_link_element(file_url_transform_relative(file_create_url('public://css/26.css')) . '?'), 25 => $create_link_element('generated-relative-url:public://css/26.css' . '?'),
26 => $create_link_element(file_url_transform_relative(file_create_url('public://css/27.css')) . '?'), 26 => $create_link_element('generated-relative-url:public://css/27.css' . '?'),
27 => $create_link_element(file_url_transform_relative(file_create_url('public://css/28.css')) . '?'), 27 => $create_link_element('generated-relative-url:public://css/28.css' . '?'),
28 => $create_link_element(file_url_transform_relative(file_create_url('public://css/29.css')) . '?'), 28 => $create_link_element('generated-relative-url:public://css/29.css' . '?'),
29 => $create_link_element(file_url_transform_relative(file_create_url('public://css/30.css')) . '?'), 29 => $create_link_element('generated-relative-url:public://css/30.css' . '?'),
30 => $create_link_element(file_url_transform_relative(file_create_url('public://css/31.css')) . '?'), 30 => $create_link_element('generated-relative-url:public://css/31.css' . '?'),
], ],
], ],
// 32 file CSS assets with the same properties, except for the 10th and // 32 file CSS assets with the same properties, except for the 10th and
@ -229,38 +237,38 @@ class CssCollectionRendererUnitTest extends UnitTestCase {
31 => $create_file_css_asset('public://css/32.css'), 31 => $create_file_css_asset('public://css/32.css'),
], ],
[ [
0 => $create_link_element(file_url_transform_relative(file_create_url('public://css/1.css')) . '?'), 0 => $create_link_element('generated-relative-url:public://css/1.css' . '?'),
1 => $create_link_element(file_url_transform_relative(file_create_url('public://css/2.css')) . '?'), 1 => $create_link_element('generated-relative-url:public://css/2.css' . '?'),
2 => $create_link_element(file_url_transform_relative(file_create_url('public://css/3.css')) . '?'), 2 => $create_link_element('generated-relative-url:public://css/3.css' . '?'),
3 => $create_link_element(file_url_transform_relative(file_create_url('public://css/4.css')) . '?'), 3 => $create_link_element('generated-relative-url:public://css/4.css' . '?'),
4 => $create_link_element(file_url_transform_relative(file_create_url('public://css/5.css')) . '?'), 4 => $create_link_element('generated-relative-url:public://css/5.css' . '?'),
5 => $create_link_element(file_url_transform_relative(file_create_url('public://css/6.css')) . '?'), 5 => $create_link_element('generated-relative-url:public://css/6.css' . '?'),
6 => $create_link_element(file_url_transform_relative(file_create_url('public://css/7.css')) . '?'), 6 => $create_link_element('generated-relative-url:public://css/7.css' . '?'),
7 => $create_link_element(file_url_transform_relative(file_create_url('public://css/8.css')) . '?'), 7 => $create_link_element('generated-relative-url:public://css/8.css' . '?'),
8 => $create_link_element(file_url_transform_relative(file_create_url('public://css/9.css')) . '?'), 8 => $create_link_element('generated-relative-url:public://css/9.css' . '?'),
9 => $create_link_element(file_url_transform_relative(file_create_url('public://css/10.css')) . '?', 'screen'), 9 => $create_link_element('generated-relative-url:public://css/10.css' . '?', 'screen'),
10 => $create_link_element(file_url_transform_relative(file_create_url('public://css/11.css')) . '?'), 10 => $create_link_element('generated-relative-url:public://css/11.css' . '?'),
11 => $create_link_element(file_url_transform_relative(file_create_url('public://css/12.css')) . '?'), 11 => $create_link_element('generated-relative-url:public://css/12.css' . '?'),
12 => $create_link_element(file_url_transform_relative(file_create_url('public://css/13.css')) . '?'), 12 => $create_link_element('generated-relative-url:public://css/13.css' . '?'),
13 => $create_link_element(file_url_transform_relative(file_create_url('public://css/14.css')) . '?'), 13 => $create_link_element('generated-relative-url:public://css/14.css' . '?'),
14 => $create_link_element(file_url_transform_relative(file_create_url('public://css/15.css')) . '?'), 14 => $create_link_element('generated-relative-url:public://css/15.css' . '?'),
15 => $create_link_element(file_url_transform_relative(file_create_url('public://css/16.css')) . '?'), 15 => $create_link_element('generated-relative-url:public://css/16.css' . '?'),
16 => $create_link_element(file_url_transform_relative(file_create_url('public://css/17.css')) . '?'), 16 => $create_link_element('generated-relative-url:public://css/17.css' . '?'),
17 => $create_link_element(file_url_transform_relative(file_create_url('public://css/18.css')) . '?'), 17 => $create_link_element('generated-relative-url:public://css/18.css' . '?'),
18 => $create_link_element(file_url_transform_relative(file_create_url('public://css/19.css')) . '?'), 18 => $create_link_element('generated-relative-url:public://css/19.css' . '?'),
19 => $create_link_element(file_url_transform_relative(file_create_url('public://css/20.css')) . '?', 'print'), 19 => $create_link_element('generated-relative-url:public://css/20.css' . '?', 'print'),
20 => $create_link_element(file_url_transform_relative(file_create_url('public://css/21.css')) . '?'), 20 => $create_link_element('generated-relative-url:public://css/21.css' . '?'),
21 => $create_link_element(file_url_transform_relative(file_create_url('public://css/22.css')) . '?'), 21 => $create_link_element('generated-relative-url:public://css/22.css' . '?'),
22 => $create_link_element(file_url_transform_relative(file_create_url('public://css/23.css')) . '?'), 22 => $create_link_element('generated-relative-url:public://css/23.css' . '?'),
23 => $create_link_element(file_url_transform_relative(file_create_url('public://css/24.css')) . '?'), 23 => $create_link_element('generated-relative-url:public://css/24.css' . '?'),
24 => $create_link_element(file_url_transform_relative(file_create_url('public://css/25.css')) . '?'), 24 => $create_link_element('generated-relative-url:public://css/25.css' . '?'),
25 => $create_link_element(file_url_transform_relative(file_create_url('public://css/26.css')) . '?'), 25 => $create_link_element('generated-relative-url:public://css/26.css' . '?'),
26 => $create_link_element(file_url_transform_relative(file_create_url('public://css/27.css')) . '?'), 26 => $create_link_element('generated-relative-url:public://css/27.css' . '?'),
27 => $create_link_element(file_url_transform_relative(file_create_url('public://css/28.css')) . '?'), 27 => $create_link_element('generated-relative-url:public://css/28.css' . '?'),
28 => $create_link_element(file_url_transform_relative(file_create_url('public://css/29.css')) . '?'), 28 => $create_link_element('generated-relative-url:public://css/29.css' . '?'),
29 => $create_link_element(file_url_transform_relative(file_create_url('public://css/30.css')) . '?'), 29 => $create_link_element('generated-relative-url:public://css/30.css' . '?'),
30 => $create_link_element(file_url_transform_relative(file_create_url('public://css/31.css')) . '?'), 30 => $create_link_element('generated-relative-url:public://css/31.css' . '?'),
31 => $create_link_element(file_url_transform_relative(file_create_url('public://css/32.css')) . '?'), 31 => $create_link_element('generated-relative-url:public://css/32.css' . '?'),
], ],
], ],
]; ];
@ -294,57 +302,3 @@ class CssCollectionRendererUnitTest extends UnitTestCase {
} }
} }
/**
* Temporary mock for file_create_url(), until that is moved into
* Component/Utility.
*/
if (!function_exists('Drupal\Tests\Core\Asset\file_create_url')) {
function file_create_url($uri) {
return 'file_create_url:' . $uri;
}
}
/**
* Temporary mock of file_url_transform_relative, until that is moved into
* Component/Utility.
*/
if (!function_exists('Drupal\Tests\Core\Asset\file_url_transform_relative')) {
function file_url_transform_relative($uri) {
return 'file_url_transform_relative:' . $uri;
}
}
/**
* CssCollectionRenderer uses file_create_url() & file_url_transform_relative(),
* which *are* available when using the Simpletest test runner, but not when
* using the PHPUnit test runner; hence this hack.
*/
namespace Drupal\Core\Asset;
if (!function_exists('Drupal\Core\Asset\file_create_url')) {
/**
* Temporary mock for file_create_url(), until that is moved into
* Component/Utility.
*/
function file_create_url($uri) {
return \Drupal\Tests\Core\Asset\file_create_url($uri);
}
}
if (!function_exists('Drupal\Core\Asset\file_url_transform_relative')) {
/**
* Temporary mock of file_url_transform_relative, until that is moved into
* Component/Utility.
*/
function file_url_transform_relative($uri) {
return \Drupal\Tests\Core\Asset\file_url_transform_relative($uri);
}
}

View File

@ -3,6 +3,7 @@
namespace Drupal\Tests\Core\Asset; namespace Drupal\Tests\Core\Asset;
use Drupal\Core\Asset\CssOptimizer; use Drupal\Core\Asset\CssOptimizer;
use Drupal\Core\File\FileUrlGeneratorInterface;
use Drupal\Tests\UnitTestCase; use Drupal\Tests\UnitTestCase;
/** /**
@ -24,10 +25,23 @@ class CssOptimizerUnitTest extends UnitTestCase {
*/ */
protected $optimizer; protected $optimizer;
/**
* The file URL generator mock.
*
* @var \Drupal\Core\File\FileUrlGeneratorInterface|\PHPUnit_Framework_MockObject_MockObject
*/
protected $fileUrlGenerator;
protected function setUp(): void { protected function setUp(): void {
parent::setUp(); parent::setUp();
$this->fileUrlGenerator = $this->createMock(FileUrlGeneratorInterface::class);
$this->optimizer = new CssOptimizer(); $this->fileUrlGenerator->expects($this->any())
->method('generateString')
->with($this->isType('string'))
->willReturnCallback(function ($uri) {
return 'generated-relative-url:' . $uri;
});
$this->optimizer = new CssOptimizer($this->fileUrlGenerator);
} }
/** /**
@ -58,7 +72,8 @@ class CssOptimizerUnitTest extends UnitTestCase {
// - Proper URLs in imported files. (https://www.drupal.org/node/265719) // - Proper URLs in imported files. (https://www.drupal.org/node/265719)
// - A background image with relative paths, which must be rewritten. // - A background image with relative paths, which must be rewritten.
// - The rewritten background image path must also be passed through // - The rewritten background image path must also be passed through
// file_create_url(). (https://www.drupal.org/node/1961340) // FileUrlGeneratorInterface::generate().
// (https://www.drupal.org/node/1961340)
// - Imported files that are external (protocol-relative URL or not) // - Imported files that are external (protocol-relative URL or not)
// should not be expanded. (https://www.drupal.org/node/2014851) // should not be expanded. (https://www.drupal.org/node/2014851)
[ [
@ -72,7 +87,7 @@ class CssOptimizerUnitTest extends UnitTestCase {
'browsers' => ['IE' => TRUE, '!IE' => TRUE], 'browsers' => ['IE' => TRUE, '!IE' => TRUE],
'basename' => 'css_input_with_import.css', 'basename' => 'css_input_with_import.css',
], ],
str_replace('url(images/icon.png)', 'url(' . file_url_transform_relative(file_create_url($path . 'images/icon.png')) . ')', file_get_contents($absolute_path . 'css_input_with_import.css.optimized.css')), str_replace('url(images/icon.png)', 'url(generated-relative-url:' . $path . 'images/icon.png)', file_get_contents($absolute_path . 'css_input_with_import.css.optimized.css')),
], ],
// File. Tests: // File. Tests:
// - Retain comment hacks. // - Retain comment hacks.
@ -104,7 +119,7 @@ class CssOptimizerUnitTest extends UnitTestCase {
'browsers' => ['IE' => TRUE, '!IE' => TRUE], 'browsers' => ['IE' => TRUE, '!IE' => TRUE],
'basename' => 'css_input_with_import.css', 'basename' => 'css_input_with_import.css',
], ],
str_replace('url(../images/icon.png)', 'url(' . file_url_transform_relative(file_create_url($path . 'images/icon.png')) . ')', file_get_contents($absolute_path . 'css_subfolder/css_input_with_import.css.optimized.css')), str_replace('url(../images/icon.png)', 'url(generated-relative-url:' . $path . 'images/icon.png)', file_get_contents($absolute_path . 'css_subfolder/css_input_with_import.css.optimized.css')),
], ],
// File. Tests: // File. Tests:
// - Any @charset declaration at the beginning of a file should be // - Any @charset declaration at the beginning of a file should be
@ -267,58 +282,10 @@ class CssOptimizerUnitTest extends UnitTestCase {
} }
/** /**
* Temporary mock for file_create_url(), until that is moved into * CssCollectionRenderer uses file_uri_scheme() which need to be mocked.
* Component/Utility.
*/
if (!function_exists('Drupal\Tests\Core\Asset\file_create_url')) {
function file_create_url($uri) {
return 'file_create_url:' . $uri;
}
}
/**
* Temporary mock of file_url_transform_relative, until that is moved into
* Component/Utility.
*/
if (!function_exists('Drupal\Tests\Core\Asset\file_url_transform_relative')) {
function file_url_transform_relative($uri) {
return 'file_url_transform_relative:' . $uri;
}
}
/**
* CssCollectionRenderer uses file_create_url() & file_url_transform_relative(),
* which *are* available when using the Simpletest test runner, but not when
* using the PHPUnit test runner; hence this hack.
*/ */
namespace Drupal\Core\Asset; namespace Drupal\Core\Asset;
if (!function_exists('Drupal\Core\Asset\file_create_url')) {
/**
* Temporary mock for file_create_url(), until that is moved into
* Component/Utility.
*/
function file_create_url($uri) {
return \Drupal\Tests\Core\Asset\file_create_url($uri);
}
}
if (!function_exists('Drupal\Core\Asset\file_url_transform_relative')) {
/**
* Temporary mock of file_url_transform_relative, until that is moved into
* Component/Utility.
*/
function file_url_transform_relative($uri) {
return \Drupal\Tests\Core\Asset\file_url_transform_relative($uri);
}
}
if (!function_exists('Drupal\Core\Asset\file_uri_scheme')) { if (!function_exists('Drupal\Core\Asset\file_uri_scheme')) {
function file_uri_scheme($uri) { function file_uri_scheme($uri) {

View File

@ -2,6 +2,7 @@
namespace Drupal\Tests\Core\Template; namespace Drupal\Tests\Core\Template;
use Drupal\Core\File\FileUrlGeneratorInterface;
use Drupal\Core\GeneratedLink; use Drupal\Core\GeneratedLink;
use Drupal\Core\Render\RenderableInterface; use Drupal\Core\Render\RenderableInterface;
use Drupal\Core\StringTranslation\TranslatableMarkup; use Drupal\Core\StringTranslation\TranslatableMarkup;
@ -60,6 +61,13 @@ class TwigExtensionTest extends UnitTestCase {
*/ */
protected $systemUnderTest; protected $systemUnderTest;
/**
* The file URL generator mock.
*
* @var \Drupal\Core\File\FileUrlGeneratorInterface|\PHPUnit_Framework_MockObject_MockObject
*/
protected $fileUrlGenerator;
/** /**
* {@inheritdoc} * {@inheritdoc}
*/ */
@ -70,8 +78,9 @@ class TwigExtensionTest extends UnitTestCase {
$this->urlGenerator = $this->createMock('\Drupal\Core\Routing\UrlGeneratorInterface'); $this->urlGenerator = $this->createMock('\Drupal\Core\Routing\UrlGeneratorInterface');
$this->themeManager = $this->createMock('\Drupal\Core\Theme\ThemeManagerInterface'); $this->themeManager = $this->createMock('\Drupal\Core\Theme\ThemeManagerInterface');
$this->dateFormatter = $this->createMock('\Drupal\Core\Datetime\DateFormatterInterface'); $this->dateFormatter = $this->createMock('\Drupal\Core\Datetime\DateFormatterInterface');
$this->fileUrlGenerator = $this->createMock(FileUrlGeneratorInterface::class);
$this->systemUnderTest = new TwigExtension($this->renderer, $this->urlGenerator, $this->themeManager, $this->dateFormatter); $this->systemUnderTest = new TwigExtension($this->renderer, $this->urlGenerator, $this->themeManager, $this->dateFormatter, $this->fileUrlGenerator);
} }
/** /**
@ -167,6 +176,22 @@ class TwigExtensionTest extends UnitTestCase {
$this->assertEquals('1978-11-19', $result); $this->assertEquals('1978-11-19', $result);
} }
/**
* Tests the file_url filter.
*/
public function testFileUrl() {
$this->fileUrlGenerator->expects($this->once())
->method('generateString')
->with('public://picture.jpg')
->willReturn('sites/default/files/picture.jpg');
$loader = new StringLoader();
$twig = new Environment($loader);
$twig->addExtension($this->systemUnderTest);
$result = $twig->render('{{ file_url(file) }}', ['file' => 'public://picture.jpg']);
$this->assertEquals('sites/default/files/picture.jpg', $result);
}
/** /**
* Tests the active_theme_path function. * Tests the active_theme_path function.
*/ */