Closed (fixed)
Project:
Drupal core
Version:
11.x-dev
Component:
file.module
Priority:
Critical
Category:
Task
Assigned:
Issue tags:
Reporter:
Created:
29 Jan 2018 at 16:42 UTC
Updated:
18 Jun 2024 at 10:41 UTC
Jump to comment: Most recent, Most recent file
Comments
Comment #2
wim leersGraphQL issue: https://github.com/drupal-graphql/graphql/issues/458#issuecomment-361307410.
Comment #3
gabesulliceThis was only postponed on #1927648: Allow creation of file entities from binary data via REST requests, which has landed.
From a JSON API perspective, what's most important to not duplicate is moving the file around, validating the file entity itself and handling streaming. What's less important is identifying the target entity type, entity and field. We have all that logic already.
Looking at the current REST resource plugin, it's only these lines that I wish were not in the service:
The first two lines JSON API can handle easily. The third we could technically let the service handle, but that would reduce flexibility in the future. Passing a stream wrapper is easy enough. I imagine GraphQL would prefer to pass a stream along too.
Perhaps the "validate" portion of the first to methods could be kept as public static methods. The header parsing and field definition loading could be dropped and made implementation specific though.
Comment #5
wim leers#2958554: Allow creation of file entities from binary data via JSON API requests now has a green patch. That green patch ported all of Drupal core's REST file upload resource plugin's test coverage.
Time to work on extracting a reusable service!
Comment #6
wim leersNow actively working on this.
Comment #7
wim leersThis builds on the work that #2958554: Allow creation of file entities from binary data via JSON API requests did to extract a shared service.
Comment #8
wim leersStill passing tests, hurray! 🎉
Observations:
rest.moduleuse it, and it still passes tests, so ✅The only differences are:
That issue also ported over all core REST file upload test coverage, including all the validation edge cases. It passes tests. People are using that issue's patch on their projects. In short: it works. ✅
That is of course the ever so important verification that this issue's "file upload" service actually serves its purpose! 🎉
final classinstead. We could turn it into an interface later, when the need arises. For now, I think it makes sense to lock down "the one truly official way" to upload files. Thoughts?Comment #9
wim leersAlso, considering the impact of this on JSON API and the API-First ecosystem, bumping to major.
Comment #10
wim leersMoving to the correct component.
Comment #11
gabesullice@Wim Leers asked me to port some changes from #2958554-118: Allow creation of file entities from binary data via JSON API requests into this patch.
Comment #13
wim leersI just stumbled upon the recently opened #3021652: Deprecate upload-related functions file.inc and move to a file upload service, which massively overlaps with this issue. I encouraged them to instead collaborate on this issue.
These then also require updates to the existing REST file upload test coverage. That should make this green again. And should make it easier for the #3021652 contributors to make changes to this patch as they please :)
Comment #14
berdirI don't think these issues/services actually overlap that much yet in reality, that one was thought as a replacement for *existing* functions while this is more about additional reusability on top of the existing API to avoid duplication between different API services.
Might actually make sense to have both, but we do need to have naming that makes sense.. that's currently more or less the only thing that these services seem to have in common ;)
Maybe this one could have a name that more reflects its use-case/purpose, something like StreamUploader maybe? And then this will likely use a bunch of services from the lower-level upload service the other issue is working on.
Comment #15
wim leers… which is exactly where there is a lot of overlap! Loading the field definition, determining the validation constraints based on that, and so on.
Comment #16
wim leersComment #17
wim leers@Berdir: what would you like to see change here? This has been green and ready for months (since #7). #3021652: Deprecate upload-related functions file.inc and move to a file upload service only started ~3 weeks ago, and I've reviewed that issue but there hasn't been a response yet.
I would like it very much if this would not be a blocker to JSON:API.
Idea: remove the interface, mark the class internal, commit it. That way, we can still refactor it in #3021652: Deprecate upload-related functions file.inc and move to a file upload service. This is not by any means a step backward compared to HEAD, because all of this logic is currently an internal implementation detail of
\Drupal\file\Plugin\rest\resource\FileUploadResourceanyway.Thoughts?
Comment #18
gabesullice+1
Comment #19
gabesulliceAfter receiving a feature request in JSON:API to allow client-generated UUIDs for the file entity to be created, I think that only having
$filenameis too rigid.I think
$filenameshould be replaced by an$entity_valuesargument or that argument should be optional at the end.If we mark this class internal, that suggestion can be incorporated in a followup.
Comment #20
wim leersAlright, let's make it
@internalthen.\Drupal\file\Plugin\rest\resource\FileUploadResourcewas and remains the API surface, committing this patch doesn't change that at all.Comment #21
gabesulliceCan this be private?
Should this be private too?
Comment #22
berdirI don't care that much about internal or not. What I do care about is a) where it lives and b) how it is called.
Right now it is in file.module. I guess that is necessary because of the field dependency. I think the goal is that the service of the other module is in Drupal\Core\FileSystem and not have any file entity/field dependencies. At best some things that it does could be a separate service, but maybe we could consider to move parts to the new service when it exists.
The other is naming, right now, this is called file.uploader. But it's not about any file, it's about files for file fields. Plus, @WimLeers confirmed in Slack that it is also only meant as a service for API implementations like REST, JSON-API, GraphQL and so on. And not a replacement for file_save_upload(), which is about form uploads.
So I think this should have a name that reflects that.. FileFieldUploader maybe, possibly also include Api or Services or something like that?
This is a hidden \Drupal::service() dependency. If you want to make this thing unit-testable, you should inject the entity type manager and then do $this->entityTypeManager->getStorage('file')->create()
Then we could possibly convert some of the exception tests we have for REST to unit tests for this class, so we don't have to have the same test coverage again for REST, JSON-API, .. Possibly at least some of it should probably be kept to assert how the specific format handles that. Which brings me nicely to my next point..
This service is now directly tied to specific HTTP Responses/exceptions. That means it would be weird for JSON-API or GraphQL to catch HTTP exceptions and reformat these errors as a standard json-api error response or so.
Maybe this service should throw generic exceptions instead. But then each implementation has to re-throw them, so not sure. I think in this case it might be OK, because the request itself is also format neutral and it's just a POST of the file + headers.
This would break subclasses. We're technically allowed to make such a change, but for example in my entity.manager issues, I've been very careful, with optional new arguments and BC to avoid breaking subclasses.
Just pointing that out here, do with that information as you please ;)
Comment #23
wim leers#21: That's unfortunately not possible, because
FileUploadResourceis a plugin, and plugins cannot have services injected by the container, since they're self-constructing (Their::create()method constructs them, and that can only access public services.)#22:
FileFieldUploadermakes perfect sense.I spotted a few small problems myself:
file.uploaderwas injected intoFileUploadResourcebut did not have the proper docs yet.This is checking the plugin ID of the field type, but it should be checking the class of the field type, to allow subclasses. Otherwise the
imagefield type will not work. (Discovered this thanks to the JSON:API module's tip of the branch already containing this, which led to #3026459: The controller result claims to be providing relevant cache metadata, but leaked metadata was detected in which I noticed this.)Comment #24
berdir2. I'm not saying it is a priority, but writing the code so that you *could* do it still makes sense. Otherwise why bother to inject anything? Sorry if I was unclear, what I meant is that File::create() is exactly the same as having a \Drupal::service() in your code. It's a nice helper for tests/non-oop code, but in services, we want to inject everything we can IMHO.
4. I know the current BC rules allow it, but we've been getting more strict about that recently because we have broken a lot of contrib/custom code over the last few minor releases due to constructor changes and it is annoying for contrib maintainers. I do plan to open an issue to improve the BC policy in that regard and also document safe(r) ways to override service/plugin constructors. Again, just pointing out that, depending on who is going to do the final review/commit, it might be put back to needs work. So it's a bit more code now, but one risk less that it won't be committed when it is RTBC ;)
Comment #25
wim leersOkay, I'll address both of those.
What do you think is necessary for this to reach RTBC beyond those two points?
Comment #26
dwwI was pointed here by @gabesullice
Warning: #2492171: Provide options to sanitize filenames (transliterate, lowercase, replace whitespace, etc) will now conflict with this, since it, too, changes the FileUploadResource constructor.
Technically neither blocks the other, so I'm not postponing or making either a child of the other, but maybe it makes sense to get that in first, then make sure this plays nicely with it.
Cheers,
-Derek
Comment #27
wim leersI think you're right, but it really saddens and worries me that this has been ready for more than 3 months and keeps getting semi-blocked :( This is going to be a blocker for #2843147: Add JSON:API to core as a stable module, so it jeopardizes JSON:API getting into 8.7.
Comment #28
dwwYeah, sorry. I totally feel your pain. I don't know what to do about it. I hereby promise to help make sure the new event dispatch gets properly moved around here in and the
FileUploadResourceconstructor changes are properly merged. Whether that's re-rolling this once #2492171 lands, or helping to review your re-roll is TBD. ;)Side note: I don't think anyone really reviews patches that are assigned to someone. That implies you're planning to do more work, so you might want to unassign yourself if that's not really true. However, it looks like this isn't "ready", and there's pending feedback that hasn't been incorporated. Maybe unassigned + NW is a more honest status for this? Or NW + assigned to you to indicate you're planning to incorporate the requested changes/fixes.
I do agree with #22.2: If you're going to DI at all, you might as well go all the way.
File::create()isn't ideal in here. I also agree that integration tests for this are important, but it's good not to have hidden dependencies if they can be avoided.Anyway, apologies for potentially further complicating your life. I was just trying to be responsible and handle the API-side at #2492171 ...
Cheers,
-Derek
Comment #29
dwwp.s. Alternately, if by some miracle this gets RTBC and lands before #2492171: Provide options to sanitize filenames (transliterate, lowercase, replace whitespace, etc) does, I promise not to be mad at you that I'll have to re-roll #2492171 accordingly. ;)
Comment #30
wim leersNo response to #25 in two weeks, so let's get address the feedback that we do have.
#28: not your fault, our glacially slow processes are the problem.
(#23 still applies cleanly, yay!)
#22.2: done.
#22.4: I tried to do this, but … got stuck. Most of the time, we're adding dependencies. Here, we are removing dependencies. I don't see how we can deprecate them now to ensure a painless update path from D8 to D9? Pointers very welcome! 🙏Really curious what you're gonna answer — 15 minutes of searching core for examples resulted in nothing 😀
Comment #31
berdirYes, the removed dependencies makes this really hard.
I think it is fine to remove them, as I wrote in my issue, even if "my" stricter rules are adopted, it's still a best-effort thing and we can make exceptions when we think the impact will be acceptable.
The only references to this class that I could with http://grep.xnddx.ru/search?text=FileUploadResource is jsonapi. That actually was useful because it highlights that the constructor of the new class has the wrong classname ;)
So lets clean this up..
Comment #32
wim leersWill do this first thing in the morning!
Comment #33
wim leersOther things came up in the morning and then the rest of the day, but I still did do it today! 😇
Oops … 😊
Comment #34
berdirI'd say the interdiff shows why this is a good decision in this case :)
Looks good to me, RTBC.
As far as related/overlapping issues go...
* #2244513: Move the unmanaged file APIs to the file_system service (file.inc) will conflict with this, because several of functions used here will be deprecated there and the issue does update them in the old class and their uses in the new one would trigger deprecation messages. I think it would make sense to get that in first unless it is set back to needs work as it has been @ RTBC for a week now and is a massive patch that will unblock at least 10 other issues for further cleanup around our file system API.
* #2492171: Provide options to sanitize filenames (transliterate, lowercase, replace whitespace, etc) has been set back a bit and I think it makes sense to get this one in first anyway. Then it won't have to be touched/refactored again here and it's already available then to jsonapi and other API modules.
* #3021652: Deprecate upload-related functions file.inc and move to a file upload service is waiting on the first issue to land and I'm not 100% sure yet how that will continue. I do believe there is a need for a separate file upload related services, or it could be merged into this. Still pretty much in flux, so definitely fine to wait until after this is in.
Comment #35
dwwThis changes file_uri, but leaves $prepared_filename as-is. Is that a good idea? Don't we want the resulting file entity to end up with the final filename (even with _2 if needed)?
Right here. Why not use the actual filename?
This gets all fubar with multibyte chars in your filename, as we painfully learned at #2492171: Provide options to sanitize filenames (transliterate, lowercase, replace whitespace, etc). I believe you need to use the file_system service for this if you want it to handle mb char filenames. See #2492171-257: Provide options to sanitize filenames (transliterate, lowercase, replace whitespace, etc) for more.
we can call assert() in regular code like this? ;) It just throws an exception if it fails? Cool. ;)
\Drupal::sad(). I guess we can't use the DI'ed service here since this is a public static. :/
NW for point 3. Everything else is optional.
Also, this probably needs a change record to tell people about the new service, changes to the constructors that were modified, etc.
Otherwise, agreed this looks RTBC.
Thanks!
-Derek
Comment #36
dwwAlso, the summary is mostly empty or full of lies (API changes section). ;) That should get a refresh now that this is all decided and nearly done.
Thanks,
-Derek
Comment #37
wim leersassert()is for runtime assertions, and is turned off on production servers. See https://www.drupal.org/node/2569701. Lots of things in Drupal core use this.Regarding points 1–3: his patch is only moving existing logic and making it reusable. It's explicitly not changing any logic. Otherwise it becomes extremely difficult to review. #35.3 was discovered in #2492171: Provide options to sanitize filenames (transliterate, lowercase, replace whitespace, etc), either it should be solved in that issue, or in a separate (yet-to-be-created) issue whose sole goal is to fix that.
This does not need a change record, because
\Drupal\file\FileFieldUploaderis still marked@internal, that was decided in #17 through #22, to allow #3021652: Deprecate upload-related functions file.inc and move to a file upload service to still make additional changes.Issue summary updated!
Comment #38
dwwBold, RTBC'ing your own patch! :)
1-3: Hrm, okay, makes sense. Sure, we can fix 3 in the re-roll of #2492171: Provide options to sanitize filenames (transliterate, lowercase, replace whitespace, etc). It's already fixed over there (with tests).
4. Yeah, I dig runtime
assert(). My previous life was a C/C++ programmer. ;) I just hadn't noticed this in core (yet), and was happily surprised this was used. My "cool ;)" was in earnest, not sarcasm.5. Hah, great. ;) Even better.
Interdiff looks peachy. +1 to RTBC!
Re: CR: sorry, I missed the @internal discussion for the new service. Makes sense. However, don't we at least want to warn people about this API change (which still isn't in the summary):
I know #3030640: [policy, documentation] Clarify Constructor BC policy and document best practices for subclassing other classes is still TBD, but isn't it kind to warn people about these sorts of changes in the meanwhile? Perhaps that's just noise, since presumably no one has extended this class. *shrug*
Thanks,
-Derek
Comment #39
wim leersRTBC'ing your own patch for trivial changes is fine. I don't do it lightly. It took me years before I even dared do that :)
RE: "API change" in plugin constructor: see #30 + #31. I first did all the work to not change the constructor, but then @Berdir preferred this direction. indeed matches with that outcome. I don't think it makes sense to create a change record for every constructor change. Especially not if it's for a plugin, whose constructors aren't an API anyway. I'd be happy to create such a change record if consensus is that we want it, but so far that seems to not be the case.
Comment #40
dwwYes, I'm +1 to the constructor change. I just see people requiring CRs for basically everything, so I thought I'd make sure we were covered. But yeah, fine, I doubt anyone cares that this is changing. Maybe we can footnote it in the eventual CR when the service is no longer @internal (I guess over at #3021652: Deprecate upload-related functions file.inc and move to a file upload service).
Thanks,
-Derek
Comment #41
alexpottLet's document what @internal means here.
I think it might be easier to share functionality between this service and the UI code - ie _file_save_upload_single() is the creation of the tmp file happened outside of the creation of the File entity - and then the method that creates the File Entity could take an SPL file info object and some way to change whether we call file_unmanaged_prepare or \Drupal\Core\File\FileSystem::moveUploadedFile()
Having a lock like this only used for API first based file uploads feels odd - we can race with UI as well right? Also I would have thought we'd take a lock before reading the stream to a temp file.
I'm pondering whether or not this really belongs on this service or does the code really belong somewhere else.
This needs to use \Drupal\Core\File\FileSystem::basename() - atm this has a bug due to a PHP bug - see https://bugs.php.net/bug.php?id=77239 so the static-ness here is problematic.
This feels a bit odd can't the implementors do this or why is it passable in in the first place.
Comment #42
dwwRe: #41:
1. Agreed.
2. Agreed.
3. Seems like what #3021652: Deprecate upload-related functions file.inc and move to a file upload service is trying to solve, and this is the share-all-the-API-stuff side of life.
4. I think it means "we don't fully know WTF we're doing until #3021652 is done, stay tuned." ;)
5. Maybe.
6. Maybe.
7. Yeah, probably. I think that's a reflection of this service being API-centric, per #22.1.
8. I tried at #35.3 and @Wim pushed back. I guess your pushes are heavier than mine. ;)
9. Agreed. I didn't notice that before, good catch. That does seem weird.
Comment #43
alexpottRe #35.3 / #41.8 - I can accept not solving it here but putting that method in a static makes solving it properly harder - which is why I brought it up.
Comment #44
dwwRe: #43 - excellent point! I missed that detail both in your comment and in the function signature. Definitely agree. I'll leave it to @Wim to reply on why this method has to be static and if it's feasible to move it into something that can rely on the injected file_system service, instead. It lives in the questionable
validateAndParseContentDispositionHeader()method, which perhaps doesn't belong here at all (per #41.7).Thanks,
-Derek
Comment #45
wim leers@internalprecisely because it's subject to change, and it's moving code without changing it at all. Code with thorough functional test coverage. I completely agree kernel and unit tests would be wonderful to have, but that has been true for many years. Writing such tests while much of File module's internals are in flux anyway seems like a recipe for wasting time? This patch is only moving code, and hence not increasing risk._file_save_upload_single()looks very different. It sounds like you're asking for this long-needed clean-up to happen here instead of in #3021652: Deprecate upload-related functions file.inc and move to a file upload service? Also, this would be a risk and scope creep, since this is issue/patch is only about moving code added by #1927648: Allow creation of file entities from binary data via REST requests.#43 + #44: I see. Done! ✅
Comment #46
dwwRe: #45: I'm satisfied. ;) Back to RTBC.
This will collide with #3032620: \Drupal\file\Plugin\rest\resource\FileUploadResource uses basename() when it needs to use the Drupal version which is also RTBC, but we can easily sort that out depending on the order of commits. ;)
This will also collide with #3032390: Add an event to sanitize filenames during upload, but that's still at NW, so I'm not afraid. We can trivially re-roll that once this lands. The collision is pretty small in scope.
Thanks,
-Derek
Comment #48
dwwConfused bot. Requeued #45.
Comment #50
wim leersComment #52
berdirNeeds a reroll because the file_system issue landed finally. Means having to call fewer functions :)
About the CR, I still think that it would be useful, mostly for the changes on the existing plugin, not just the constructor but also the other methods that were moved away/refactored. If someone *did* subclass it, then they'd need to update their code most likely.
I had to create a CR for https://www.drupal.org/node/3030634, which I still think was overkill and just generates noise, I think it's fair to expect one here too, it's not that much work and lowers the chance that it gets pushed back.
About the @internal, I'm still a bit on the fence about the approach. At least the argumentation on the class is pretty bogus, the referenced issue has no intention of introducing any conceptual changes to how file uploads works, all it aims to do is move a bunch of functions to a service so we can remove file.inc. If anything is still in flux/internal, then it is your own API :)
Comment #53
wim leersComment #54
wim leers@internal: Well, at the time, that issue seemed to be changing all the things, so at the time I think it was fair. A lot has changed in a short period of time here. That being said, I think even then it was arguably wrong, because it lacked precision. I think it's better to not link to an issue and instead describe the reason for it being internal in more detail. Did that. A concrete reason for this not yet to beinternalpublic (thanks @Berdir for pointing out that typo!), is to allow the different consumers of this API to surface feature gaps that may cause BC breaks. We already have one example of that: #3021155: Accept client-generated IDs (UUIDs) for file uploads too.Comment #55
wim leersFor the record: @Berdir is referring to #2244513: Move the unmanaged file APIs to the file_system service (file.inc) having landed.
Comment #57
dwwYay! The tests in #3032620: \Drupal\file\Plugin\rest\resource\FileUploadResource uses basename() when it needs to use the Drupal version caught your rebase mistake. ;) You're still calling
basename()directly.Comment #58
wim leersAwesome! :D And oops 😅
Comment #59
dwwA bit more careful review this time...
Given that everything about this seems to assume services / API, I still wonder if this class should be named accordingly. If it's not really viable to use for the non-API case, we should say so. If it *is* viable, it'd be lovely to get an @see to #3021652: Deprecate upload-related functions file.inc and move to a file upload service or something.
Don't we need to do something to cleanup the
$temp_file_pathbefore throwing an Exception?And here, shouldn't we try to purge it, even if we couldn't move it? The most likely source of failure here would be at the new location, but we don't want to leave this file sitting around awaiting eventual temp purging.
And, don't we also want to release the file lock right here before we throw the Exception?
The comment says "Close the file streams", but we only close 1 thing here. Seems we either want to also
fclose($stream)here, or change the comment.Ditto.
if
fopen()returned NULL and we're inside the else from testingif($temp_file), seems like callingfclose($temp_file)is a bad idea.And ditto the concern about a plural comment with a single
fclose().Does this lock ID really want ":rest:" in the name if the intention is to share this across multiple APIs?
handleFileUploadForField()can throw exceptions without closing the stream. We should try/catch here so we definitelyfclose($stream)in case of trouble, otherwise seem we're just leaking FDs.Yeah, the code we're replacing does two
fclose()'s at this spot...Comment #60
alexpottThis issue was discussed at length by @Wim Leers, @Berdir, @dww and myself and I promised I would post my thoughts on this issue to recap my points.
Once JSON API has landed we should re-scope this issue and/or #3021652: Deprecate upload-related functions file.inc and move to a file upload service to be about providing a secure easy to use file upload service that can be leveraged core, contrib and custom for the various use-cases. As @Berdir pointed out with JSON API in core and its own set of methods calling low level file commands we've hit the rule of three.
Comment #61
wim leersThanks for the summary and bringing clarity to this issue, @alexpott! 🙏
Removing the tag since as of #60 this is no longer blocking #2843147: Add JSON:API to core as a stable module.
Also retitling to reflect the new scope, and the fact that this is now blocked on #2843147: Add JSON:API to core as a stable module.
Comment #62
dwwFor the record, I'm -1 to the strategy from #60.
a) That means the JSON:API version of this code is still buggy and needs work: #2843147-145: Add JSON:API to core as a stable module
b) That means yet more code paths we have to touch for #3032390: Add an event to sanitize filenames during upload and related upload hardening / cleanup.
I'd say:
c) Yet more deprecation hoop jumping as we try to merge/replace them all, but at least that one is mitigated by "hopefully less deprecation hoop jumping" if there are significant changes to the service once we carefully consider the UI/form case. ;)
Still although this perhaps makes things easier for the clean solution in 8.8.0, it makes things worse for 8.7.0. Given our general deprecation and API change frenzy during minor releases, I don't know that the benefits of waiting for "perfect" outweigh the costs. Hence, -1.
But (thankfully!) this isn't my decision to make. :) I defer to @alexpott.
But meanwhile, #2843147: Add JSON:API to core as a stable module is (should be) still blocked on fixing Drupal\jsonapi\ForwardCompatibility\FileFieldUploader.
Comment #63
dwwFYI: #62.a is now at #3035866: Update \Drupal\jsonapi\ForwardCompatibility\FileFieldUploader to be in sync with \Drupal\file\Plugin\rest\resource\FileUploadResource
Comment #64
wim leers#62: yep. Those were the arguments you and I presented in yesterday's discussion that @alexpott refers to in #60. It's fine to repeat those here, but I'm not going to rehash the discussion.
Instead, I'm attaching the chat in full for anyone to read. This is important because in Slack, those discussions become impossible to access in a week or less.
Comment #65
dwwRe: #64 thanks for posting that for posterity, @Wim Leers! Definitely helpful to save conversations like that in the issue, both so they're not lost/forgotten, and to be more accessible to other contributors and folks not constantly on Slack. @Wim Leers++ ;)
Comment #68
imclean commentedWhat is this issue currently postponed on? I've had a read through and can't quite work it out, sorry.
While I'm not overly familiar with the internal workings of Drupal's file management, I have been a regular consumer of related APIs. A unified file management service would greatly simplify a lot of things we are trying to achieve.
Comment #69
dww@imclean: re: #68:
Ugh, whoops. We all totally forgot about this. :( It shouldn't be postponed anymore. It just needs work. However, I suspect we've missed all kinds of deadlines to do this properly for 8.9.x, and it's likely to be 9.1.x material at this point. But maybe @alexpott knows better so I'll leave the version alone for now.
Thanks/sorry,
-Derek
Comment #70
imclean commentedThanks for the update Derek. Is there an overview somewhere of the various levels of file management and operations required? I see a lot of duplicated code in various parts of core an contrib (e.g. if file exists create new entity or reuse existing?).
Just quickly, here are the levels and choices I've come across recently, from low to high level.
"Physical" File
Existing filename
File Entity
Existing filename
Media Entity
Existing File Entity Reference
Existing Filename
Node/other Entity
Existing Media Entity Reference
Existing Filename
Comment #73
bkosborne+1 for this effort! I'm working on trying to get the Feeds module capable of importing a remote file found in some external feed into a file field. I found all the complex code that both REST and JSON API are performing to handle this similar task and agree it would be wonderful to have some service that handles it all.
Comment #74
klausiWhile I ported the file name security fix to graphql module I found a bug in FileUploadResource: #3184974: FileUploadResource does not release lock on validation errors.
So yep, would be really good to have a central core service.
Comment #75
dww@alexpott opened #3199264: Create a file upload service and replace the guts of \Drupal\jsonapi\Controller\TemporaryJsonapiFileFieldUploader, \Drupal\file\Plugin\rest\resource\FileUploadResource and _file_save_upload_single() as a critical task, which I just marked duplicate with this. But I'm bumping the prio here accordingly.
Thanks,
-Derek
Comment #76
alexpottComment #77
wim leersPer #74.
Comment #78
wim leersEchoing what #74 said regarding the GraphQL module, but now for the CKEditor 5 module.
Bringing CKEditor 5 to Drupal 9|10 will also need the ability to upload files, and will hence also run into this (well, it already did: #3201055: Upload validation and processing).
Comment #79
zrpnrThis needed a serious reroll, the last commit I found that the patch in #58 applied to was
0b6e897There were patch breaking changes in
04343d7#3036197: REST FileUploadResource::streamUploadData() can call fclose(FALSE)55c96a6#3000057: Deprecate drupal_set_time_limit() and file_upload_max_size() and move to Environment componentae58cf0#3069046: Properly manage newly required parameter of FileUsageBase::__construct()9aa7d13#3023015: Replace usages of deprecated constants in file.inccc564e2#3121362: Fix duplicate word typos (the the, to to, etc.) for code comments7567d54#3142934: Replace \Drupal\Component\Utility\Bytes::toInt() with \Drupal\Component\Utility\Bytes::toNumber() due to return typec8cc155#3055193: [Symfony 5] The "Symfony\Component\HttpFoundation\File\MimeType\MimeTypeGuesser" class is deprecated since Symfony 4.3, use "Symfony\Component\Mime\MimeTypes" instead.1994ca5SA-CORE-2020-012fd1d675#2863652: Do not allow files with "php|pl|py|cgi|asp|js" extensions to be renamed to *.txt and be uploaded if *.txt is not allowed by the field widget69a0a83#3032390: Add an event to sanitize filenames during uploadI tried to keep as close as possible to the original patch while adding in the changes to
FileUploadResourceover the past 2 years.All 44 functional tests that use
FileUploadResourceTestBasepassed for me locally, we'll see what testbot finds! 🤞Comment #80
gabesulliceBravo! 👏👏👏
Comment #81
dww@zrpnr: Fantastic work, thanks for bringing this forward!
Since you based your work on #58, most of my points from review #59 are not fixed. ;) But I can't blame you for that. You're "just" rerolling this beast after a ton of changes to all the related code. So here's a fresh review on #79:
Probably doesn't matter, but it seems slightly weird that the class name is FileFieldUploader while the service ID is file.uploader.field. Shouldn't they agree? Can the service ID be file.field.uploader ?
As a brand new service, this is our chance to put these dependencies in a "nice" order. Do we care? Is there any rhyme / reason for the current order?
Is any of this REST stuff actually used in here? I'm not seeing it. If so, seems like a poor separation of responsibilities. I'd think something generic here in
\Drupal\Filewouldn't/shouldn't know or care about anything REST-specific.Update: now that I got through the whole patch, I see the call in
protected function validate()Yuck, I don't think we want that. Not entirely sure the best way to refactor this. Maybe
Drupal\file\Plugin\rest\resource\FileUploadResource::post()can still worry about this? I'd have to think more about a truly clean solution, but after 7pm on a Friday night is not a good time for that. ;)This is all new code. How could anyone be calling it without the event dispatcher? I think this can all be removed. It's got the wrong class name in the deprecation message, so it looks like this was copied in the (heroic!) reroll from somewhere else that does need it, but put here where it doesn't belong.
The
@returnsays we'll return a list of validation errors (if any). But the code no longer does that. I think we lost saving the return value fromvalidate()during the reroll. I think we should keep the documented behavior, and get the code back to agree.We've got a
%sin thesprintf(), but no value to replace it with. Either this doesn't need asprintf()at all, or we should pass the filename.// Begin building the file entity.
Again, as new code, can this ever happen here?
Would:
"Temporary file could not be moved to permanent location"
be more clear here? I know it's not really "permanent", either. ;) But as written, this is a little confusing.
// First, check that the header exists.
I think we should consistently use 'the "Content-Disposition" header' in all these exceptions. The last one does it that way. The first two do not.
Not sure why we're @see'ing to a rest thing here. Don't we want to
@see self::validate()instead?I don't understand why this static method is here. Not everything trying to upload a file necessarily wants to use
php://inputas the upload stream, right? If that's what callers want, seems like it should be their problem, not the responsibility of this class.If we are keeping it here, we're losing the comment about 'rb' being required on Windoze machines...
a) "Reads file upload data to a temporary file."
b) This method doesn't "moves to file destination", so let's end the comment after "temporary file."
Can we type hint
$streamasresource?We're only closing a single stream, the temp file. Using "streams" is confusing here, since it sort of implies we're closing the $stream we were passed in, which we definitely don't do. I think both of these should say:
// Close the temp file stream.See #59.4 and 59.5. ;)
Not true. This comment should be removed. The input file stream is now the caller's responsibility as documented. ;)
Is there a fancier / more precise type we can use for these, not just 'array'?
The docs don't match the code. Docs say we return a list of constraint violations. Code is to throw an exception that unpacks all the violations into a single string. Seems like the documentation would be a better API than the current implementation. ;)
The new
FileUploadSanitizeNameEventcan do a lot more than this. Maybe:"Prepares the filename for safe use on the site."
?
And, we should probably add:
We do not want to pass
$validatorsby reference. We're not modifying the array (and shouldn't be).// The actual extension validation occurs in self::validate().
Aren't we trying to kill
file_save_upload()? ;)These comments (and the newline between them) make it seem like we're dealing with separate things here. We're not. It's all 1 thing.
Also, it's not just "according to the PHP limit". It's the min() of PHP's limit and the field setting.
So maybe ditch the 2nd comment and newline, and use this for the 1st comment:
// Cap the upload size according to the PHP limit and field settings (if any).
Or something...
This is great progress. We've moved a bunch of stuff out of RESTlandia into something generic.
But the scope of the issue is to unify this with the regular file UI and JSON:API. Seems like none of that is here. See the plan from the end of #60. Now that JSON:API is in core, we should see a similarly huge # of lines removed from core/modules/jsonapi somewhere... ;)
Ditto, I believe the goal of this issue is also to decimate
_file_save_upload_single()and move most/all of that into this service.So _file_save_upload_single() + JSON:API + REST = "the rule of 3" mentioned in #60.
That seems like a lot of API change. :( Isn't this going to potentially blow up a lot of things? Don't we need to be more careful here and do more deprecation dancing?
Thanks again @zrpnr for getting us back to a working patch! Agreed with #80: Bravo!!
Comment #82
alexpott#81.25 is very important. I also think we need to think about how the API looks.
Comment #83
ravi.shankar commentedHere I have tried to address the following points of comment #81:
1, 2, 4, 5, 6, 7, 9, 10, 12, 14, 15, 16, 17, 20, 21, 22, and 24.
This still needs work for the remaining points.
Comment #84
gabesulliceAnother reason
notto have a shared service.Edit: a very misleading typo
Comment #85
kapilv commentedComment #86
kapilv commentedComment #87
dww@ravi.shankar re: #83: Thanks! Mostly your fixes look good.
However, for point 2 you only changed exactly the spot I flagged. That's changing the order of arguments that get passed to the constructor for this service class. So we also need to fix
FileFieldUploader:: __construct()to match:So, still TODO from #81:
2. (see above)
3.
6. You removed the %s, but maybe we wanted to pass in the filename instead?
8.
11.
13.
18.
19.
23. I'm only half joking. ;) Seems weird to be adding references to code we're also trying to kill. Relates to point 25.
25. (Agreed with @alexpott - this is the main thing that needs (much) more work before this is ready for real review.)
26.
Everything else in the interdiff looks good, and I confirm you fixed my concern in the other cases.
Thanks again!
-Derek
Comment #89
larowlanTagging this for work sprint
Comment #90
kim.pepperTaking a look
Comment #91
kim.pepperAttempt at a re-roll of #83 while fixing the order of arguments in
FileFieldUploader::_construct()Comment #93
kim.pepperSomething changed with the validators.
Comment #94
spokjeComment #95
kim.pepper@dww in #81.5 you say we should return a list of validators. However, the current code calls:
\Drupal\rest\Plugin\rest\resource\EntityResourceValidationTrait::validate()which doesn't return validators and instead throws\Symfony\Component\HttpKernel\Exception\UnprocessableEntityHttpException\file_validate()which returns an array of error messages.I think it makes it difficult to do that. What's the ideal interface we want?
Comment #96
spokjeComment #97
kim.pepperGiven the current dependencies on file and rest modules for validation, I think it might be worth looking at splitting this issue up into smaller achievable chunks. Thoughts?
Comment #98
kim.pepperI've created a meta issue #3221796: [META] Modernise file upload logic to track this and the separate tasks as proposed above. Please feel free to speak up if you think we should handle this differently.
Comment #99
kim.pepperThis is only blocked on #3221794: Unify file upload validation from rest and json api modules now.
Comment #100
kim.pepperAfter spending some time on #3221794: Unify file upload validation from rest and json api modules I've come to the conclusion its not going to make things easier by splitting this issue down. The changes to the dependencies by splitting off the validation will only need to be undone when we need to update them for the uploader.
I moved some of the logic in that issue, including the undeclared dependencies on the file module in JSON API, and have put them into this combined issue.
I have been working through the common code and differences between REST and JSON API, as well as taking a look at what _file_upload_single() and GraphQL modules are doing.
I've tried to split out the Uploader into smaller, single-purpose classes. This can hopefully reduce the complexity. e.g.
There are still differences in validation. For example JSON API checks permissions for the entity the file field is attached to, but REST is much simpler. Not sure if we should be trying to unify that logic here.
I expect there will be a number of errors with this patch (e.g. error message formatting is slightly different in each module), but wanted to post it for early feedback.
The interdiff is also not going to be too useful as there are a lot of changes.
Comment #102
kim.pepperFix newlines in error messages.
Comment #103
kim.pepperComment #104
daffie commentedFirst quick review:
I am sure if we can remove these constants without creating a BC break.
I am quite sure this will be a BC break. Adding, and changing typehints. Changing the parameters. If contrib is overriding this method, they will have a problem.
Could we add some testing for this new method.
Could we add "public" before "API".
Which keys are those? Which ones are required?
Can we add the typehint "string" twice.
How does the exception get thrown. Not saying that it is wrong, just asking.
Can we add AccessResultInterface as a typehint.
What is "resource" exactly in "@param resource $stream"?
This method throws a bunch of non-documented exceptions. And the one that is documented is never throw as far as I can see.
Can we a to the documentation that the parameter is "optional".
Can we add the typehint "string" for the parameter $file_field_name.
Comment #105
joachim commented> I am quite sure this will be a BC break. Adding, and changing typehints. Changing the parameters. If contrib is overriding this method, they will have a problem.
Plugin classes are explicitly not in the BC policy and we can change them.
Comment #106
kim.pepperThanks for the review @daffie.
This is still a work in progress, and I'm keen to get feedback on how we split out the classes for re-use etc. Getting feedback from the JSON API (@gabesullice) and REST (@Wim Leers) module maintainers would be great! Also GraphQL if they are watching this issue.
Also, we still need to replace duplicate code in _file_save_upload_single().
Responses to #104:
I've also added an interface \Drupal\file\Upload\FileFieldUploaderInterface as I think it makes the API much more readable.
Comment #107
kim.pepper🤔 I'm wondering if we exclude
_file_save_upload_single()from this issue. It is different in a few ways:file_save_upload()which supports multi-file uploads\Drupal::request()->files->get('files', [])Comment #108
berdirRandom idea. Can we add smaller public methods to the new service that we could maybe re-use, for example validation handling? But that could also be done later.
Comment #109
kim.pepper@Berdir yeah I split out that into it's own service. Wasn't sure if that was going too far.
Comment #110
MykolaVeryha commented@kimpepper Path #106 has bugs and can't be used to upload a file via REST API
Please check this method \Drupal\file\Upload\FileFieldUploader::createFile
At that time the file is stored in a temporary directory but you are creating a file entity with a destination URI. The file wasn't moved to the destination URI yet.
As result, other modules can't use hook_file_validate() cause they won't be able to realize where is the uploaded file.
We can see the correct behavior in the file_move() function: the file URI should be set only after the file moving.
Comment #111
MykolaVeryha commentedThere is the updated patch
Comment #112
MykolaVeryha commentedComment #113
kim.pepperAfter coming back to this issue after a bit of a break, I think we need to break it down into bite-sized chunks.
Since
_file_save_upload_single()is only creating file entities, and does not care about file fields, I split it off to a new issue: #3232248: Move _file_save_upload_single to a service and deprecate. We may be able to leverage that for the file uploads in this issue.Comment #114
larowlan#3232248: Move _file_save_upload_single to a service and deprecate
Comment #115
andypostblocker is in #3232248: Move _file_save_upload_single to a service and deprecate
Comment #116
dwwWonder if we should focus on getting #3223209: deprecate file_save_data, file_copy and file_move and replace with a service in first? Not sure how much overlap / conflict there still would be. I haven't had time to dig into the latest code here to understand what's happening and where.
Comment #117
alexpott@dww this issue delivers far more tangible benefits than the other issue. This one should be prioritised
Comment #118
kim.pepperWorking on this
Comment #119
kim.pepperI took a fresh stab at this after #3232248: Move _file_save_upload_single to a service and deprecate landed so no interdiff. This is a work in progress.
The main points are:
\Drupal\jsonapi\Controller\FileUpload first.Still need to look at REST\Drupal\file\Upload\FileUploadHandlerwhich uses\Symfony\Component\HttpFoundation\File\UploadedFile.UploadedFileis primarily used for form uploads with multipart form data.content-dispositionheader andphp://inputto read file data directly.UploadedFileisn't directly available from the$request, and we need to manage it ourselves\Drupal\file\Upload\FileFieldUploadHandlerwraps\Drupal\file\Upload\FileUploadHandlerand uses settings, validators etc from the the file field settings\Drupal\file\Upload\FileStreamUploaderto readphp://inputand return anUploadedFileso we can re-useFileUploadHandler\Drupal\file\Upload\FileFieldUploadAccessChecker\Drupal\file\Upload\FilenameExtractor\Drupal\file\Upload\FileFieldUploadHandler\Drupal\file\Upload\FileStreamUploaderas their own services and wrote unit tests for them
Looking for early feedback on the approach before I tackle REST.
Comment #121
kim.pepperFixes some of the tests due to implied dependency on the file module.
Comment #123
kim.pepperFixes a few more tests.
I may have hit a stumbling block using
UploadedFileas\Symfony\Component\HttpFoundation\File\UploadedFile::isValid()callsis_uploaded_file()which only works for files that are in the$_FILESsuperglobal which I think is only set on form posts.Comment #124
kim.pepperOne way might be to change our
\Drupal\file\Upload\FileUploadHandler::handleFileUpload()to take aUploadedFileInterfacethen provide one based on symfonyUploadedFilefor forms, and another based onphp://inputfor REST & JSON API.Comment #126
kim.pepperThis is an effort to implement what I proposed in #124.
It introduces:
\Drupal\file\Upload\UploadedFileInterfacewith two subclasses:\Drupal\file\Upload\FormUploadedFilewhich wrapsSymfony\Component\HttpFoundation\File\UploadedFile\Drupal\file\Upload\RawUploadedFilefor use with our JSON API / REST controllers.\Drupal\file\Upload\FileUploadHandler::handleFileUpload()to take aUploadedFileInterface\Drupal\file\Upload\FileFieldUploadHandlernow subclassesFileUploadHandlerso we can handle the special case of\Drupal\Core\File\FileSystemInterface::moveUploadedFile()which only works with form uploaded files.Comment #128
kim.pepperFixes a couple more test fails, and switches
FileFieldUploadHandlerTestto a kernel test.Comment #130
alexpottI have a proposal. We move jsonapi.file_upload out of jsonapi.services.yml and into \Drupal\jsonapi\JsonapiServiceProvider::register() and we can register the service if the file module is around. \Drupal\jsonapi\Routing\Routes::getFileUploadRoutesForResourceType() already depends on the File entity type existing.
Comment #131
kim.pepperAs discussed in Slack, if this doesn't get in before 9.3.0 alpha, we need a smaller issue to ensure we are not painted into a corner with UploadedFile. I created #3245244: Make FileUploadHandler re-usuable for non-form file uploads.
Comment #132
kim.pepperWe might want to look at
\Drupal\file\Plugin\Field\FieldType\FileItemin this issue too. It has some duplicate code for:getUploadLocation()getUploadValidators()Comment #133
catchIf we end up doing this, we're going to need an update/post update in jsonapi to install file module.
Dynamically registering the services depending on whether file module is installed or not seems like a good idea.... But what do we do with the route/controller if it's not though?
Comment #134
berdirI think I commented earlier that this dependency already existed, it was just hidden because it was copied/code/function calls. It would probably already fail in a very ugly way on HEAD.
We do have a route requirement to check for another module, for example:
We can possibly do the same here?
Comment #135
catchIf it's completely broken in HEAD, then I think it'd be fine to make the route dependent on file module too.
Comment #136
berdirLooking closer, I think it's not actually a problem.
The routes are defined dynamically anyway, and if there are no file fields on a given resource, it just won't define any routes for it. So defining the service dynamically should be fine?
See \Drupal\jsonapi\Routing\Routes::getFileUploadRoutesForResourceType(), we could make it more explicit with a module exists check there but it should be fine.
Comment #137
catchOK that is encouraging. Given that, I think we should go for #130, so marking needs work for that. If the routes are only defined when there's a file field, then it's probably fine to leave as-is.
Comment #138
kim.pepperRe-roll of #128 after #3245244: Make FileUploadHandler re-usuable for non-form file uploads
Comment #140
kim.pepperThis moves
'jsonapi.file_upload'service to be dynamically registered if the file module is enabled as per #130.Comment #141
kim.pepperRe-roll of ##14 after #3223209: deprecate file_save_data, file_copy and file_move and replace with a service
Comment #143
kim.pepperRe-roll of #141
Comment #145
kim.pepperWorked with @larowlan to try and figure out the test fails. Found one bug where we weren't passing through the entity for access checks, but don't think we've found the main reason yet.
Comment #146
kim.pepperPass EXISTS_RENAME to handleFileUpload().
Comment #149
kim.pepper#3154962: TemporaryJsonapiFileFieldUploader::checkFileUploadAccess() checks for bundle landed
Comment #150
karishmaamin commentedTried to fix build failures
Comment #151
joachim commentedOverall I appreciate the density of comments in the code -- it helps a lot with following what the code is doing!
I'm not sure about the pattern of adding lots of services which only have one method, which is only used once.
First line should try to say what the class does, not just repeat its name.
Though I'm not really sure what the purpose of this class is -- it's just a wrapper around the entity field manager which also checks the target type. It's only called once. Is it really necessary for this to be in a service?
Stray line.
Are we checking the setting rather than the field type to allow for entity reference fields which target files?
What file? Should this say 'field' instead?
I can't find where this property is used.
Doesn't agree with the @param doc on the constructor.
Documentation standards say this should be a complete sentence.
Would be nice if the docs explained why this number.
'optional' should be in lowercase.
Missing docs.
Comment #154
mxr576As the scope of this task to introduce new _generic_ APIs in Drupal, having a
\Drupal\file\Upload\FileFieldUploadHandler::handleFileUpload()public method (inherited from the parent class) and also an overridden`\Drupal\file\Upload\FileFieldUploadHandler::moveUploadedFile()triggers my senses because we are might abusing inheritance here.Comment #156
volegerComment #157
lamp5I was testing #150 using json api + private files upload + anonymous user and it works for 1 file (i can see deprecations warning) but for many files it looks like upload override files id in "anonymous_allowed_file_ids" so there is no option to save a entity because validation fails.
Comment #158
kim.pepperIt's been a long time since I looked at this issue. I've taken the patch at #150 and fixed up a few of the PHPCS issues to see how the tests will run.
Comment #160
kim.pepperFix return type declarations and remove unnecessary method overrides.
Comment #162
kim.pepperDigging into the test failures, it looks like in
Drupal\Tests\jsonapi\Functional\FileUploadTest::testFileUploadMaliciousExtensionwe are assuming a file with filenameexample.phpwill get successfully uploaded asexample.php_.txtcore/modules/jsonapi/tests/src/Functional/FileUploadTest.php (line 621):I'm a bit confused why this should not get rejected given in
core/modules/system/src/EventSubscriber/SecurityFileUploadEventSubscriber.php (line 78)we have the comment:Comment #163
kim.pepperThe good news is I think I found why the `file_validate_extensions` was reverting back to default extensions instead of allowing any. The bad news is this opened up a whole new list of test fails.
Comment #164
kim.pepperFix spelling typo.
Comment #166
kim.pepperI've added a specific kernel test to check the access in
\Drupal\file\Upload\FileFieldUploadAccessChecker. The code is taken from\Drupal\Tests\jsonapi\Kernel\Controller\TemporaryJsonapiFileFieldUploaderTest.I've created an abstract base class in response to the feedback in #154. I hope this addresses those concerns.
We still need to work out the test fails which appear to be access related. I'm hoping to get help with these.
Comment #168
kim.pepperCode in
\Drupal\jsonapi\Controller\TemporaryJsonapiFileFieldUploader::checkFileUploadAccesshad changed since it was copied over into\Drupal\file\Upload\FileFieldUploadAccessChecker::checkFileUploadAccess. The new test picked that up. :-DComment #170
larowlanReroll and attempt to address the failures
Comment #171
kim.pepperRemaining tasks are to address feedback in:
Comment #172
larowlanAdded those to issue summary, fixed some more tests that failed locally with new changes
Comment #173
needs-review-queue-bot commentedThe Needs Review Queue Bot tested this issue. It fails the Drupal core commit checks. Therefore, this issue status is now "Needs work".
Apart from a re-roll or rebase, this issue may need more work to address feedback in the issue or MR comments. To progress an issue, incorporate this feedback as part of the process of updating the issue. This helps other contributors to know what is outstanding.
Consult the Drupal Contributor Guide to find step-by-step guides for working with issues.
Comment #174
larowlanAddressing phpstan issues
Comment #175
dwwExcited to see this moving forward again!
All of the interdiff between #146 and #150 looks like wrong “fixes” that introduced bugs. Wish Kim had re-started from 146, instead. 😅 I wonder if those bugs have subsequently been fixed, or if they mean we’re still missing test coverage. 😉 hope to look more closely when I’m not on my phone.
Comment #176
kim.pepperComment #177
needs-review-queue-bot commentedThe Needs Review Queue Bot tested this issue. It fails the Drupal core commit checks. Therefore, this issue status is now "Needs work".
This does not mean that the patch needs to be re-rolled or the MR rebased. Read the Issue Summary, the issue tags and the latest discussion here to determine what needs to be done.
Consult the Drupal Contributor Guide to find step-by-step guides for working with issues.
Comment #178
kim.pepperAdds deprecation to
\Drupal\jsonapi\Controller\TemporaryJsonapiFileFieldUploaderand service. Fixes phpstan issue.Comment #179
kim.pepperLooping back to address feedback in #151.
FileFieldDefinitionResolver::resolveFieldDefinition()back to\Drupal\jsonapi\Controller\FileUpload::resolveFieldDefinition. I think I assumed it would be used in more places than just the jsonapi module.Comment #180
kim.pepperLooks like there was a problem uploading the patch files. Will try again...
Comment #182
smustgrave commentedSeems #180 is adding
php-http/discovery
is that intentional? Don't see it in the issue summary about adding a new package.
Comment #183
kim.pepper#182 that was unintentional. Removed.
Comment #184
smustgrave commentedSorry for not catching this before but CR could use another look. Seems it was started in 8.8
Also can the deprecation be updated for 10.2, think 10.1 was missed.
Comment #185
kim.pepperAdding another reason to have a unified uploader. #3372385: CKEditor file upload sets file URI prior to validation, causing validators to be unable to find the file.
Comment #186
wim leers@kim.pepper++
Based on your deep experience in this problem space, @kim.pepper, should we land #3372385: CKEditor file upload sets file URI prior to validation, causing validators to be unable to find the file., or should we instead try to land this to avoid the need for yet another custom work-around?
Comment #187
kim.pepperI don't think this should hold up a bug fix that could potentially be backported.
Although I have spent hours on this issue, a lot of it has been discovery. I think we should make sure we have a vigorous architectural review before moving ahead.
Comment #188
kim.pepperI've added a sequence diagram showing the complexity of the current state of form uploads to the meta issue #3221796: [META] Modernise file upload logic
Comment #189
kim.pepperRe-titling the issue as cores file upload doesn't follow the same logic. It uses UploadedFile objects from the request, and not a direct input stream upload like REST and JSON API.
For core file upload refactoring see #3375423: Deprecate file_managed_file_save_upload(), file_save_upload() and _file_save_upload_from_form() and replace with a service
Comment #190
kim.pepperRather than eating the elephant in one go, I'm breaking this up into more bite-sized pieces.
I think we should postpone this issue on the following:
Comment #191
larowlanOne of the blockers is in
Comment #192
wim leersIncredible 🤯
Comment #193
kim.pepperTwo more of the blockers are in:
Comment #194
larowlanAdded additional task from https://git.drupalcode.org/project/drupal/-/merge_requests/4866#note_230080
Comment #195
kim.pepperLast remaining blocker #3389447: Provide a trait to create file upload validators from file field settings is in so we are un-blocked!
Comment #196
wim leers🤩🙏🙏🙏🙏
Comment #197
kim.pepperThe next step forward for this effort is now ready for reviews: #3401734: Refactor FileUploadResource to use FileUploadHandler
Comment #198
kim.pepperMarking this as a meta issue now have two child issues that we want to do one at a time:
Comment #199
kim.pepper#3401734: Refactor FileUploadResource to use FileUploadHandler is in 🎉 which unblocks #3444748: Refactor JSON-API file uploads to use FileUploadHandler
Comment #200
kim.pepper#3444748: Refactor JSON-API file uploads to use FileUploadHandler Just landed so I think we are safe to mark this fixed now.
Thanks to everyone here for the years of work and support to get us to this point. Much appreciated. 🫶
No doubt there will be follow up issues to further refine and improve the code re-use here, but I think we are now in a much more secure situation than before.
Comment #201
wim leersWOWWWWWWWWWWWWWWWWWWWW!!!!!!!!!!!!!!!!!
We all owe @kim.pepper thanks, beers, lemonades, applause and gratitude for many years to come, because thanks to his unrelenting effort Drupal is now finally consistent in this very security-sensitive area! 🤩🥳
Comment #203
quietone commentedThis issue does not need a change record, so removing related tags. Any change records needed have been done in the child issues.