If you choose file, upload it and remove, choose another file again ( with invalid extension), then error message about extension is shown and hidden immediately. Bug may be in modules/file/file.js in validateExtension function:
validateExtension: function (event) {
// Remove any previous errors.
$('.file-upload-js-error').remove(); // !!!! PROBLEM HERE - this function is called twice, second time with this.value == ''
// Add client side validation for the input[type=file].
var extensionPattern = event.data.extensions.replace(/,\s*/g, '|');
if (extensionPattern.length > 1 && this.value.length > 0) {
var acceptableMatch = new RegExp('\\.(' + extensionPattern + ')$', 'gi');
if (!acceptableMatch.test(this.value)) {
var error = Drupal.t("The selected file %filename cannot be uploaded. Only files with the following extensions are allowed: %extensions.", {
'%filename': this.value,
'%extensions': extensionPattern.replace(/\|/g, ', ')
});
$(this).parents('div.form-managed-file').prepend('<div class="messages error file-upload-js-error">' + error + '</div>');
this.value = '';
return false;
}
}
},
Temporarily I override this function in my custom js-file with this code:
Drupal.file.validateExtension = function (event) {
// Remove any previous errors.
if (this.value) // THIS CONDITION was added
{
$('.file-upload-js-error').remove();
// Add client side validation for the input[type=file].
var extensionPattern = event.data.extensions.replace(/,\s*/g, '|');
if (extensionPattern.length > 1 && this.value.length > 0) {
var acceptableMatch = new RegExp('\\.(' + extensionPattern + ')$', 'gi');
if (!acceptableMatch.test(this.value)) {
var error = Drupal.t("The selected file %filename cannot be uploaded. Only files with the following extensions are allowed: %extensions.", {
'%filename': this.value,
'%extensions': extensionPattern.replace(/\|/g, ', ')
});
$(this).parents('div.form-managed-file').prepend('<div class="messages error file-upload-js-error">' + error + '</div>');
this.value = '';
return false;
}
}
}
}
Comments
Comment #1
czigor commentedTested this and the solution works for me even for files without extension or ending with a period (eg. "filename."). Added a patch. All credit goes to bazel.
Comment #2
czigor commentedBy the way, does anyone know why validateExtension does not get called twice if we had not uploaded a file with a valid extension before? The line
this.value = '';should trigger the fileValidateAutoAttach behavior shouldn't it? (Which in turn calls Drupal.file.validateExtension.)
Comment #3
sun1) Opening bracket must be on the same line as the control structure (if).
2) Instead of indenting the entire function body, implement an early-return.
Powered by Dreditor.
Comment #4
czigor commentedHoops, that's been a bit long time. Corrections attached. D8.
Comment #5
czigor commentedtagging
Comment #6
quicksketchThis problem is caused by this API change in jQuery (from http://api.jquery.com/bind/):
What's happening here is Drupal.attachBehaviors() gets called multiple times on new content. In previous versions of jQuery that was fine because .bind() would only attach a unique function one time. Now it binds the same function again each time, even if it's identical.
In Drupal-convention, we should use .once() to prevent the behavior from getting attached multiple times. It's not real pretty but it's consistent with our code throughout the rest of core.
Comment #7
guschilds commentedThank you for supplying this patch! This worked for me in Drupal 7. I came across another issue for the same problem and commented in there as well: #1516492: File extension validation doesn't work after one upload.
Not marking as RTBC because I tested in 7.x and this is marked as 8.x-dev. I am not able to recreate this issue in 8 because it doesn't seem to be using validateExtension in file.js for any client-side validation, but I could be wrong.
Comment #8
David_Rothstein commentedI marked #1516492: File extension validation doesn't work after one upload and #1992028: JavaScript error message for incorrect image field extension doesn't work for the second upload as duplicates.
As discussed in the latter issue, you can actually reproduce the bug without ever hitting the "remove" button. If you have a multivalued file field (I tested with image fields) then uploading a valid file for the first one followed by an invalid file for the second one triggers the problem.
The above patch seems to fix both cases, but I haven't been able to test this for Drupal 8 either.
Comment #9
David_Rothstein commentedJust marked the following two issues as duplicates as well:
#1996072: No Error message on selecting not allowed file in Image field
#1998998: Illegal image type error message not shown if other error message already present on screen
The second one points out that the first upload attempt doesn't even need to be successful (if it's prevented by another upload validator the bug will still occur on the second attempt).
Comment #10
nod_tagging
Comment #11
impol commentedBackport #4 to D7
In my case this error appears in IE11 only.
Comment #13
chasingmaxwell commentedThis is a backport of #6 for Drupal 7.
Comment #14
David_Rothstein commented6: file_behaviors_once-1074214-6.patch queued for re-testing.
Comment #15
David_Rothstein commentedComment #18
lokapujyaComment #19
lokapujya13: file_behaviors_once_D7-1074214-13.patch queued for re-testing.
Comment #20
lokapujya11: 1074214__fix_IE11_Wrong_file_extension_JS_error_is_hidden.patch queued for re-testing.
Comment #22
impol commentedRecreated #11 patch.
Comment #23
David_Rothstein commentedStill needs fixing in Drupal 8, right? The latest Drupal 8 patch is #6 but needs a reroll.
Comment #24
lokapujyaI cannot recreate the issue in D8. The javascript in D8 seems to be reworked. Also, I don't know why we have 2 different D7 patches in #22 and #13.
Comment #25
chasingmaxwell commented@lokapujya, The reason there are two is that they solve the problem in two different ways. I preferred the once() approach originally provided by @quicksketch because it follows convention in Drupal behaviors elsewhere in core. So I backported it to D7.
Comment #26
evilehk commentedHere is a quick recap of the problem that currently exists in Drupal 7 core:
The expected result to see invalid file extension message outputted from file.js, but the message is quickly removed because validateExtension is called twice. The patch in #13 solves the problem. The same issue does not, or no longer, exists in Drupal 8 core. Therefore I set this issues Version back to 7.x-dev and removed the needs backport tag.
Comment #28
lokapujyaComment #29
zestagio commentedHello,
I tested manualy, and I have next behaviours:
I inspected js and found problem. On first render an upload field, this element has html id like edit-field-image-und-0-upload and added js settings for validation this element by id (Upload field use id of parent element and on each ajax request, js settings not updated, that error):
After click on "Upload" or "Remove" button, upload field will be re-generated with new html id like edit-field-image-und-0-upload--2, but js settings use old html id, and error message not added to page.
I attached the patch, wich fixed this problem
Comment #30
rafalB commentedPatch from comment #29 works for me.
Comment #31
neha.gangwar commentedPatch #29 worked for me also.
Please include this in core module.
Comment #32
jsobiecki commentedPatch #29 also worked for me, but it also introduced regression.
I have a form, that one of elements (select list) executes custom ajax call and updates other element.
Ajax callback returns single element, that will be updated after ajax call. There are no other actions in callback.
This form includes also two file upload fields, that are not related to this custom ajax call.
And scenario to reproduce is following:
1. I'm triggering custom ajax call.
2. This triggers Drupal.behaviors.fileValidateAutoAttach.detach method. This method
if (settings.file && settings.file.elements) { $.each(settings.file.elements, function (selector) { $(selector, context).unbind('change', Drupal.file.validateExtension); }removes validation callback from all file elements.
3. Later Drupal.behaviors.fileValidateAutoAttach.attach is executed. This code should re-register validation callback:
$.each(settings.file.elements, function (selector) { var extensions = settings.file.elements[selector]; $(selector, context).once( function () { $(this).bind('change', {extensions: extensions}, Drupal.file.validateExtension); }But it will not do that,s because once class was not removed from existing elements.
4. After that, existing file elements doesn't have validation callback registered and they accept any file type provided by user.
Attached file updates #29 with fix to described problem
Comment #33
jsobiecki commentedFixed typo in last patch.
Comment #34
yvesmarie commentedAs describe in #29, this bug seems to have two parts:
1. a behaviors attach issue: in some case
Drupal.behaviors.fileValidateAutoAttachis attached twice. First in theinsertajax command, and then again in theDrupal.ajax.successfunction.2. a buggy id generation issue: the file input id added to the ajax settings doesn't match the one in the generated markup when a counter is added to the base id.
However the second issue is a bit more complicated as it impacts other part of the managed file widget. Especially when we want to use the id for the widget label
forattribute. This issue is better addressed in https://www.drupal.org/project/drupal/issues/2594955 for which a patch already successfully fix the issue: https://www.drupal.org/files/issues/file-managed_file_duplicate_id_error...I submit a new patch that only address the first issue (the one initially describe in this topic). As previous patches it wraps every behaviors with jQuery.once; but, for better retrocompatibility, it also use removeOnce for cleaner behaviors detach.
Comment #35
taran2lDeletedComment #36
taran2lDeletedComment #37
taran2l@yvesmarie, thanks for the patch. It almost works, but there is a case when it does not: existing code relies on HTML id, which is not guaranteed to match $element['#id'].
Attached patch fixes this. Please review
Comment #38
taran2lComment #39
taran2l