Commit 4a320ea0 authored by catch's avatar catch

Issue #1494670 by Liam Morland, jhedstrom, Wim Leers, mfb: References to CSS,...

Issue #1494670 by Liam Morland, jhedstrom, Wim Leers, mfb: References to CSS, JS, and similar files should be root-relative URLs: avoids mixed content warnings & fewer bytes to send
parent 758a81e6
...@@ -343,10 +343,10 @@ function theme_get_setting($setting_name, $theme = NULL) { ...@@ -343,10 +343,10 @@ function theme_get_setting($setting_name, $theme = NULL) {
// 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')) {
$cache[$theme]->set('logo.url', file_create_url($theme_object->getPath() . '/logo.svg')); $cache[$theme]->set('logo.url', file_url_transform_relative(file_create_url($theme_object->getPath() . '/logo.svg')));
} }
elseif ($logo_path = $cache[$theme]->get('logo.path')) { elseif ($logo_path = $cache[$theme]->get('logo.path')) {
$cache[$theme]->set('logo.url', file_create_url($logo_path)); $cache[$theme]->set('logo.url', file_url_transform_relative(file_create_url($logo_path)));
} }
// Generate the path to the favicon. // Generate the path to the favicon.
...@@ -354,14 +354,14 @@ function theme_get_setting($setting_name, $theme = NULL) { ...@@ -354,14 +354,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_create_url($favicon)); $cache[$theme]->set('favicon.url', file_url_transform_relative(file_create_url($favicon)));
} }
else { else {
$cache[$theme]->set('favicon.url', file_create_url('core/misc/favicon.ico')); $cache[$theme]->set('favicon.url', file_url_transform_relative(file_create_url('core/misc/favicon.ico')));
} }
} }
elseif ($favicon_path) { elseif ($favicon_path) {
$cache[$theme]->set('favicon.url', file_create_url($favicon_path)); $cache[$theme]->set('favicon.url', file_url_transform_relative(file_create_url($favicon_path)));
} }
else { else {
$cache[$theme]->set('features.favicon', FALSE); $cache[$theme]->set('features.favicon', FALSE);
...@@ -756,7 +756,7 @@ function template_preprocess_links(&$variables) { ...@@ -756,7 +756,7 @@ function template_preprocess_links(&$variables) {
*/ */
function template_preprocess_image(&$variables) { function template_preprocess_image(&$variables) {
if (!empty($variables['uri'])) { if (!empty($variables['uri'])) {
$variables['attributes']['src'] = file_create_url($variables['uri']); $variables['attributes']['src'] = file_url_transform_relative(file_create_url($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
...@@ -764,7 +764,7 @@ function template_preprocess_image(&$variables) { ...@@ -764,7 +764,7 @@ function template_preprocess_image(&$variables) {
$srcset = array(); $srcset = array();
foreach ($variables['srcset'] as $src) { foreach ($variables['srcset'] as $src) {
// URI is mandatory. // URI is mandatory.
$source = file_create_url($src['uri']); $source = file_url_transform_relative(file_create_url($src['uri']));
if (isset($src['width']) && !empty($src['width'])) { if (isset($src['width']) && !empty($src['width'])) {
$source .= ' ' . $src['width']; $source .= ' ' . $src['width'];
} }
......
...@@ -135,7 +135,7 @@ public function render(array $css_assets) { ...@@ -135,7 +135,7 @@ public function render(array $css_assets) {
// assets: output a LINK tag for a file CSS asset. // assets: output a LINK tag for a file CSS asset.
if (count($css_assets) <= 31) { if (count($css_assets) <= 31) {
$element = $link_element_defaults; $element = $link_element_defaults;
$element['#attributes']['href'] = file_create_url($css_asset['data']) . $query_string_separator . $query_string; $element['#attributes']['href'] = file_url_transform_relative(file_create_url($css_asset['data'])) . $query_string_separator . $query_string;
$element['#attributes']['media'] = $css_asset['media']; $element['#attributes']['media'] = $css_asset['media'];
$element['#browsers'] = $css_asset['browsers']; $element['#browsers'] = $css_asset['browsers'];
$elements[] = $element; $elements[] = $element;
...@@ -148,7 +148,7 @@ public function render(array $css_assets) { ...@@ -148,7 +148,7 @@ public function render(array $css_assets) {
// LINK tag. // LINK tag.
if (!$css_asset['preprocess']) { if (!$css_asset['preprocess']) {
$element = $link_element_defaults; $element = $link_element_defaults;
$element['#attributes']['href'] = file_create_url($css_asset['data']) . $query_string_separator . $query_string; $element['#attributes']['href'] = file_url_transform_relative(file_create_url($css_asset['data'])) . $query_string_separator . $query_string;
$element['#attributes']['media'] = $css_asset['media']; $element['#attributes']['media'] = $css_asset['media'];
$element['#browsers'] = $css_asset['browsers']; $element['#browsers'] = $css_asset['browsers'];
$elements[] = $element; $elements[] = $element;
...@@ -168,7 +168,7 @@ public function render(array $css_assets) { ...@@ -168,7 +168,7 @@ public function render(array $css_assets) {
// control browser-caching. IE7 does not support a media type on // control browser-caching. IE7 does not support a media type on
// the @import statement, so we instead specify the media for // the @import statement, so we instead specify the media for
// the group on the STYLE tag. // the group on the STYLE tag.
$import[] = '@import url("' . Html::escape(file_create_url($next_css_asset['data']) . '?' . $query_string) . '");'; $import[] = '@import url("' . Html::escape(file_url_transform_relative(file_create_url($next_css_asset['data'])) . '?' . $query_string) . '");';
// Move the outer for loop skip the next item, since we // Move the outer for loop skip the next item, since we
// processed it here. // processed it here.
$i = $j; $i = $j;
......
...@@ -265,7 +265,7 @@ public function rewriteFileURI($matches) { ...@@ -265,7 +265,7 @@ public function rewriteFileURI($matches) {
$last = $path; $last = $path;
$path = preg_replace('`(^|/)(?!\.\./)([^/]+)/\.\./`', '$1', $path); $path = preg_replace('`(^|/)(?!\.\./)([^/]+)/\.\./`', '$1', $path);
} }
return 'url(' . file_create_url($path) . ')'; return 'url(' . file_url_transform_relative(file_create_url($path)) . ')';
} }
} }
...@@ -79,7 +79,7 @@ public function render(array $js_assets) { ...@@ -79,7 +79,7 @@ public function render(array $js_assets) {
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_create_url($js_asset['data']); $element['#attributes']['src'] = file_url_transform_relative(file_create_url($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'])) {
......
...@@ -75,7 +75,7 @@ public static function preRenderButton($element) { ...@@ -75,7 +75,7 @@ public static function preRenderButton($element) {
$element['#attributes']['type'] = 'image'; $element['#attributes']['type'] = 'image';
Element::setAttributes($element, array('id', 'name', 'value')); Element::setAttributes($element, array('id', 'name', 'value'));
$element['#attributes']['src'] = file_create_url($element['#src']); $element['#attributes']['src'] = file_url_transform_relative(file_create_url($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'];
......
...@@ -135,7 +135,9 @@ public function getFunctions() { ...@@ -135,7 +135,9 @@ public function getFunctions() {
new \Twig_SimpleFunction('url', array($this, 'getUrl'), array('is_safe_callback' => array($this, 'isUrlGenerationSafe'))), new \Twig_SimpleFunction('url', array($this, 'getUrl'), array('is_safe_callback' => array($this, 'isUrlGenerationSafe'))),
new \Twig_SimpleFunction('path', array($this, 'getPath'), array('is_safe_callback' => array($this, 'isUrlGenerationSafe'))), new \Twig_SimpleFunction('path', array($this, 'getPath'), array('is_safe_callback' => array($this, 'isUrlGenerationSafe'))),
new \Twig_SimpleFunction('link', array($this, 'getLink')), new \Twig_SimpleFunction('link', array($this, 'getLink')),
new \Twig_SimpleFunction('file_url', 'file_create_url'), new \Twig_SimpleFunction('file_url', function ($uri) {
return file_url_transform_relative(file_create_url($uri));
}),
new \Twig_SimpleFunction('attach_library', [$this, 'attachLibrary']), new \Twig_SimpleFunction('attach_library', [$this, 'attachLibrary']),
new \Twig_SimpleFunction('active_theme_path', [$this, 'getActiveThemePath']), new \Twig_SimpleFunction('active_theme_path', [$this, 'getActiveThemePath']),
new \Twig_SimpleFunction('active_theme', [$this, 'getActiveTheme']), new \Twig_SimpleFunction('active_theme', [$this, 'getActiveTheme']),
......
...@@ -298,8 +298,11 @@ public function getJSSettings(EditorEntity $editor) { ...@@ -298,8 +298,11 @@ public function getJSSettings(EditorEntity $editor) {
); );
// 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 += array( $settings += array(
'drupalExternalPlugins' => array_map('file_create_url', $external_plugin_files), 'drupalExternalPlugins' => array_map($root_relative_file_url, $external_plugin_files),
); );
// Parse all CKEditor plugin JavaScript files for translations. // Parse all CKEditor plugin JavaScript files for translations.
...@@ -421,6 +424,7 @@ public function buildContentsCssJSSetting(EditorEntity $editor) { ...@@ -421,6 +424,7 @@ public function buildContentsCssJSSetting(EditorEntity $editor) {
$this->moduleHandler->alter('ckeditor_css', $css, $editor); $this->moduleHandler->alter('ckeditor_css', $css, $editor);
$css = array_merge($css, _ckeditor_theme_css()); $css = array_merge($css, _ckeditor_theme_css());
$css = array_map('file_create_url', $css); $css = array_map('file_create_url', $css);
$css = array_map('file_url_transform_relative', $css);
return array_values($css); return array_values($css);
} }
......
...@@ -90,8 +90,8 @@ function testGetJSSettings() { ...@@ -90,8 +90,8 @@ function testGetJSSettings() {
'language' => 'en', 'language' => 'en',
'stylesSet' => FALSE, 'stylesSet' => FALSE,
'drupalExternalPlugins' => array( 'drupalExternalPlugins' => array(
'drupalimage' => file_create_url('core/modules/ckeditor/js/plugins/drupalimage/plugin.js'), 'drupalimage' => file_url_transform_relative(file_create_url('core/modules/ckeditor/js/plugins/drupalimage/plugin.js')),
'drupallink' => file_create_url('core/modules/ckeditor/js/plugins/drupallink/plugin.js'), 'drupallink' => file_url_transform_relative(file_create_url('core/modules/ckeditor/js/plugins/drupallink/plugin.js')),
), ),
); );
$expected_config = $this->castSafeStrings($expected_config); $expected_config = $this->castSafeStrings($expected_config);
...@@ -114,9 +114,9 @@ function testGetJSSettings() { ...@@ -114,9 +114,9 @@ function testGetJSSettings() {
$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_create_url('core/modules/ckeditor/tests/modules/js/llama_contextual.js'); $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_and_button'] = file_create_url('core/modules/ckeditor/tests/modules/js/llama_contextual_and_button.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['contentsCss'][] = file_create_url('core/modules/ckeditor/tests/modules/ckeditor_test.css'); $expected_config['contentsCss'][] = file_url_transform_relative(file_create_url('core/modules/ckeditor/tests/modules/ckeditor_test.css'));
ksort($expected_config); ksort($expected_config);
$this->assertIdentical($expected_config, $this->castSafeStrings($this->ckeditor->getJSSettings($editor)), 'Generated JS settings are correct for customized configuration.'); $this->assertIdentical($expected_config, $this->castSafeStrings($this->ckeditor->getJSSettings($editor)), 'Generated JS settings are correct for customized configuration.');
...@@ -261,16 +261,16 @@ function testBuildContentsCssJSSetting() { ...@@ -261,16 +261,16 @@ function testBuildContentsCssJSSetting() {
// Enable the editor_test module, which implements hook_ckeditor_css_alter(). // Enable the editor_test module, which implements hook_ckeditor_css_alter().
$this->enableModules(array('ckeditor_test')); $this->enableModules(array('ckeditor_test'));
$expected[] = file_create_url('core/modules/ckeditor/tests/modules/ckeditor_test.css'); $expected[] = file_url_transform_relative(file_create_url('core/modules/ckeditor/tests/modules/ckeditor_test.css'));
$this->assertIdentical($expected, $this->ckeditor->buildContentsCssJSSetting($editor), '"contentsCss" configuration part of JS settings built correctly while a hook_ckeditor_css_alter() implementation exists.'); $this->assertIdentical($expected, $this->ckeditor->buildContentsCssJSSetting($editor), '"contentsCss" configuration part of JS settings built correctly while a hook_ckeditor_css_alter() implementation exists.');
// Enable the Bartik theme, which specifies a CKEditor stylesheet. // Enable the Bartik theme, which specifies a CKEditor stylesheet.
\Drupal::service('theme_handler')->install(['bartik']); \Drupal::service('theme_handler')->install(['bartik']);
\Drupal::service('theme_handler')->setDefault('bartik'); \Drupal::service('theme_handler')->setDefault('bartik');
$expected[] = file_create_url('core/themes/bartik/css/base/elements.css'); $expected[] = file_url_transform_relative(file_create_url('core/themes/bartik/css/base/elements.css'));
$expected[] = file_create_url('core/themes/bartik/css/components/captions.css'); $expected[] = file_url_transform_relative(file_create_url('core/themes/bartik/css/components/captions.css'));
$expected[] = file_create_url('core/themes/bartik/css/components/table.css'); $expected[] = file_url_transform_relative(file_create_url('core/themes/bartik/css/components/table.css'));
$expected[] = file_create_url('core/themes/bartik/css/components/text-formatted.css'); $expected[] = file_url_transform_relative(file_create_url('core/themes/bartik/css/components/text-formatted.css'));
$this->assertIdentical($expected, $this->ckeditor->buildContentsCssJSSetting($editor), '"contentsCss" configuration part of JS settings built correctly while a theme providing a CKEditor stylesheet exists.'); $this->assertIdentical($expected, $this->ckeditor->buildContentsCssJSSetting($editor), '"contentsCss" configuration part of JS settings built correctly while a theme providing a CKEditor stylesheet exists.');
} }
...@@ -479,8 +479,8 @@ protected function getDefaultToolbarConfig() { ...@@ -479,8 +479,8 @@ protected function getDefaultToolbarConfig() {
protected function getDefaultContentsCssConfig() { protected function getDefaultContentsCssConfig() {
return array( return array(
file_create_url('core/modules/ckeditor/css/ckeditor-iframe.css'), file_url_transform_relative(file_create_url('core/modules/ckeditor/css/ckeditor-iframe.css')),
file_create_url('core/modules/system/css/components/align.module.css'), file_url_transform_relative(file_create_url('core/modules/system/css/components/align.module.css')),
); );
} }
......
...@@ -57,7 +57,6 @@ protected function setUp() { ...@@ -57,7 +57,6 @@ protected function setUp() {
* Method tests CKEditor image buttons. * Method tests CKEditor image buttons.
*/ */
public function testImageButtonDisplay() { public function testImageButtonDisplay() {
global $base_url;
$this->drupalLogin($this->admin_user); $this->drupalLogin($this->admin_user);
// Install the Arabic language (which is RTL) and configure as the default. // Install the Arabic language (which is RTL) and configure as the default.
...@@ -75,7 +74,7 @@ public function testImageButtonDisplay() { ...@@ -75,7 +74,7 @@ public function testImageButtonDisplay() {
$json_encode = function($html) { $json_encode = function($html) {
return trim(Json::encode($html), '"'); return trim(Json::encode($html), '"');
}; };
$markup = $json_encode($base_url . '/core/modules/ckeditor/js/plugins/drupalimage/image.png'); $markup = $json_encode(file_url_transform_relative(file_create_url('core/modules/ckeditor/js/plugins/drupalimage/image.png')));
$this->assertRaw($markup); $this->assertRaw($markup);
} }
......
...@@ -126,7 +126,7 @@ function color_block_view_pre_render(array $build) { ...@@ -126,7 +126,7 @@ function color_block_view_pre_render(array $build) {
// 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_create_url($logo); $build['content']['site_logo']['#uri'] = file_url_transform_relative(file_create_url($logo));
} }
return $build; return $build;
......
...@@ -121,7 +121,7 @@ function _testColor($theme, $test_values) { ...@@ -121,7 +121,7 @@ function _testColor($theme, $test_values) {
$this->drupalGet('<front>'); $this->drupalGet('<front>');
$stylesheets = $this->config('color.theme.' . $theme)->get('stylesheets'); $stylesheets = $this->config('color.theme.' . $theme)->get('stylesheets');
foreach ($stylesheets as $stylesheet) { foreach ($stylesheets as $stylesheet) {
$this->assertPattern('|' . file_create_url($stylesheet) . '|', 'Make sure the color stylesheet is included in the content. (' . $theme . ')'); $this->assertPattern('|' . file_url_transform_relative(file_create_url($stylesheet)) . '|', 'Make sure the color stylesheet is included in the content. (' . $theme . ')');
$stylesheet_content = join("\n", file($stylesheet)); $stylesheet_content = join("\n", file($stylesheet));
$this->assertTrue(strpos($stylesheet_content, 'color: #123456') !== FALSE, 'Make sure the color we changed is in the color stylesheet. (' . $theme . ')'); $this->assertTrue(strpos($stylesheet_content, 'color: #123456') !== FALSE, 'Make sure the color we changed is in the color stylesheet. (' . $theme . ')');
} }
...@@ -191,7 +191,7 @@ function testLogoSettingOverride() { ...@@ -191,7 +191,7 @@ function testLogoSettingOverride() {
// Ensure that the overridden logo is present in Bartik, which is colorable. // Ensure that the overridden logo is present in Bartik, which is colorable.
$this->drupalGet('admin/appearance/settings/bartik'); $this->drupalGet('admin/appearance/settings/bartik');
$this->assertIdentical($GLOBALS['base_url'] . '/' . 'core/misc/druplicon.png', $this->getDrupalSettings()['color']['logo']); $this->assertIdentical($GLOBALS['base_path'] . 'core/misc/druplicon.png', $this->getDrupalSettings()['color']['logo']);
} }
/** /**
......
...@@ -977,7 +977,12 @@ function file_tokens($type, $tokens, array $data, array $options, BubbleableMeta ...@@ -977,7 +977,12 @@ 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
// tokens are also often used in e-mails, it's better to keep absolute
// file URLs. The 'url.site' cache context is associated to ensure the
// correct absolute URL is used in case of a multisite setup.
$replacements[$original] = file_create_url($file->getFileUri()); $replacements[$original] = file_create_url($file->getFileUri());
$bubbleable_metadata->addCacheContexts(['url.site']);
break; break;
// These tokens are default variations on the chained tokens handled below. // These tokens are default variations on the chained tokens handled below.
...@@ -1228,7 +1233,13 @@ function template_preprocess_file_link(&$variables) { ...@@ -1228,7 +1233,13 @@ function template_preprocess_file_link(&$variables) {
$options = array(); $options = array();
$file_entity = ($file instanceof File) ? $file : File::load($file->fid); $file_entity = ($file instanceof File) ? $file : File::load($file->fid);
// @todo Wrap in file_url_transform_relative(). This is currently
// 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 = file_create_url($file_entity->getFileUri()); $url = file_create_url($file_entity->getFileUri());
$variables['#cache']['contexts'][] = 'url.site';
$mime_type = $file->getMimeType(); $mime_type = $file->getMimeType();
// Set options as per anchor format described at // Set options as per anchor format described at
......
...@@ -70,6 +70,8 @@ public function setFileUri($uri) { ...@@ -70,6 +70,8 @@ public function setFileUri($uri) {
/** /**
* {@inheritdoc} * {@inheritdoc}
*
* @see file_url_transform_relative()
*/ */
public function url($rel = 'canonical', $options = array()) { public function url($rel = 'canonical', $options = array()) {
return file_create_url($this->getFileUri()); return file_create_url($this->getFileUri());
......
...@@ -52,6 +52,8 @@ public function viewElements(FieldItemListInterface $items, $langcode) { ...@@ -52,6 +52,8 @@ public function viewElements(FieldItemListInterface $items, $langcode) {
$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
// impossible. See below.
$url = file_create_url($items->getEntity()->uri->value); $url = file_create_url($items->getEntity()->uri->value);
} }
...@@ -63,6 +65,16 @@ public function viewElements(FieldItemListInterface $items, $langcode) { ...@@ -63,6 +65,16 @@ public function viewElements(FieldItemListInterface $items, $langcode) {
'#type' => 'link', '#type' => 'link',
'#title' => $view_value, '#title' => $view_value,
'#url' => Url::fromUri($url), '#url' => Url::fromUri($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 {
......
...@@ -55,6 +55,9 @@ public function settingsForm(array $form, FormStateInterface $form_state) { ...@@ -55,6 +55,9 @@ public function settingsForm(array $form, FormStateInterface $form_state) {
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
// impossible. See BaseFieldFileFormatterBase::viewElements(). Fix in
// https://www.drupal.org/node/2646744.
$value = file_create_url($value); $value = file_create_url($value);
} }
return $value; return $value;
......
...@@ -33,6 +33,9 @@ public function viewElements(FieldItemListInterface $items, $langcode) { ...@@ -33,6 +33,9 @@ public function viewElements(FieldItemListInterface $items, $langcode) {
$entity->rss_elements[] = array( $entity->rss_elements[] = array(
'key' => 'enclosure', 'key' => 'enclosure',
'attributes' => array( 'attributes' => array(
// In RSS feeds, it is necessary to use absolute URLs. The 'url.site'
// cache context is already associated with RSS feed responses, so it
// does not need to be specified here.
'url' => file_create_url($file->getFileUri()), 'url' => file_create_url($file->getFileUri()),
'length' => $file->getSize(), 'length' => $file->getSize(),
'type' => $file->getMimeType(), 'type' => $file->getMimeType(),
......
...@@ -30,7 +30,7 @@ public function viewElements(FieldItemListInterface $items, $langcode) { ...@@ -30,7 +30,7 @@ public function viewElements(FieldItemListInterface $items, $langcode) {
foreach ($this->getEntitiesToView($items, $langcode) as $delta => $file) { foreach ($this->getEntitiesToView($items, $langcode) as $delta => $file) {
$elements[$delta] = array( $elements[$delta] = array(
'#markup' => file_create_url($file->getFileUri()), '#markup' => file_url_transform_relative(file_create_url($file->getFileUri())),
'#cache' => array( '#cache' => array(
'tags' => $file->getCacheTags(), 'tags' => $file->getCacheTags(),
), ),
......
...@@ -69,6 +69,12 @@ public function buildOptionsForm(&$form, FormStateInterface $form_state) { ...@@ -69,6 +69,12 @@ public function buildOptionsForm(&$form, FormStateInterface $form_state) {
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
// 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')); $this->options['alter']['path'] = file_create_url($this->getValue($values, 'uri'));
} }
......
...@@ -66,7 +66,8 @@ function testFileTokenReplacement() { ...@@ -66,7 +66,8 @@ function testFileTokenReplacement() {
$metadata_tests['[file:path]'] = $base_bubbleable_metadata; $metadata_tests['[file:path]'] = $base_bubbleable_metadata;
$metadata_tests['[file:mime]'] = $base_bubbleable_metadata; $metadata_tests['[file:mime]'] = $base_bubbleable_metadata;
$metadata_tests['[file:size]'] = $base_bubbleable_metadata; $metadata_tests['[file:size]'] = $base_bubbleable_metadata;
$metadata_tests['[file:url]'] = $base_bubbleable_metadata; $bubbleable_metadata = clone $base_bubbleable_metadata;
$metadata_tests['[file:url]'] = $bubbleable_metadata->addCacheContexts(['url.site']);
$bubbleable_metadata = clone $base_bubbleable_metadata; $bubbleable_metadata = clone $base_bubbleable_metadata;
$metadata_tests['[file:created]'] = $bubbleable_metadata->addCacheTags(['rendered']); $metadata_tests['[file:created]'] = $bubbleable_metadata->addCacheTags(['rendered']);
$metadata_tests['[file:created:short]'] = $bubbleable_metadata; $metadata_tests['[file:created:short]'] = $bubbleable_metadata;
......
...@@ -34,7 +34,7 @@ function template_preprocess_image_style_preview(&$variables) { ...@@ -34,7 +34,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'] = array( $variables['original'] = array(
'url' => file_create_url($original_path), 'url' => file_url_transform_relative(file_create_url($original_path)),
'width' => $original_image->getWidth(), 'width' => $original_image->getWidth(),
'height' => $original_image->getHeight(), 'height' => $original_image->getHeight(),
); );
...@@ -55,7 +55,7 @@ function template_preprocess_image_style_preview(&$variables) { ...@@ -55,7 +55,7 @@ function template_preprocess_image_style_preview(&$variables) {
} }
$preview_image = $image_factory->get($preview_file); $preview_image = $image_factory->get($preview_file);
$variables['derivative'] = array( $variables['derivative'] = array(
'url' => file_create_url($preview_file), 'url' => file_url_transform_relative(file_create_url($preview_file)),
'width' => $preview_image->getWidth(), 'width' => $preview_image->getWidth(),
'height' => $preview_image->getHeight(), 'height' => $preview_image->getHeight(),
); );
......
...@@ -70,6 +70,7 @@ public function buildUri($uri); ...@@ -70,6 +70,7 @@ public function buildUri($uri);
* 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()
*/ */
public function buildUrl($path, $clean_urls = NULL); public function buildUrl($path, $clean_urls = NULL);
......
...@@ -199,9 +199,16 @@ public function viewElements(FieldItemListInterface $items, $langcode) { ...@@ -199,9 +199,16 @@ public function viewElements(FieldItemListInterface $items, $langcode) {
} }
foreach ($files as $delta => $file) { foreach ($files as $delta => $file) {
$cache_contexts = array();
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
// 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)); $url = Url::fromUri(file_create_url($image_uri));
$cache_contexts[] = 'url.site';
} }
$cache_tags = Cache::mergeTags($cache_tags, $file->getCacheTags());