Problem/Motivation
When uploading items to the media library widget, it is possible users have selected the wrong file. Possibly even containing sensitive information. It is currently not possible to directly correct this from the media library widget.
Proposed resolution
Allow users to delete items from the media library through the media library widget.
Remaining tasks
Write patch
UX review
Accessibility review
Code review
Commit
User interface changes
Video of patch #43: https://www.drupal.org/files/issues/2019-03-05/media-library-remove-medi...
Added remove buttons:

Hover state:

Focus state:

Message after removing an item:

Message after removing the last item:

RTL display.

API changes
Data model changes
Release notes snippet
Comments
Comment #2
ajay547 commentedComment #3
seanbHere is a first patch built on top of #3023802-12: Show media add form directly on media type tab in media library and #3020716-64: Add vertical tabs style menu to media library.
Comment #4
seanbCopying this over from #3023802-21: Show media add form directly on media type tab in media library
We should make sure users can correct "mistakes" when adding new media. I think allowing users to remove added media will solve this. It would be nice to get confirmation on this.
Comment #5
seanbReroll on top of #3023802-32: Show media add form directly on media type tab in media library.
Comment #6
phenaproximaTagging for screenshots. We will definitely need usability and accessibility review on this one.
Comment #7
phenaproximaYikes. This needs a big comment.
Comment #8
seanbFixed #7 and added some screenshots to the IS.
Comment #9
phenaproximaRet-titling for clarity.
Comment #10
panchoRTL display points to a problem with the counter being two strings rather than one. However, that doesn't seem to be introduced here.
Otherwise the patch looks awesome. Just a few nits:
1.)
First line has 81 characters, maybe even them out.
Same twice.
2.)
Do we want to settle with one or the other variable name?
3.)
$deltais so much more descriptive than the generic$i. You purged three or four$ikeys, so how about bringing the last one in line, too?4.)
Not your fault, but
$midsis so confusing, because unlike$nids,$tidsor$vids, it resembles an actual word. I was surprised to see our coding standard now allows camelCase variables, though not mixed. In a slight stretch of the coding standards, I'd propose$mIdsfor improved clarity, and clarity should IMHO always be the first goal.If considered a new convention, this would have to be separately discussed in general (think this would improve readability for
$nIds,$tIdsor$vIdsas well). Otherwise it might just be a solution for$mids.As I'm not yet confident enough to catch everything, we should have another review. But I think this is very close to RTBC.
Comment #11
seanbThe mentioned feedback is partially applicable to #3023802: Show media add form directly on media type tab in media library. I think you reviewed the combined patch 3023801-8-3023802-41.patch instead of 3023801-8-do-not-test.patch?
Sorry for the confusion, I'm building patches on top of patches to make progress, but I can understand how this is not always clear.
I will copy over your feedback and fix this in the other issue.
Comment #12
panchoIndeed, I did. No problem, though. I was just a bit surprised where you took the whole amount of new code in a quite good condition... :)
Comment #13
seanbReroll on top of #55 of #3023802: Show media add form directly on media type tab in media library. Everything in #10 should be be fixed.
Comment #14
panchoMoved nitpick from #3023802-60: Show media add form directly on media type tab in media library:
@seanB's in #61:
I'm not saying we should remove the focus styling. Rather we should make sure the button is reset to its original "X" (without the "close" hover) before the "afterglow" kicks in.
Comment #15
phenaproximaIs this really needed? I think the calling code sets #parents on the entity form element, so won't remove_button get a workable name generated automatically?
If we _do_ need it, this should get a comment too.
This can be one line:
$delta = array_slice($triggering_element['#array_parents'], -2, 1)[0].Comment #16
seanbReroll now #3023802: Show media add form directly on media type tab in media library is in!
#14
Added a fix that shows the text on focus. That looks a lot better and is better for a11y as well probably.
#15
1. Added a comment why we need it :)
2. Fixed.
Comment #17
phenaproximaCan we add a @see referencing removeButtonSubmit()?
Apart from this, I think the code looks good to me.
Comment #18
seanbDone!
Comment #19
phenaproximaThis issue is a security and privacy improvement, because without it: say you accidentally upload a file into the media library that you shouldn't. Right now, you would have to add the file to your media library, and actually save it into the media library -- which means it would be visible to anyone who has permission to see the media library -- and you can't remove it without going to another screen and deleting the media item (which you might not have permission to do).
With this patch, you can remove the bad upload right away, without needing to save it into the media library (for all to see) first.
Comment #20
berdir> This issue is a security and privacy improvement, because without it: say you accidentally upload a file into the media library that you shouldn't
Well. It would be more useful if we'd actually delete the file that you uploaded, but we don't. #2821423: Dealing with unexpected file deletion due to incorrect file usage. Maybe that whole topic should be moved under the media umbrella, so it receives some attention again? Working only through media entities vastly simplifies the possible file usage problems as it is really just tracking the media entity usage and it is left to the user if and when he wants to delete that.
Comment #21
phenaproximaTrue, but a cruft file that is marked temporary and not saved to the media library is, at least, not immediately visible to anyone who has permission to use the media library, and will be deleted later by cron (as far as I know).
Comment #22
berdirYes, it is not listed, that is true. *But* it is not marked as temporary with the default settings and will therefore also not be deleted. That was disabled by default in 8.4 or 8.3 and nobody touched the file usage issues anymore since that happened :)
Comment #23
marcoscanoI agree it would be ideal to avoid leaving the file languishing around, if we can. This could be a follow-up improvement though, if you feel it doesn't belong here.
Sorry if I'm missing part of the story, but at this point isn't it safe enough to assume that the file
$added_media[$delta]points to is not being used anywhere else (because it was just uploaded), and then we can just delete it right here?This would avoid the need to track any usage at all, as long as this form is not used for in-line editing (which I don't think it will, but I could be wrong here).
Comment #24
berdirSorry if I was unclear, I did definitely not expect to solve that problem here, I'm just trying to bring some light into a rather dark corner of Drupal core that everyone is ignoring and that we should really improve (anyone trying to follow GDPR is going to have a problem with that eventually).
But yes, in this specific case, we can possibly just mark the file as temporary or even actually delete it directly.
Comment #25
seanbWe have tests in
MediaLibraryTest::testWidgetUpload()to ensure uploaded files via the media add form in the media library are temporary until the form is saved.Since the media add form for the library is a custom thing, I think this is already handled.
Comment #26
phenaproximaNeeds a reroll after #2996029: Add oEmbed support to the media library.
Comment #27
damienmckennaRerolled patch #18 against the latest 8.7.x codebase.
Comment #28
damienmckennaComment #29
damienmckennaComment #30
seanbAdded small padding to the remove button to make the click area a little bigger and the focus style better. Also added minor doc improvements and remove button tests for the oEmbed add form.
Comment #31
wim leers@Berdir++ for drawing attention to #2821423: Dealing with unexpected file deletion due to incorrect file usage :) I agree with #23 that it doesn't belong here. Ideally, we could do what @Berdir suggests in #24. But … the good news that this is already happening: — this is already true :) The "removal" functionality is really only available immediately after creating media, BEFORE it is saved, so
status=0is by definition still true. So, AFAICT @Berdir's concerns are already addressed.(I just tested it manually while reviewing the patch and was pleasantly surprised this just happens to already work as one would hope!)
🤔 This is setting a lot of stuff. I would like to see a review from @lauriii on this.
🤔
… and the wrong media item is removed.Right?
🔍🐛 Nit: "Prevent … preventing …" is hard to read, how about "Ensure … preventing …"?
👍This is surprisingly elegant!
Comment #32
phenaproxima+1 on a front-end review from Lauri.
Comment #33
seanb#31
1. Fixed. Guess I broke that in the last patch, because it worked before. Thanks!
2. We are overriding the default button styling (and that contains a lot).
3. Correct, no matter which button you click, the last item is removed.
4. Fixed.
5. Yay!
Comment #35
seanbReroll now that #3035434: Improve name of MediaLibraryWidget dialog landed.
Comment #36
phenaproximaI think this needs a comment.
Because we're using BEM in this style sheet, I think the selectors can simply be
.media-library-add-form__remove-button, plus whatever modifiers are called for.Thanks for this excellent and useful comment :)
Question -- should we re-key the media items (i.e., array_values($added_media)) here?
Should we also assert that the file entity itself is marked temporary?
Comment #37
seanb#36
1. Fixed.
2. Fixed. You are right :)
3. Yay!
4. Fixed. We apparently can.
5. We already have tests for that. We could repeat it, but it wouldn't add any value imho.
Besides that, added an announcement for the add/remove actions in the form and made sure the focus is set on the first tabbable element in the form after something is added or removed.
Comment #38
phenaproximaWe don't need to store $clicked_button in its own variable.
This is not a correct usage of assertJsCondition(); I think you're just supposed to pass the JavaScript directly to $this->assertJsCondition().
Comment #39
phenaproximaKicking back for those things.
Comment #40
seanbFixed #38.
Comment #41
seanbRerolled now #2981044: Unify the grid/table views of the media library landed.
Comment #42
phenaproximaWe demoed this to the UX team today, in the presence of the mighty @andrewmacpherson. Feedback was very positive and, with a few changes, we have sign-off from both the usability and accessibility angles.
I think the path forward is clear; let's get this done!
Comment #43
seanbNew patch to:
- Change the DOM order so the remove button is after the fields.
- Add a remove message to the form
- Fix an issue with the focus state having a border while it shouldn't
Also updated the tests and added new screenshots and a video to the IS.
Comment #44
phenaproximaTo guarantee the correct ordering, maybe we should set explicit weights on the preview, remove_button, and fields sub-elements.
Interdiff looks good otherwise.
Comment #45
seanbAdded the weights.
Comment #47
seanbThat was an unrelated fail. Back to NR.
Comment #48
lauriiiLet's add a todo to remove
[type="submit"]from the selector when this is moved to Seven.Comment #49
seanbAhh, you are right! Not sure what to do about this. We could:
I think this needs a11y feedback.
This has come up before. This is a case where nobody really know how big this problem actually is, and since the design of the remove button came from #2113931: File Field design update: Upload field., which has been looked at multiple times and has UX signoff, I think we should just continue and find out if this really is a problem or not. Changing the icon later should be an easy change, but I think we need real world feedback to decide if this is needed or not.
Will add that when I know what to do with the first point.
Comment #50
phenaproximaI'm very much in favor of this option. It makes the most sense from a usability perspective, and if it doesn't compromise the accessibility of the Remove button(s), it's pretty much perfect.
Comment #51
seanbFixed!
Implemented option 3 from #49. The reason I did this:
1. The delete button, since it is a destructive action, should probably not get the focus by default.
3. Skipping the entire form and go the the save button could give the impression that there is no form/remove button for screen reader users. Screen reader users could potentially miss information if we do this.
Also fixed an issue for the status messages when the last item is removed. It used to look like this:

Now it looks like this:

Comment #53
seanbFixed the tests.
Comment #54
rainbreaw commented@seanB and @phenaproxima demo'd this to me via screencast today to review accessibility concerns, and the functionality seems to pass accessibility checks with the current patch:
A couple new questions:
Comment #56
seanbIncorporated the newly added wrapper div for the added items from #3023797: Let users choose what to do after selecting and/or adding items in the media library to make sure the patches are going to work together.
Regarding the questions in #54:
1. Fixed this in this patch. When there are more items to remove, the focus is automatically set to the next remove button.
2. I don't think we remove the outline on the focused state? Just double checked. Also see the screenshot in the IS.
Comment #57
seanbAdded an AJAX message for when the remove button is clicked (like in #3035446: Inform assistive tech users about the outcome of using the MediaLibraryWidget dialog) and optimized the remove button AJAX callback a bit.
I'm also tagging this for backport to 8.7.x, since Media Library will hugely benefit from this UX improvement and, as an experimental module, this patch may be eligible to land in alpha.
Comment #58
phenaproximaThis seems like it should use BEM (media-library-add-form__description)...?
Comment #59
seanbThat shouldn't even be in here, copied a bit too much from #3023797: Let users choose what to do after selecting and/or adding items in the media library. I did find that
media-library-add-form-mediacould also make better use of the BEM convention. Sincemedia-library-add-form__mediawas already used for the add media items, I renamed the wrapper tomedia-library-add-form__added-media.Comment #60
seanbRerolled now #3035316: ‘Add media’ in MediaLibraryWidget should be a button, not a link landed.
Comment #61
seanbNew reroll now #3037767: Improve responsive styling of grid items in the media library landed as well. No changes since #60.
Comment #62
phenaproximaNothing major found!
Should we add a todo here pointing out that we can remove the ID once the AJAX system allows targeting by selectors that aren't IDs? (With a @see to the issue.)
To allow extending classes to more easily insert things between these controls, I think the weights should be 10, 20, and 30 respectively.
I don't think this needs the #media-library-add-form-wrapper prefix.
Should this class name be using BEM? (media-library-add-form__input-wrapper)?
Also, I'm not really clear on why this container element is needed...if it's really needed, can the comment explain that better?
Is this still necessary?
I don't think this needs the #media-library-add-form-wrapper prefix.
Nit: There is no need for $filename to be its own variable. The string can just be "The media item $png_image->filename has been removed."
Copypasta :) We're testing oEmbed here, so the item was not uploaded.
#media-library-add-form-wrapper not needed here.
Comment #63
seanbFixed #62.
1. Fixed.
2. Fixed.
3. Fixed.
4. Fixed.
5. Yes, type_three is only first for field_twin_media.
6. Fixed.
7. Fixed.
8. Fixed.
9. Fixed.
Comment #64
phenaproximaOkay! Thanks for fixing my feedback.
This has now been code reviewed multiple times, demoed to the UX team more than once (and approved), and been through an accessibility review thanks to @rainbreaw. It's a major UX improvement with (as the issue tags suggest) privacy implications, and there is extensive test coverage, so I see little reason to wait longer to land this one.
RTBC once green on all backends.
Comment #68
webchickThanks so much, @rainbreaw for jumping in here on the accessibility side! Adding credits for everyone here, plus the participants in the March 5 UX meeting https://www.youtube.com/watch?v=t_UVusCsurI
Comment #69
lauriiiSorry to bump this back to NW, but since this patch contains some duplicate changes with #3023797: Let users choose what to do after selecting and/or adding items in the media library, the feedback I posted there is relevant here as well.
Comment #70
seanbFixed #69. I think this was referring to comment #64 and #66 in #3023797: Let users choose what to do after selecting and/or adding items in the media library regarding the code below.
Added some documentation to clarify why we need it.
Comment #71
phenaproximaLooks like Lauri's feedback is, indeed, addressed. I think we are good to go.
Comment #72
lauriiirole="list"and the list itemsrole="listitem. We should also addaria-labelattribute to the list items, as well astabindex="-1". This would allow us to focus on the previous media item after removing media from the list.I also couldn't figure out what is this needed for. I removed this and tested manually different possible scenarios and couldn't get the outline visible. If this is indeed needed, let's add some documentation here as well.
Could we include an explanation on why the outline isn't needed?
Nitpick: let's format these according to our coding standards.
I think this comment would be the most benefitial if we explained a scenario where the focus is shifted here (and why) 🙂
Comment #73
seanbFixed #72.
1. Fixed. This is a nice improvement. Thanks!
2. Not sure which element your comment is referring to? I did find that the following code didn't do anything:
3. Fixed.
4. Fixed. I changed all comments in the CSS to follow the coding standards.
5. Fixed. Added some more docs.
Comment #74
phenaproximaI gave this a quick manual test and found a little bug that needs fixing. Try this:
This won't be automatically testable since Mink doesn't support multi-file uploads. So I'm marking this for manual testing.
The good news is, I gave the widget a quick test using the keyboard. It seemed to work quite nicely -- the tab order made perfect sense. In addition to that, the interdiff makes sense; the added comments really do a lot to clarify some of the quirks of our implementation. So, thanks!
Comment #75
seanbApparently we CAN test multiple file uploads (thanks @lendude for the tip!). Fixed #74 and added tests for it.
Comment #76
phenaproximaCouldn't we just use max() here? (Since it's a variadic function, we'd probably need to do something like
$nth_child = call_user_func_array('max', array_merge(array_keys($added_media), [$delta])) + 1;This seems like a good place for
$this->assertNotEmpty($assert_session->waitForText('Add or select media')).When I encountered this bug, I left one of the alt text fields empty. Can we do that here, just to verify that it works in that case (it will also serve as proof that the remove button does not try to validate the form).
This test uses a LOT of very long selectors, and that makes it hard for human brains to parse. I think we might want to do something like
$first_media = $assert_session->elementExists('css', 'selector-for-first-media-item'), and then run further asserts on that (like$assert_session->fieldExists('Name', $first_media)). This might be a lot more readable.Comment #77
seanb#76:
1. I don't think so? Max returns the highest value, while we want the next value. The problem is we can't predict what that value is going to be. For example, we have delta's [1,2,3,4,5,6]. When we delete number 3, the focus should be shifted to number 4 (that's predictable). If we then delete item 2, the next item will be 5. That second case is the problem. The max() would return 6 in both cases. What we actually want is to know what the sequance number is of the first element with a delta larger than the removed one, and as far as I could find there is no function that does that.
2. We actually do this in 13 places in the test. I think it would be best to keep it consistent.
3. Fixed.
4. We don't really have delta based selectors we can use. We could use nth-child() selectors, but that would also be a little hard to grasp since item 3 becomes number 2 if you remove something. The field name is the only thing that contains the delta consistently (so
media[2]staysmedia[2]).Comment #78
seanbHere is an attempt to improve #76 1 and 4. I have added a data attribute
data-media-library-added-deltacontaining the delta of each added media item. I used a data attribute instead of a class, because this allows us to target the delta very specifically, and because we don't use this for styling at all.This data attribute allows us to replace the nth-child stuff with actual delta's. We still need to loop over everything until we find the item we want to set focus on, but at least we no longer have to worry about the difference between the position of the media item and its delta. Which hopefully makes the code easier to understand.
This also allows us to make the tests a bit more readable.
Comment #79
phenaproximaEven though this isn't strictly necessary for things to work, I think I like it. I think it increases clarity of the code, and improves future flexibility (and potentially themeability). +1!
I think these interdiffs look right to me, and we have put this patch through its paces enough times at this point. The privacy hole is filled -- if there are other bugs after this, we can deal with them in follow-up issues. Go team!
Comment #80
larowlanwould it be better to use array_values here instead so we've always got a sequential set of deltas and avoid doing it during submit - or does that mess with the parents logic?
should we be checking this is the first tabbable element using .get(0)?
Comment #81
seanb1. We had that, but that causes the bug in #74. So I think we need to keep the delta's the same.
2. Added :first to the selector, that should also do the trick and that way we could do the same in the
InvokeCommand().Comment #82
gábor hojtsySuperb, thanks all! I really like how this came together with feedback from all angles.
Comment #87
idebr commentedThis issue was a big improvement for the usability of the Media widget! However, a vanilla installation will still leave the removed file for 6 hours before it is removed with a cron task. I think we can improve this situation by removing the file immediately when pressing the 'Remove' button. This option was actually already suggested by Berdir here in #24:
To this end I have added a patch at #3073734: Immediately delete a media's file when pressing 'Remove' in the media creation modal to remove the file attachment immediately after clicking 'Remove' in the media creation modal.
Comment #88
wim leers