Problem/Motivation
Examples:
template_preprocess_file_link()\Drupal\file\Plugin\Field\FieldFormatter\BaseFieldFileFormatterBase::viewElements()\Drupal\file\Plugin\Field\FieldFormatter\FileUriFormatter::viewValue()- …
These all generate links (<a href="…"></a>) using LinkGenerator/\Drupal\Core\Link, which in turn require \Drupal\Core\Url objects.
Those \Drupal\Core\Url objects require a URL scheme. Which means root-relative (file) URLs cannot be passed to them, which means we must generate absolute file URLs, which means trouble as soon as you encounter sites available over both HTTP and HTTPS.
Proposed resolution
Detect root-relative URLs, automatically use the base: scheme. This is then similar to what \Drupal\Core\Url::fromUri() already does for protocol-relative URLs.
Remaining tasks
None.
User interface changes
None.
API changes
No changes; one addition: Url::fromUri() no longer considers /cat.png invalid (i.e. as using an invalid URI scheme); instead it detects this as a root-relative URL and hence uses the base: scheme.
Data model changes
None.
| Comment | File | Size | Author |
|---|---|---|---|
| #59 | 2646744-file-urls-fix.patch | 6.8 KB | jennakoo |
Issue fork drupal-2646744
Show commands
Start within a Git clone of the project using the version control instructions.
Or, if you do not have SSH keys set up on git.drupalcode.org:
Comments
Comment #2
wim leersComment #3
dawehnerIsn't that the point of protocol relative URLs?
//foo/bar/baz.jsComment #4
wim leersBut protocol-relative URLs are also not supported by the Url class.
Comment #5
dawehnerThere is #2573635: Url::fromUri() should accept protocol-relative URLs for protocol relative URLs.
In that issue I made the point that the URL class should implement everything which is possible, but some people disagree with it.
Comment #6
xjmWe usually treat the URL generation as a part of routing, so moving there. (Maybe the component should be "routing and URL generation system".)
Comment #7
wim leers+1
Comment #8
mac_weber commented@Wim Leers
There is an open issue for protocol-relative: #1783278: Scheme-relative URL rejected by validation
Comment #9
effulgentsia commentedDiscussed this with @catch, @alexpott, @xjm, and @Cottser, and we agreed this is Major, because it appears to be a regression from Drupal 7, which would impede the porting of sites that rely on it to Drupal 8.
Comment #10
mac_weber commentedThis is very related to this issue and others that need an improvement at the
Urlclass: #2691099: Improve external URL validation in many waysComment #15
oleksiyComment #17
andypostneeds work for tests
Comment #18
oleksiyThere is the available test in the patch. Can you say what's wrong there, please?
Comment #19
andypost@Oleksiy it needs "test-only" patch to prove coverage
Comment #21
zymbian commentedProviding interdiff for patch #15 and #19 .
Comment #22
andypost@zymbian for testonly patch no interdiff is needed, it is the same patch with tests except bug fix - see 13) in https://www.drupal.org/contributor-tasks/write-tests
Comment #23
alexpottThe word
Weis unnecessary.I wonder if the change in behaviour will have any negative impacts. I.e. times when we want the invalid URL exception for validation purposes.
Comment #25
berdirComing here from the @todo in \Drupal\file\Plugin\Field\FieldFormatter\BaseFieldFileFormatterBase::viewElements().
The thing is that the @todo there and the issue title at least here are wrong. As the patch provides, root-relative paths work just fine, you simply have to prefix it with base:.
I'm not sure we need to fix anything here, it simply means that a caller to fromUri() needs to prefix the URL with base.
Comment #26
wim leersLooks like that's what #15 was already doing.
Addressing #23:
Comment #28
wim leersWe should also fix the @todos that were referring to this.
Comment #29
berdirdoes it make sense to assert that we don't have any cache contexts explicitly?
Looks like a really nice cleanup, will wait with RTBC until #2402533: Provide File::createFileUrl() as a replacement for the deprecated File:url() implementation is committed as this will need quite a reroll then, should then however also be simpler as we can just remove the FALSE argument in many of these cases.
Comment #30
wim leersThat uncovered some failures. For
UnroutedUrlAssembler::assemble()to work, we need to transform the root-relative URL to abase:URI.Comment #33
wim leers#2402533: Provide File::createFileUrl() as a replacement for the deprecated File:url() implementation just landed! But first, making this green again.
ImageFieldDisplayTestpasses for me locally though… So I'll stick to "mostly" green.Comment #34
wim leersAnd now using #2402533: Provide File::createFileUrl() as a replacement for the deprecated File:url() implementation's new API.
Comment #37
berdirHm, so turns out it doesn't actually work yet, not if you have a installed Drupal in a subfolder. The problem is that file_transform_relative() already includes (or actually, does not remove) the base path, and then if you run that test with against http://localhost/d8, you end up with /d8/d8 as the path.
This fixes the test but is obviously very ugly. Not quite sure what to do about that. This behavior is hardcoded in \Drupal\Core\Utility\UnroutedUrlAssembler::buildLocalUrl(), we have nothing to control that.
Good thing our tests are still running in a sub-folder..
Also fixed ComputedFileUrlTest, that could now actually be a unit test, wasn't sure about converting that here.
Comment #38
dawehnerIt feels like creating a helper method would helpful. Maybe something like
Url::fromUri('relative_drupal://')or so? What do you think about that, would that be worth moving to a separate issue?Kudos for adding tests in multiple places!
Comment #39
dawehnerIt fascinates me that there is already documentation for this case.
Comment #40
wim leers#37
D'OH OF COURSE 😅
My bad. (But I'm glad I updated existing usages, otherwise this might've gotten committed without us realizing this…)
I wonder if a
file_url_transform_base_url()method that returns a\Drupal\Core\Urlobject with abase:URI is warranted here, which does that stripping of the base path if any?#39: not sure what you mean :P
Comment #41
berdirI was also thinking about another function. But we're trying to deprecate the transform functions in #2669074: Convert file_create_url() & file_url_transform_relative() to service, deprecate it in favor of a new service that actually has the transform-relative built-in in the create URL method, so I' not sure if we should have a new method there (different return values based on query arguments are ugly, we already had to add too much of that for render cacheability stuff), or on Url or what :)
I paused on the related issue to wait for this one to be finished, unsure which order makes sense now.
The problem is that I'm not sure how many other cases are affected, it's a pretty fundamental problem and we only had very few test fails and only noticed it because testbot still runs with a subdirectory for history reasons.. more things could be broken. In fact, I've been advocating to use this API in other issues, so I expect that a considerable amount of code out there is using it incorrectly already :-/
Comment #42
wim leersYeah this is … tough.
I wonder if that leads us to a very different conclusion: that
file_create_url()returning plain strings is the problem, that it should returnUrlobjects instead.Urlis new in Drupal 8.file_create_url()was written in a time when we just had stream wrappers and strings-as-URLs.I'm sure this would be nontrivial to do.
But … just imagine how nice it must be to always deal with a
Urlobject. By default, this would result inUrlobjects withbase:URIs. With the https://www.drupal.org/project/cdn module installed, the file URL alteration would make it result inhttps://or protocol-relative URIs. Either way, the return value offile_create_url()(and/or its successor) would be aUrlobject.I think that most of the pain we're observing is due to us first getting a file-URL-as-a-string and then wanting to convert it to file-URl-as-a-Url-value-object. If we can avoid that conversion step, the pain also disappears. We also gain consistency in DX.
Of course, this would be a quite big undertaking. Thoughts?
Comment #44
wim leersComment #49
berdir#2669074: Convert file_create_url() & file_url_transform_relative() to service, deprecate it is now in, with a method to return Url objects, and I believe it removed all those @todos from the code base.
The issue here is not yet fixed, but we now do have an API that can generate correct Url objects for files, so I'm downgrading this to normal for now unless we have a use case that doesn't work yet.
We still have work to do to modernize the underlying API's, so that we don't have to convert back and forth between the getExternalUrl() method of stream wrappers, so there _might_ still be specific issues, but they should hopefully be far less common now.
Comment #51
rokzabukovec commentedRerolled patch for Drupal 9.3
Comment #52
berdirPer my comment above, you shoudn't need this patch anymore, you are mostly just reverting back to wrong/deprecated code. If you use this patch then that might also explain why you are getting errors.
Comment #53
rokzabukovec commentedI updated the core to 9.3.2 and this issue came up. After updating the node where I attached some file to it then I saved it and when trying to go to that node the Invalid URL error WSOD came up. This patch is solving the issue. Why? I don't know, but it does.
Comment #56
andypostparented to #3087434: [meta] Use FileUrlGenerator::generate() everywhere, then deprecate generateString() and generateAbsoluteString()
hiding all patches as useless
Comment #59
jennakoo commentedRerolled the patch for Drupal 10.1
Comment #60
andypostComment #61
smustgrave commentedThink this needs an issue summary update, reading comment 51-53 @berdir makes it sound like this patch shouldn't be needed anymore.
Comment #62
rajab natshah