Steps to reproduce, in its most severe form:
- Create a content type with a file field that allows a finite number > 1 files to be uploaded
- Turn JavaScript off
- Create a new node of that type, and choose more files than are allowed.
- Save the node
Result is a WSOD and failure to save other data POSTED to the entity. I don't feel that I'm established enough in the Ways of Drupal to call this critical myself, but it looks like a candidate.
This issue has a less severe form when JavaScript is on: The request on which the file uploads still PHP fatals in the same way, but since the request is always submitted immediately upon file selection over ajax, the symptom is that upon transmission of the files, nothing happens. There is no error message/indication of what happened.
| Task | Novice task? | Contributor instructions | Complete? |
|---|---|---|---|
| Do some manual testing | Would be good to confirm (attached a raw post request maybe?) that this really results in same behavior but I'm pretty sure this works. Otherwise Drupal wouldn't accept it :) | ||
| Embed before and after screenshots in the issue summary | Novice |
Can you post a screenshot of the UI in case more than the allowed number of files have been uploaded? |
| Comment | File | Size | Author |
|---|---|---|---|
| #74 | 2535302-74.patch | 9.56 KB | mbaynton |
| #69 | 2535302-69.patch | 3.98 KB | kgoel |
| #67 | Screen Shot 2015-07-22 at 4.18.48 PM.png | 37.65 KB | cilefen |
| #48 | selecting_too_many-2535302-48.patch | 10.92 KB | claudiu.cristea |
Comments
Comment #1
mbayntonPlayed with this some more on other browsers etc; mitigating the issue somewhat is that if the user knows what's up, many browsers will restore their form entries if you back-button to /node/add/[type] right away...but I wasn't able to reproduce that consistently even on the same browser session, sometimes the values reappear and other times they don't. Per jhodgdon's IRC advice, I'm going to go ahead and set this critical.
Comment #2
cilefen commentedCritical bugs:
"Critical bugs include those that:
"
Comment #3
cilefen commentedData loss is bad.
Comment #4
cilefen commentedWhat is the fatal error?
Comment #5
mbayntonPHP Fatal error: Call to undefined method Drupal\\field\\Entity\\FieldStorageConfig::getFieldName() in /data/website/tcdrupal8/core/modules/file/src/Plugin/Field/FieldWidget/FileWidget.php on line 353
Comment #6
mbayntonComment #7
tim.plunkettComment #8
tim.plunkettStill very much needs tests.
Comment #9
mbayntonWorking on the tests...
Comment #10
RavindraSingh commented#8 is working fine as I hace tested the patch manually at local. and didn't see ant WSOD or "PHP Fatal error: Call to undefined method Drupal\\field\\Entity\\FieldStorageConfig::getFieldName() in /data/website/tcdrupal8/core/modules/file/src/Plugin/Field/FieldWidget/FileWidget.php on line 353"
Below are the screenshots of verification:

Uploaded files when JS is OFF
Result of Saving the Node page when JS is OFF.
Comment #11
RavindraSingh commentedComment #12
wim leersWow, nice find!
Comment #13
dawehnerIt is not straightforward to write a test coverage for that.
I tried it with
but that did not produced the expected result.
Comment #14
dawehnerIn general this issue summary shows that having some more static analysis of our code is not bad idea.
Comment #15
berdirWorking on a test.
Comment #16
berdirOk. I don't see any other way except of building the whole POST data by hand, I don't see how you can upload multiple files with the same name through either our or curl's API.
Took me quite some time to get that right, that stuff is picky :p
Anyway, this is a failing tests, also noticed that we're displaying the wrong number of uploaded files.. but I'm not sure which one to use since we could have existing files. Either we mention how many files were uploaded (but then it could say max 4, uploaded 2) or we use the total count but then we might need to reword the message to make sense.
Comment #18
fabianx commentedThe test looks great to me!
Nice one!
Comment #19
mbaynton@Berdir thanks for working on the test. Unfortunately since I said I was working on that less than a day ago and it was, as you found, a bit tricky, there was some duplication of effort. I had not yet resigned myself to cranking out the POST string manually but perhaps that's what needed to happen.
I have one minor suggested improvement, in the error message set by
validateMultipleCount, I think computation of the number of files provided is better done as$keep + 1than as$uploaded.$keepis being used for 0-based array indexing but takes into account the number of files previously uploaded to the entity (for example if there's 2 already, then I edit the entity and add 2 more).$uploadedis only the number of files sent on this particular request. So you could end up with "Field myfield can only hold 3 values but there were 2 uploaded" etc.It doesn't seem like this error message is displayed in this re-edit case at present, but this seems more robust.
Oh, I'm betting this is one of those times where one provides an "interdiff"?
Comment #21
lauriiiI think we need test coverage for the issue described on #19 since #16 was passing without fixing that
Comment #22
mbaynton@lauriii you are right of course. I'll take this one, with a little thought about how one would go about writing a test for that I'm finding other very interesting things that I think will lead to a better, entirely different overall fix for this issue.
Comment #23
berdir$keep + 1 makes no sense, there could be any amount of images, not just one more than allowed? I guess what you meant is to use $count.
And yes, as I said, the message could be confusing that way. But it would be equally confusing if it would say you uploaded 4 files when you only uploaded two? We should probably reword the error a bit, not sure which number to use either way.
Not sure which issue @21 refers to exactly, just the message?
Comment #24
fabianx commented#23: The message and the wrong count.
We should explicitly test for the correct expected message is I think what laurii means.
Comment #25
berdirTesting for the correct message is easy.
The harder part is deciding what the correct message is.
As I said, the patch in #19 is IMHO worse, not better.
If we want something better than #16 then we need to rewrite the message. And either say something like N *additional* files were uploaded (and use $uploaded, like my patch) Or we say something like "N existing and uploaded files" and use $count.
And I guess a few variations of the test. I can update the test accordingly, not sure about what to write. But it might be easier when we have the test already.
Comment #26
berdirThis was assigned to @mbaynton, but I think it shouldn't overlap too much since he was trying to figure out a different solution apparently. I just added some more tests. First the same as before, then creating a node with 1 file, then adding two more, then trying to add another two.
Found another weird bug, the count of the current files didn't work, this seems to work in the test, still a bit weird.
Tests are not fully working yet but done for now.
No interdiff, since the test code moved around quite a bit, so would be pretty useless.
Comment #28
mbaynton@Berdir you are right about #19 being nuts, was too early in the morning. I do have a new and different solution that I'll be posting shortly; the change itself was quick&easy but the killer remained solving the multiple-file upload via curl problem.
Comment #29
mbayntonOk here we go. So, what do you do when a method is responsible for a critical issue? Delete the method! Presto, critical issue no longer in codebase :P
Obviously that logic is flawless, but some more detailed discussion may still be warranted:
^ (this is an attempt at humor...)
Laurii's request for a better test made me notice that there are two totally different behaviors exhibited by Drupal when one uploads too many files to a field, depending on whether you upload them all at once to a new entity or add some of them later to an updated entity. In the former case, the method containing the critical issue is invoked; in the latter case, a validation defined as a constraint in Drupal\Core\Field\FieldItemList is used instead. Depending on which logic kicks the submission out as invalid, different error text is produced, but more importantly, the FieldItemList-based logic retains all the uploaded files, and adds UI on the form rebuilt in the response to allow the user to pick which of the files should be removed to get in compliance. This is pretty slick, really, a much better UX than an error message and dropped files, that someone clearly worked hard on.
The logical question to ask given all this is, can we just use the FieldItemList-based logic for the creating new entity case as well. The answer sure seems to be, "yes, of course", and doing so only required not having the other one be registered as an
#element_validateon the form, so not a change of mind-blowing scope. Although it would be good if those more familiar with field widgets could review and agree with this assessment -- I spent a little time studying as a novice and came to the conclusion that the intended architecture is for field widgets to be as unconcerned with how many of them happen to be on an entity as possible, delegating that toFieldItemList, but identifying a reason for there ever being duplicate logic inFileWidgetwould give an extra level of assurance.About the tests: I was of course in need of a way to test multiple-file uploads to a single field via curl just as any other fix for this issue would be. I attempted to use Berdir's, but as soon as you change anything at all about the non-test side in patch 16, you get high failure counts including seemingly unrelated tests/asserts; drupal starts kicking back 403 forbiddens a lot. I ended up having to pull a packet trace between the test runner and the server into wireshark and have it tell me the manually generated multipart;form-data was malformed to get to the bottom of it; at which point I was highly motivated to find a less homebrew solution to that. So there's a way to do it without writing your own response body that you can see for yourself in the code. It provides an
uploadNodeFilesin FileFieldTestBase that may be useful for some other test later too.Since both fix and tests are 100% new, no interdiffs.
Likely I have committed some Drupal coding style sins, for which I apologize, but its a hard thing to never screw up while simultaneously learning a large new codebase. I will be grateful to anyone who points them out and/or fixes them.
Comment #31
fabianx commented#29: Tests look great to me. Thanks so much for your contribution! :)
Comment #32
mbayntonComment #33
mbayntonSo I was just browsing through the other critical issues for fun and noticed that some are being tested against PHP 5.4. I removed some PHP 5.4-compatbility code from the tests because the requirements for Drupal 8 say 5.5. I am confused?
Comment #34
RavindraSingh commented@mbaynton, you are correct, it should be passed against php 5.5. in one of the recent thread https://groups.drupal.org/node/473473
Comment #35
lauriii@mbayton: We have just started testing on PHP 5.5 because we agreed dropping the PHP 5.4 only a very short period of time ago and there was also technical stuff that needed to get addressed before we could start testing on PHP 5.5. That comment is 14 days old. All the new patches will be tested against PHP 5.5.
Comment #36
chx commentedNo need to apologize really, it's always good to see people new to core development in the critical queue. Welcome :) !
So
} else {this one, we use a line break:In
$args = array(it's good to break into multiple lines but you only need to indent by two spacesI also would move
drupal_realpath($file->getFileUri());into a variable to avoid writing it twice.Otherwise, I believe this is great work, please keep it up!
Comment #37
mbayntonI seem to be on the right track so here's the style issues fixed and some more test coverage. @chx thanks for taking the time to highlight the issues!
Comment #38
dawehnerEnsure that we document all parameters
Comment #39
RavindraSingh commentedExcept @dawehner, everything looks good to me. I have tested manually the issue and found its working in both state
Attached screenshot in JS-OFF state
Comment #40
mbayntonYeah @dawehner is not letting me off easy. The function is now documented for the first time in history :P
Comment #41
RavindraSingh commentedLooks good.
Once small thing needs clarification here. can't be use $nid instead of $nid_or_type because whenever we require bundle type we can use $extra['type'] ?
Comment #42
RavindraSingh commentedComment #43
mbaynton@RavindraSingh no, because it is overwritten:
This is code that was already there, and that tests expect to work that way.
Comment #44
vijaycs85First of all, great finding and thanks for filing this as bug and fixing @mbaynton. Here is my questions/comments:
1. This is a public method, so treated as an API and we are removing it. Not sure how that would affect contrib/sites that are using it.
2. This method provided validation + user friendly message. However the patch doesn't seem to provide this message anywhere?
Update: Just read the comment #29 and found my answer for #2 in this comment. So may be we can ignore that.
Comment #45
dawehnerI'm not entirely convinced that every public method should be treated as API function. It was public, because it was used as FAPI callback.
Comment #46
catchIn terms of the method removal, we could add a 'just in case' change record, but I'm not concerned about the API change as such especially given it's necessary to fix the critical issue. If we'd settled on marking things @internal, this is one that definitely should have been.
Comment #47
berdirI'd just say something like Additional values when a new node is created. entity_create() is deprecated so we should avoid adding more references to it and it's values, not settings.
Can you post a screenshot of the UI in case more than the allowed number of files have been uploaded?
Best compared with how the current UI would look like with the old fix.
Nice. Cleaning up non 5.5 code is fine here I think, although you should in general be careful about expanding the scope of an issue.
Would be good to confirm (attached a raw post request maybe?) that this really results in same behavior but I'm pretty sure this works. Otherwise Drupal wouldn't accept it :)
Comment #48
claudiu.cristeaNew patch.
Comment #49
dawehnerSo #47.2 and #47.4 is still open ...
Comment #50
claudiu.cristea@dawehner, @Berdir
I think the screenshot from the simpletest (
FileFieldWidgetTest) is enough. Creating a POST it's not straight.What you think? Let me know.
Comment #51
dawehnerIs that really what we want, validation on each individual delta?
Is this maybe just broken inline error messages? Just trying to be curious.
Comment #52
mbaynton@dawehner I've got a wee bit of time now, will try to determine what we're talking to clean that up. I assume depending on scope it may or may not be a this issue thing?
Comment #53
mbaynton@Berdir
I don't quite follow. Of course by the tests and the screenshot @claudiu.cristea kindly prepared, it results in the same behavior, but it does not match the POST a browser would generate -- browsers give you
name="files[field_image_0][]"and anothername="files[field_image_0][]"while the test code gives youname="files[field_image_0][0]"andname="files[field_image_0][1]".Comment #54
mbayntonSo I just wrote a longer version of this comment which vanished on Save...anyway trying again, I am having bigger issues now than duplicate error messages. The interface to remove files also falls victim to the same field validation, since there's too many values on the field the removes don't seem to be accepted. I could've sworn I tested this when I posted #40, and I also just tried against beta12 which behaves in yet another, different way, so I wonder if this is a fast moving target.
Anyway this needs work, either by fixing at least the unable to remove issue, or by bailing and going back to the #7/#16 approach.
Comment #55
mbayntonThe interface to remove selected images is accessible in HEAD by uploading < the max images, then uploading more images to get > the max. I am reproducing the issue where you can't remove anything at that point in HEAD, so maybe a reason to not just fall back to #7/#16 since those wouldn't fix this.
Comment #56
berdirI don't think we need to fix the validation problem here, pretty sure that's yet another of those inline error message problems.
Not being able to submit on validation forms however is a problem. The delete button probably needs .. #limit_validation_errors.
Comment #57
dawehnerYeah sorry. Let's refocus!
Comment #58
mbayntonThe immediate problem is FileWidget overwriting the #limit_validation_errors on the remove button set by ManagedFile to require the entire field widget to pass validation (line 397.) That just doesn't make sense, it's asking for this exact issue, but changing it of course has side-effects. I _think_ I'm mostway through a patch to clean those up and so far its still minor, but we'll see when I'm 100% done...
FileWidget definitely needs some love.
Comment #59
tim.plunkettI think at this point, the *only* code change we should make is the one in #8, s/getFieldName/getName, and the new tests.
We've uncovered several inconsistencies and major bugs, but none of them are directly related to the part of this issue that is critical.
@mbaynton, can you open a follow-up issue for the other problems you've found?
Comment #60
dawehnerYeah, we want to fix the critical asap and ensure we have the needed follow ups.
Comment #61
mbayntonOk, working on writing up a spinoff issue, there's really two things needing fixing
The first definitely fits my definition of "pretty bad" which I think can map to "Major", but the second probably isn't as big a deal, so a major and a normal issue it is.
Comment #62
mbayntonChildren are now listed on this issue. I can do the new patch tonight (no existing one has both fix and working tests) tonight if nobody else beats me to it.
Comment #63
kgoel commentedI am working on it
Comment #64
kgoel commentedThis patch is a combination #48and #8 that's why no interdiff. I took #8change and test coverage from #48.
Comment #66
dawehnerThe message is probably different now:
, I think we have to adapt the test.
Comment #67
cilefen commentedNo, the error message does not make sense. In this case I uploaded like 8 files onto a 3-file-allowed field.
Comment #68
berdirYes, it's the wrong number. And multiple changes have been discussed.
#16 had my fix for that, which can still be confusing, #19 hat a different fix which is still wrong (it would then say 4) and #23 and #25 has some more thoughts from me about the error message. #25 has two suggestions on how we could slightly change the text so it is correct, not sure which is better. Doesn't make a huge difference I think.
Comment #69
kgoel commentedAgain no interdiff since this is straight upload of #16. This was discussed on IRC. Tim Plunkett and dawehner suggested to put #16patch and it was RTBC in #17.
Comment #70
tim.plunkettAgreed, we now have two child issues to hash out all of the other changes that could be made here, but #16 was more than enough for this critical. Thanks!
Comment #71
dawehnerNice constructive work!
yeah we should learn to stay even more focused.
Comment #72
berdirI'm not sure about this, I think the test approach from @mbaynton was much cleaner than what I did. I think we just need to fix the assertion message there?
And I'm not sure if #68 was considered, I don't think that making a small change to the error message isn't that much out of scope for this issue, since we're already changing the line anyway.
Comment #73
mbayntonYes, what @Berdir said. I disagree with the RTBC because my experience with #16's test code is that it is buggy, but happens to pass exactly the change #16 introduces. It will inaccurately cause tests to fail when the response that must be sent changes, because it does not construct the multipart;form-data portion of the response in a complaint fashion in the general case. The response may need to be different for a reason as simple as testing with an extra field on the entity. Tl;dr, it'll start failing in unusual and mysterious ways for someone in future. Since in any case new test code must be introduced, and we have better test code available in #48, it is what should go in -- just with the changed error message in the assert.
Makes sense to me as well, although the improvement is not in a patch yet and if all goes well the child issues will result in this code not being around long-term anyway.
What's the protocol, if @Berdir and I would both not have RTBC'd, is that grounds to un-RTBC something?
Comment #74
mbayntonThis is the patch I'm behind. It was constructed from
Since it's a remix of previously posted code from a few 'branches' to this issue, I'm not sure what interdiffs would be of interest. I have most every stage of this issue in various git branches/commits locally if there's a particular interdiff someone wants generated, I could probably do it.
Comment #75
tim.plunkettThis is the patch I hoped would result in #64, but there were too many additional changes from iterations between #16 and #48.
This is also what I think @Berdir was asking for in #72.
RTBC +1.
Comment #76
webchickWowza. Major points for thoroughness, mbaynton! :D
Comment #77
fabianx commentedRTBC + 1, that is the fix I want to see.
Comment #78
alexpottCommitted 341f0e4 and pushed to 8.0.x. Thanks!
Yay! But we need to do more (in a followup). PHP5.5 defaults to still allowing @ to signify file uploads whereas PHP5.6 defaults to not. I think we should change to not supporting this as the switch of behaviour in PHP versions makes for an unstable test environment. Created #2538260: Set CURLOPT_SAFE_UPLOAD to TRUE in Simpletest's CURL options
Comment #80
cilefen commented#2537930: Clean up FileWidget's behavior when too many files uploaded
#2537940: Expose already-coded better UX when too many files are uploaded