Updated: Comment #N
Problem/Motivation
When a file field is used more than once on a page, the AJAX upload or remove will always target the first instance.
One example for reproducing this is if two entities reuse the same field, and their edit forms are displayed on the same page.
Steps to reproduce in #22
Proposed resolution
Use drupal_html_id() for the ajax-wrapper added by file_managed_file_process()
Remaining tasks
Find solution.
User interface changes
N/A
API changes
N/A
Original report by @eme
When you have multiple forms containing the same file, it is not possible to remove it.
Let's take an example : I have nodes with comments and the comment forms on the page, and I have a file field in the comment form. I can load the file, but not remove it.
Why ? Because when you load multiple forms you have additionnal ID, like form-id--x, and this additionnal ID is incremental for any form loaded to make sure they got a unique id. Issue is that when you load the file via ajax, it has the issue of not taking again the original ID, but the normal one, and therefore it acts on the first form ! In some configurations, ajax callback breaks and you are just redirected.
Note that this does not happen to the first one that has no additionnal id and therefore works fine.
Attached is a little video demo.
Must say I'm working on that for some days and it seems quite tricky...
Comment | File | Size | Author |
---|---|---|---|
#29 | file_field_ajax_wrapper-1811100-29-D7-do-not-test.patch | 2.13 KB | redndahead |
#21 | file-field-ajax-wrapper-1811100-21.patch | 2.34 KB | jyotisankar |
screens.zip | 1.53 MB | eme |
Comments
Comment #1
Anonymous (not verified) CreditAttribution: Anonymous commentedIs this issue present in 8.x-dev?
Comment #2
eme CreditAttribution: eme commentedJust tried to test with Drupal 8, but this issue is hard to test easily because Views 8 does not work (it call neither comments nor comment form on its page when option is selected).
Any idea to call easily multiple comment forms (or other) ?
Comment #3
eme CreditAttribution: eme commentedI just digged into file.module, and what is for sure is that the bug should be in D8 as well : we define the ajax wrapper for all filefield the same way : see lignes 1043 && 1114 :
So if we have the same wrapper for all file fields of same element ID, we will for sure have an issue. As I do not see anything unique in $element, I tested with a uniqid() added and it seems to solve this first issue.
I think there is another issue and will post it when I have additionnal data, but it occurs in more complex situation where we modify multiple files adding / deleting (seems to be related to the process that comes toward drupal_html_id.
Comment #4
eme CreditAttribution: eme commentedIndeed, last bug that is quite difficult to explain and for now disappears with a quite horrible hack in drupal_html_id : a uniqid() instead of strtr(drupal_strtolower($id), array(' ' => '-', '_' => '-', '[' => '-', ']' => ''));
This is no solution so it still to be digged, but it has an interesting seems that with multiple AJAX call from different forms, we loose some data in ajax_html_ids that aggregate all ids that should be.
For me, the bug do not come from drupal_html_id that works fine, but from file module that forgot to add dynamically its ids.
To resume how file modue does, here is the nesting of functions :
file_ajax_upload (ajax callback) > drupal_process_form > drupal_rebuild_form > form_builder > drupal_html_id
During this process, ids corresponding to files seems to be just lost and therefore not incrementing (so AJAX behavior applies to anoter file field).
Hope this is clear enough...
Comment #5
eme CreditAttribution: eme commentedComment #6
eme CreditAttribution: eme commentedThe following hack in common.inc (function drupal_html_id) solves the issue as well :
Which means that the issue comes specifically from the naming of the upload / remove buttons that do not take the correct ID. Still the issue is not to be search in this function : correct naming are not added to $_POST['ajax_html_ids'] for me.
Comment #7
andypostProbably it's duplicate of
#1575060: ajax_html_ids are broken for forms with file element (encoding=multipart/form-data)
#1305882: drupal_html_id() considered harmful; remove ajax_html_ids to use GET (not POST) AJAX requests
#1414510: Remove drupal_reset_static for drupal_html_id() when form validation fails
Comment #8
andypostDuplicate of #1575060: ajax_html_ids are broken for forms with file element (encoding=multipart/form-data)
Comment #9
tim.plunkettThis is not a dupe, it's very much its own issue.
I hit this on D7, and the offending code is identical in D8.
Comment #10
tim.plunkettThis is the wrong fix. $element['#id'] is already run through drupal_html_id(). The actual bug is that since there is never an actual form element with that ID printed to the page, Drupal.ajax.prototype.beforeSerialize does not add it to $_POST['ajax_html_ids'].
Comment #11
andypostyep, this bug is different...
Comment #12
tim.plunkettSo multiple file fields do not suffer from this problem because their element ID is used by the fieldset wrapper, and ajax.js is able to discover the ID.
But single file fields do not have their ID printed, so it is never added to $_POST['ajax_html_ids'].
Merely printing this div on the page is enough to fix the bug.
Comment #13
andypostmakes sense, but needs test coverage, also second div could break styling
Comment #14
tim.plunkettSince drupalPostFormAjax doesn't actually run ajax.js, the only test we could add is that the ID is displayed on the page. Which seems like a useless test...
Comment #15
andypostSo manual testing is needed, also having a test for elemnt IDs is in page markup could be helpful
Comment #16
tim.plunkettI've manually tested it on D8 and D7. And it is directly added to the HTML being output, how could it not be on the page?
Comment #17
yched CreditAttribution: yched commentedGot hit by this too, and attached patch fixes it.
Wondering, however, whether we really need two different IDs there - $element['#id'] and $element['#id']-ajax-wrapper.
Why don't we just use $element['#id'] as the ['#ajax']['#wrapper'] ?
Attached patches (D7 & D8) do that, seems to work.
Comment #18
atrocity CreditAttribution: atrocity commentedPatch in post #12 for D7 works fine for me. Thanks for this.
Comment #19
jhedstromComment #20
yched CreditAttribution: yched commentedRerolled the patches in #17.
- D8 patch needed a refactor now that the code moved from file_managed_file_process() to ManagedFile::processManagedFile()
- D7 patch is a straigth reroll (#17 still applied with fuzz)
Comment #21
jyotisankar CreditAttribution: jyotisankar commentedComment #22
andypostSteps to test, done manually via simpletest.me
1) Configure comment type with file field admin/structure/comment/manage/comment/fields
2) Configure node type teaser to display comment field /admin/structure/types/manage/article/display/teaser
3) Create 2 nodes with comments enabled /node/add/article
4) Try to upload file, FAIL
Recent log contains exception and warning
Preview is broken
Comment #23
yched CreditAttribution: yched commented@andypost: why did you reset to NW ?
Comment #24
Fabianx CreditAttribution: Fabianx commentedThe ajax wrapper is doubled btw., which is another bug that should be fixed here, too.
Comment #25
andypostThere's special issue #2245901: Trim trailing EOF whitespace from templates
Comment #26
Fabianx CreditAttribution: Fabianx commentedNo, it is doubled even before the first AJAX takes place once by Element and once by FileField.
Both use #prefix, but its at different levels.
Comment #27
idebr CreditAttribution: idebr commentedI'm assuming @andypost in #25 was referring to #2346893: Duplicate AJAX wrapper around a file field
Comment #28
dcam CreditAttribution: dcam commented#21 still applies. Removing "Needs reroll" tags in preparation for Drupalcon Los Angeles sprints.
Comment #29
redndahead CreditAttribution: redndahead commentedReroll for d7
Comment #42
quietone CreditAttribution: quietone at PreviousNext commentedI tested this on Drupal 10.1.x, standard install using the steps in #22. It worked fine, there were no errors. The steps in #22 seem different that what is described in the Issue Summary. So, I tried again.
I made a new content type with two file fields. I tested creating and save with different files for each field, with the same file for each field, and only a file in the first field and only a file in the second field. In all cases, there were no errors and the files were related to the correct field.
Considering that there has been no discussion here for 8 years, I am going to say that this is now outdated.
Therefore, closing as outdated. If this is incorrect reopen the issue, by setting the status to 'Active', and add a comment explaining what still needs to be done.
Thanks!