diff --git a/core/modules/editor/src/Form/EditorImageDialog.php b/core/modules/editor/src/Form/EditorImageDialog.php index 0de74e5..dad2e67 100644 --- a/core/modules/editor/src/Form/EditorImageDialog.php +++ b/core/modules/editor/src/Form/EditorImageDialog.php @@ -206,11 +206,9 @@ public function submitForm(array &$form, FormStateInterface $form_state) { // attributes and set data-entity-type to 'file'. $fid = $form_state->getValue(['fid', 0]); if (!empty($fid)) { + /** @var \Drupal\file\FileInterface $file */ $file = $this->fileStorage->load($fid); - $file_url = file_create_url($file->getFileUri()); - // Transform absolute image URLs to relative image URLs: prevent problems - // on multisite set-ups and prevent mixed content errors. - $file_url = file_url_transform_relative($file_url); + $file_url = $file->createFileUrl(); $form_state->setValue(['attributes', 'src'], $file_url); $form_state->setValue(['attributes', 'data-entity-uuid'], $file->uuid()); $form_state->setValue(['attributes', 'data-entity-type'], 'file'); diff --git a/core/modules/editor/src/Plugin/Filter/EditorFileReference.php b/core/modules/editor/src/Plugin/Filter/EditorFileReference.php index ad2ac03..4424762 100644 --- a/core/modules/editor/src/Plugin/Filter/EditorFileReference.php +++ b/core/modules/editor/src/Plugin/Filter/EditorFileReference.php @@ -5,6 +5,7 @@ use Drupal\Component\Utility\Html; use Drupal\Core\Entity\EntityManagerInterface; use Drupal\Core\Plugin\ContainerFactoryPluginInterface; +use Drupal\file\FileInterface; use Drupal\filter\FilterProcessResult; use Drupal\filter\Plugin\FilterBase; use Symfony\Component\DependencyInjection\ContainerInterface; @@ -76,8 +77,8 @@ public function process($text, $langcode) { // URL. This ensures the URL works even after the file location changes. if ($node->hasAttribute('src')) { $file = $this->entityManager->loadEntityByUuid('file', $uuid); - if ($file) { - $node->setAttribute('src', file_url_transform_relative(file_create_url($file->getFileUri()))); + if ($file instanceof FileInterface) { + $node->setAttribute('src', $file->createFileUrl()); } } @@ -86,7 +87,7 @@ public function process($text, $langcode) { $processed_uuids[$uuid] = TRUE; $file = $this->entityManager->loadEntityByUuid('file', $uuid); - if ($file) { + if ($file instanceof FileInterface) { $result->addCacheTags($file->getCacheTags()); } } diff --git a/core/modules/file/file.module b/core/modules/file/file.module index a6f6809..df9a7f5 100644 --- a/core/modules/file/file.module +++ b/core/modules/file/file.module @@ -1171,7 +1171,7 @@ function file_tokens($type, $tokens, array $data, array $options, BubbleableMeta // 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->createFileUrl(FALSE); $bubbleable_metadata->addCacheContexts(['url.site']); break; @@ -1431,7 +1431,7 @@ function template_preprocess_file_link(&$variables) { // 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_entity->createFileUrl(FALSE); $variables['#cache']['contexts'][] = 'url.site'; $mime_type = $file->getMimeType(); diff --git a/core/modules/file/src/Entity/File.php b/core/modules/file/src/Entity/File.php index 92aa436..f768ab7 100644 --- a/core/modules/file/src/Entity/File.php +++ b/core/modules/file/src/Entity/File.php @@ -76,11 +76,23 @@ public function setFileUri($uri) { /** * {@inheritdoc} + */ + public function createFileUrl($relative = TRUE) { + $url = file_create_url($this->getFileUri()); + if ($relative && $url) { + $url = file_url_transform_relative($url); + } + return $url; + } + + /** + * {@inheritdoc} * * @see file_url_transform_relative() */ public function url($rel = 'canonical', $options = []) { - return file_create_url($this->getFileUri()); + @trigger_error('File entities returning the URL to the physical file in File::url() is deprecated, use $file->createFileUrl() instead. See https://www.drupal.org/node/3019830', E_USER_DEPRECATED); + return $this->createFileUrl(FALSE); } /** diff --git a/core/modules/file/src/FileInterface.php b/core/modules/file/src/FileInterface.php index ee7120b..dd4ac32 100644 --- a/core/modules/file/src/FileInterface.php +++ b/core/modules/file/src/FileInterface.php @@ -51,6 +51,20 @@ public function getFileUri(); public function setFileUri($uri); /** + * Creates a file URL for the URI of this file. + * + * @param bool $relative + * (optional) Whether the URL should be root-relative, defaults to TRUE. + * + * @return string + * A string containing a URL that may be used to access the file. + * + * @see file_create_url() + * @see file_url_transform_relative() + */ + public function createFileUrl($relative = TRUE); + + /** * Returns the MIME type of the file. * * @return string diff --git a/core/modules/file/src/Plugin/Field/FieldFormatter/FileMediaFormatterBase.php b/core/modules/file/src/Plugin/Field/FieldFormatter/FileMediaFormatterBase.php index 8cbf88b..92d01af 100644 --- a/core/modules/file/src/Plugin/Field/FieldFormatter/FileMediaFormatterBase.php +++ b/core/modules/file/src/Plugin/Field/FieldFormatter/FileMediaFormatterBase.php @@ -196,7 +196,7 @@ protected function getSourceFiles(EntityReferenceFieldItemListInterface $items, if (static::mimeTypeApplies($file->getMimeType())) { $source_attributes = new Attribute(); $source_attributes - ->setAttribute('src', file_url_transform_relative(file_create_url($file->getFileUri()))) + ->setAttribute('src', $file->createFileUrl()) ->setAttribute('type', $file->getMimeType()); if ($this->getSetting('multiple_file_display_type') === 'tags') { $source_files[] = [ diff --git a/core/modules/file/src/Plugin/Field/FieldFormatter/RSSEnclosureFormatter.php b/core/modules/file/src/Plugin/Field/FieldFormatter/RSSEnclosureFormatter.php index a433d99..12d0551 100644 --- a/core/modules/file/src/Plugin/Field/FieldFormatter/RSSEnclosureFormatter.php +++ b/core/modules/file/src/Plugin/Field/FieldFormatter/RSSEnclosureFormatter.php @@ -25,13 +25,14 @@ public function viewElements(FieldItemListInterface $items, $langcode) { // Add the first file as an enclosure to the RSS item. RSS allows only one // enclosure per item. See: http://wikipedia.org/wiki/RSS_enclosure foreach ($this->getEntitiesToView($items, $langcode) as $delta => $file) { + /** @var \Drupal\file\FileInterface $file */ $entity->rss_elements[] = [ 'key' => 'enclosure', 'attributes' => [ // 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->createFileUrl(FALSE), 'length' => $file->getSize(), 'type' => $file->getMimeType(), ], diff --git a/core/modules/file/src/Plugin/Field/FieldFormatter/UrlPlainFormatter.php b/core/modules/file/src/Plugin/Field/FieldFormatter/UrlPlainFormatter.php index d5e2a7c..73a8aed 100644 --- a/core/modules/file/src/Plugin/Field/FieldFormatter/UrlPlainFormatter.php +++ b/core/modules/file/src/Plugin/Field/FieldFormatter/UrlPlainFormatter.php @@ -3,6 +3,7 @@ namespace Drupal\file\Plugin\Field\FieldFormatter; use Drupal\Core\Field\FieldItemListInterface; +use Drupal\file\FileInterface; /** * Plugin implementation of the 'file_url_plain' formatter. @@ -24,8 +25,9 @@ public function viewElements(FieldItemListInterface $items, $langcode) { $elements = []; foreach ($this->getEntitiesToView($items, $langcode) as $delta => $file) { + assert($file instanceof FileInterface); $elements[$delta] = [ - '#markup' => file_url_transform_relative(file_create_url($file->getFileUri())), + '#markup' => $file->createFileUrl(), '#cache' => [ 'tags' => $file->getCacheTags(), ], diff --git a/core/modules/file/src/Tests/FileFieldWidgetTest.php b/core/modules/file/src/Tests/FileFieldWidgetTest.php index 3102bd7..bef39f1 100644 --- a/core/modules/file/src/Tests/FileFieldWidgetTest.php +++ b/core/modules/file/src/Tests/FileFieldWidgetTest.php @@ -92,7 +92,7 @@ public function testSingleValuedWidget() { $this->assertFileExists($node_file, 'New file saved to disk on node creation.'); // Ensure the file can be downloaded. - $this->drupalGet(file_create_url($node_file->getFileUri())); + $this->drupalGet($node_file->createFileUrl()); $this->assertResponse(200, 'Confirmed that the generated URL is correct by downloading the shipped file.'); // Ensure the edit page has a remove button instead of an upload button. @@ -322,7 +322,7 @@ public function testPrivateFileSetting() { $this->assertFileExists($node_file, 'New file saved to disk on node creation.'); // Ensure the private file is available to the user who uploaded it. - $this->drupalGet(file_create_url($node_file->getFileUri())); + $this->drupalGet($node_file->createFileUrl()); $this->assertResponse(200, 'Confirmed that the generated URL is correct by downloading the shipped file.'); // Ensure we can't change 'uri_scheme' field settings while there are some @@ -388,14 +388,14 @@ public function testPrivateFileComment() { $comment_file = $comment->{'field_' . $name}->entity; $this->assertFileExists($comment_file, 'New file saved to disk on node creation.'); // Test authenticated file download. - $url = file_create_url($comment_file->getFileUri()); + $url = $comment_file->createFileUrl(); $this->assertNotEqual($url, NULL, 'Confirmed that the URL is valid'); - $this->drupalGet(file_create_url($comment_file->getFileUri())); + $this->drupalGet($url); $this->assertResponse(200, 'Confirmed that the generated URL is correct by downloading the shipped file.'); // Test anonymous file download. $this->drupalLogout(); - $this->drupalGet(file_create_url($comment_file->getFileUri())); + $this->drupalGet($url); $this->assertResponse(403, 'Confirmed that access is denied for the file without the needed permission.'); // Unpublishes node. @@ -405,7 +405,7 @@ public function testPrivateFileComment() { // Ensures normal user can no longer download the file. $this->drupalLogin($user); - $this->drupalGet(file_create_url($comment_file->getFileUri())); + $this->drupalGet($url); $this->assertResponse(403, 'Confirmed that access is denied for the file without the needed permission.'); } @@ -575,7 +575,7 @@ protected function doTestTemporaryFileRemovalExploit(UserInterface $victim_user, $this->assertEqual($attacker_user->id(), $node_file->getOwnerId(), 'New file belongs to the attacker.'); // Ensure the file can be downloaded. - $this->drupalGet(file_create_url($node_file->getFileUri())); + $this->drupalGet($node_file->createFileUrl()); $this->assertResponse(200, 'Confirmed that the generated URL is correct by downloading the shipped file.'); // "Click" the remove button (emulating either a nojs or js submission). diff --git a/core/modules/file/tests/src/Functional/FileFieldDisplayTest.php b/core/modules/file/tests/src/Functional/FileFieldDisplayTest.php index a5c4ebf..68fe7d2 100644 --- a/core/modules/file/tests/src/Functional/FileFieldDisplayTest.php +++ b/core/modules/file/tests/src/Functional/FileFieldDisplayTest.php @@ -211,7 +211,7 @@ public function testDescriptionDefaultFileFieldDisplay() { // Test default formatter. $this->drupalGet('node/' . $nid); - $this->assertFieldByXPath('//a[@href="' . $node->{$field_name}->entity->url() . '"]', $description); + $this->assertFieldByXPath('//a[@href="' . $node->{$field_name}->entity->createFileUrl(FALSE) . '"]', $description); // Change formatter to "Table of files". $display = \Drupal::entityTypeManager()->getStorage('entity_view_display')->load('node.' . $type_name . '.default'); @@ -221,7 +221,7 @@ public function testDescriptionDefaultFileFieldDisplay() { ])->save(); $this->drupalGet('node/' . $nid); - $this->assertFieldByXPath('//a[@href="' . $node->{$field_name}->entity->url() . '"]', $description); + $this->assertFieldByXPath('//a[@href="' . $node->{$field_name}->entity->createFileUrl(FALSE) . '"]', $description); } /** diff --git a/core/modules/file/tests/src/Functional/Formatter/FileAudioFormatterTest.php b/core/modules/file/tests/src/Functional/Formatter/FileAudioFormatterTest.php index ae75c70..2de18a2 100644 --- a/core/modules/file/tests/src/Functional/Formatter/FileAudioFormatterTest.php +++ b/core/modules/file/tests/src/Functional/Formatter/FileAudioFormatterTest.php @@ -46,8 +46,8 @@ public function testRender($tag_count, $formatter_settings) { $this->drupalGet($entity->toUrl()); - $file1_url = file_url_transform_relative(file_create_url($file1->getFileUri())); - $file2_url = file_url_transform_relative(file_create_url($file2->getFileUri())); + $file1_url = $file1->createFileUrl(); + $file2_url = $file2->createFileUrl(); $assert_session = $this->assertSession(); $assert_session->elementsCount('css', 'audio[controls="controls"]', $tag_count); diff --git a/core/modules/file/tests/src/Functional/Formatter/FileVideoFormatterTest.php b/core/modules/file/tests/src/Functional/Formatter/FileVideoFormatterTest.php index 0cc997c..fd9e569 100644 --- a/core/modules/file/tests/src/Functional/Formatter/FileVideoFormatterTest.php +++ b/core/modules/file/tests/src/Functional/Formatter/FileVideoFormatterTest.php @@ -46,8 +46,8 @@ public function testRender($tag_count, $formatter_settings) { $this->drupalGet($entity->toUrl()); - $file1_url = file_url_transform_relative(file_create_url($file1->getFileUri())); - $file2_url = file_url_transform_relative(file_create_url($file2->getFileUri())); + $file1_url = $file1->createFileUrl(); + $file2_url = $file2->createFileUrl(); $assert_session = $this->assertSession(); $assert_session->elementsCount('css', 'video[controls="controls"]', $tag_count); diff --git a/core/modules/file/tests/src/Kernel/FileItemTest.php b/core/modules/file/tests/src/Kernel/FileItemTest.php index ac6cd88..bc6eb54 100644 --- a/core/modules/file/tests/src/Kernel/FileItemTest.php +++ b/core/modules/file/tests/src/Kernel/FileItemTest.php @@ -99,7 +99,6 @@ public function testFileItem() { $this->assertEqual($entity->file_test->display, 1); $this->assertEqual($entity->file_test->description, $description); $this->assertEqual($entity->file_test->entity->getFileUri(), $this->file->getFileUri()); - $this->assertEqual($entity->file_test->entity->url(), $url = file_create_url($this->file->getFileUri())); $this->assertEqual($entity->file_test->entity->id(), $this->file->id()); $this->assertEqual($entity->file_test->entity->uuid(), $this->file->uuid()); diff --git a/core/modules/file/tests/src/Kernel/FileLegacyTest.php b/core/modules/file/tests/src/Kernel/FileLegacyTest.php new file mode 100644 index 0000000..47a3c70 --- /dev/null +++ b/core/modules/file/tests/src/Kernel/FileLegacyTest.php @@ -0,0 +1,49 @@ +installEntitySchema('user'); + $this->installConfig(['user']); + + $this->installEntitySchema('file'); + $this->installSchema('file', ['file_usage']); + } + + /** + * Tests that File::url() is deprecated. + * + * @expectedDeprecation File entities returning the URL to the physical file in File::url() is deprecated, use $file->createFileUrl() instead. See https://www.drupal.org/node/3019830 + */ + public function testFileUrlDeprecation() { + file_put_contents('public://example.txt', $this->randomMachineName()); + $file = File::create([ + 'uri' => 'public://example.txt', + ]); + $file->save(); + + $this->assertEquals($file->createFileUrl(FALSE), $file->url()); + } + +} diff --git a/core/modules/hal/src/Normalizer/FileEntityNormalizer.php b/core/modules/hal/src/Normalizer/FileEntityNormalizer.php index ec73f95..8dd5737 100644 --- a/core/modules/hal/src/Normalizer/FileEntityNormalizer.php +++ b/core/modules/hal/src/Normalizer/FileEntityNormalizer.php @@ -83,7 +83,7 @@ protected function getEntityUri(EntityInterface $entity, array $context = []) { // back to the old behavior because it relies on said hack, not just to // generate the value for the 'uri' field of a file (see ::normalize()), but // also for the HAL normalization's '_links' value. - return file_create_url($entity->getFileUri()); + return $entity->createFileUrl(FALSE); } } diff --git a/core/modules/hal/tests/src/Kernel/FileNormalizeTest.php b/core/modules/hal/tests/src/Kernel/FileNormalizeTest.php index edcf3dc..bda9a8b 100644 --- a/core/modules/hal/tests/src/Kernel/FileNormalizeTest.php +++ b/core/modules/hal/tests/src/Kernel/FileNormalizeTest.php @@ -45,7 +45,7 @@ public function testNormalize() { 'uri' => [ [ 'value' => $file->getFileUri(), - 'url' => file_url_transform_relative(file_create_url($file->getFileUri())), + 'url' => $file->createFileUrl(), ], ], ]; diff --git a/core/modules/image/tests/src/Functional/ImageFieldDisplayTest.php b/core/modules/image/tests/src/Functional/ImageFieldDisplayTest.php index 33e042e..b0f38e8 100644 --- a/core/modules/image/tests/src/Functional/ImageFieldDisplayTest.php +++ b/core/modules/image/tests/src/Functional/ImageFieldDisplayTest.php @@ -277,7 +277,7 @@ public function testImageFieldSettings() { $node = $node_storage->load($nid); $file = $node->{$field_name}->entity; - $url = file_url_transform_relative(file_create_url(ImageStyle::load('medium')->buildUrl($file->getFileUri()))); + $url = file_url_transform_relative(ImageStyle::load('medium')->buildUrl($file->getFileUri())); $this->assertTrue($this->cssSelect('img[width=40][height=20][class=image-style-medium][src="' . $url . '"]')); // Add alt/title fields to the image and verify that they are displayed. diff --git a/core/modules/media/tests/src/Functional/Hal/MediaHalJsonAnonTest.php b/core/modules/media/tests/src/Functional/Hal/MediaHalJsonAnonTest.php index 5b8de2d..2b92c73 100644 --- a/core/modules/media/tests/src/Functional/Hal/MediaHalJsonAnonTest.php +++ b/core/modules/media/tests/src/Functional/Hal/MediaHalJsonAnonTest.php @@ -53,7 +53,7 @@ protected function getExpectedNormalizedEntity() { ], $this->baseUrl . '/rest/relation/media/camelids/field_media_file' => [ [ - 'href' => $file->url(), + 'href' => $file->createFileUrl(FALSE), 'lang' => 'en', ], ], @@ -64,7 +64,7 @@ protected function getExpectedNormalizedEntity() { ], $this->baseUrl . '/rest/relation/media/camelids/thumbnail' => [ [ - 'href' => $thumbnail->url(), + 'href' => $thumbnail->createFileUrl(FALSE), 'lang' => 'en', ], ], @@ -80,7 +80,7 @@ protected function getExpectedNormalizedEntity() { [ '_links' => [ 'self' => [ - 'href' => $file->url(), + 'href' => $file->createFileUrl(FALSE), ], 'type' => [ 'href' => $this->baseUrl . '/rest/type/file/file', @@ -115,7 +115,7 @@ protected function getExpectedNormalizedEntity() { [ '_links' => [ 'self' => [ - 'href' => $thumbnail->url(), + 'href' => $thumbnail->createFileUrl(FALSE), ], 'type' => [ 'href' => $this->baseUrl . '/rest/type/file/file', diff --git a/core/modules/media/tests/src/Functional/Rest/MediaResourceTestBase.php b/core/modules/media/tests/src/Functional/Rest/MediaResourceTestBase.php index 78678e2..9b374d5 100644 --- a/core/modules/media/tests/src/Functional/Rest/MediaResourceTestBase.php +++ b/core/modules/media/tests/src/Functional/Rest/MediaResourceTestBase.php @@ -176,7 +176,7 @@ protected function getExpectedNormalizedEntity() { 'target_id' => (int) $file->id(), 'target_type' => 'file', 'target_uuid' => $file->uuid(), - 'url' => $file->url(), + 'url' => $file->createFileUrl(FALSE), ], ], 'thumbnail' => [ @@ -188,7 +188,7 @@ protected function getExpectedNormalizedEntity() { 'target_type' => 'file', 'target_uuid' => $thumbnail->uuid(), 'title' => NULL, - 'url' => $thumbnail->url(), + 'url' => $thumbnail->createFileUrl(FALSE), ], ], 'status' => [ diff --git a/core/modules/responsive_image/src/Plugin/Field/FieldFormatter/ResponsiveImageFormatter.php b/core/modules/responsive_image/src/Plugin/Field/FieldFormatter/ResponsiveImageFormatter.php index f17fb2e..0c1da94 100644 --- a/core/modules/responsive_image/src/Plugin/Field/FieldFormatter/ResponsiveImageFormatter.php +++ b/core/modules/responsive_image/src/Plugin/Field/FieldFormatter/ResponsiveImageFormatter.php @@ -9,6 +9,7 @@ use Drupal\Core\Form\FormStateInterface; use Drupal\Core\Plugin\ContainerFactoryPluginInterface; use Drupal\Core\Url; +use Drupal\file\FileInterface; use Drupal\image\Plugin\Field\FieldFormatter\ImageFormatterBase; use Drupal\responsive_image\Entity\ResponsiveImageStyle; use Symfony\Component\DependencyInjection\ContainerInterface; @@ -227,9 +228,10 @@ public function viewElements(FieldItemListInterface $items, $langcode) { } foreach ($files as $delta => $file) { + assert($file instanceof FileInterface); // Link the element to the original file. if (isset($link_file)) { - $url = file_url_transform_relative(file_create_url($file->getFileUri())); + $url = $file->createFileUrl(); } // Extract field item attributes for the theme function, and unset them // from the $item so that the field template does not re-render them.