Problem/Motivation
In #3020716: Add vertical tabs style menu to media library the media library has been split up in different tabs per type. We want to show the source field on the tab of each media type so users can add media of that type directly.
Different media types can have different types of source fields. We currently only have an upload form implementation to add media items from the media library widget, but there is also an issue to implement oEmbed support (#2996029: Add oEmbed support to the media library). Since we don't have a base class at the moment we need to duplicate a lot of code there.
Besides that, we also want to simplify the add/upload forms to only support 1 media type at a time based on the assumptions defined in #3019150-3: Update/improve mockups and designs for the media library.
Proposed resolution
We should have a base class containing all the generic code needed to add media items.
Add an upload source field implementation on top of this class to support media types with file uploads as input.
Show the upload form directly on the media type tab implemented in #3020716: Add vertical tabs style menu to media library.
Remaining tasks
Review and commit the patch.
User interface changes
Before
Given a media reference field on a node form, you would click the "Add media" button on the node form (not the one depicted below) and get something like this:

If you click the blue "Add media" button, the modal is refreshed and contains only a standard file upload field. When you upload files, the modal asks you to fill in the required metadata. When done, you hit a "Save" button. The modal closes, and the uploaded items appear in the node form, referenced by the field.
After
When you click the "Add media" button, you see this:

The blue "Add media" button is gone, replaced by a file upload field. If you upload a file (or files) into it, the modal refreshes with the form asking for required metadata. When you click "Save", the modal does not close, but instead returns you to the initial screen, with your new upload selected (and at the top of the list).
API changes
An internal form class is split into a base class and a concrete implementation, but both are still marked internal. Media source plugins now officially accept a 'forms' array in their plugin annotation, but so far the only consumer of that information is Media Library.
Data model changes
None.
Release notes snippet
TBD
| Comment | File | Size | Author |
|---|---|---|---|
| #57 | 3023802-55.patch | 84.52 KB | seanb |
| #57 | interdiff-53-55.txt | 3.8 KB | seanb |
| #55 | 3023802-55.patch | 84.52 KB | seanb |
| #55 | interdiff-53-55.txt | 4.23 KB | seanb |
| #53 | 3023802-53.patch | 84.62 KB | seanb |
Comments
Comment #2
seanbAttached is a full patch built on top of #3020707: Streamline buttons in the media library widget and #3020716: Add vertical tabs style menu to media library. Also a patch with just the changes for this issue.
Comment #4
seanbThere was a stupid typo made last minute while creating the patches. Here is another try.
Obviously, this issue is blocked on #3020707: Streamline buttons in the media library widget and #3020716: Add vertical tabs style menu to media library for now.
Comment #5
seanb#3020707: Streamline buttons in the media library widget has landed.
#3020716: Add vertical tabs style menu to media library was changed quite a bit since #4.
Attached is a reroll with some minor improvements:
- A better class name for the upload form to be more in line with the change proposed in #2996029: Add oEmbed support to the media library.
- Added access check for media type add form to the UI builder. The form should obviously not be shown when a user does not have create permissions.
- Added a test for add form access.
Comment #6
seanbAnother reroll on top of #3020716-51: Add vertical tabs style menu to media library.
Comment #7
phenaproximaPartial review...
We should clear it with the framework managers, but I think this is a clear signal to implement PluginWithFormsInterface on MediaSourceInterface and MediaSourceBase. Since there is a base class, I suspect we'd be covered under the BC policy.
In case anyone asks: the reason we're changing this here is because, in the not too distant future, the oEmbed media source will also include an "add" form which is exposed to Media Library. So it no longer makes sense to refer to it as an "upload" form, since uploading files is only one possible strategy that will be used to add media to the library.
No need to change anything here, just explicitly calling it out.
Let's change all references to the medium image style to use the new media_library image style that we now ship with. Since that is our fallback style, we can assume it always exists, and totally remove this member from this class and its subclasses.
Can we have a comment explaining why we're adding this to the form?
Should we maybe use BEM conventions here? I think that media-library-add-form__without-input and media-library-add-form__with-input might be a bit clearer.
Or, better yet, maybe we should use multiple classes. How about ['media-library-add-form', 'with-input'], to increase styling flexibility?
I wonder if this should be moved into its own protected method. ($this->buildActions())
Perhaps this method should be abstract. I'm not sure why any subclass would *not* want to implement it.
Can we rephrase? "#parents is set here because the entity form display needs it to build the entity form fields."
We can safely change this to use the media_library fallback style. It's not being stored in configuration at all, so changing it here won't affect any upgrade path we need to write.
"...as an editable field..."
Let's make sure that the tests assert that this field is not present in the media library form display.
Can we abstract this into a protected method, validateMediaEntity(), so that subclasses have more flexibility to override the per-entity validation logic? This is a similar pattern to what's going in submitForm().
This could use a comment or two.
Let's rename $value to $source_field_value, for clarity.
I find this awkwardly placed. I wonder if this stuff should be done in a new protected method ($this->setMediaType($form, $form_state)) so we can do this validation in there.
Comment #8
phenaproximaLet's be sure the tests assert that this text appears.
We should use $form_state::hasAnyErrors() here.
Can we expand the doc comment here to explain why and how this parameter would be set?
Let's change the doc comment here: "The current state of the media library, derived from the current request."
If we use PluginWithFormsInterface, this can all go down to one long line:
$form_class = $this->entityTypeManager->getStorage('media_type')->load($selected_type_id)->getSource()->getFormClass('media_library_add');"...with a new version..."
Wouldn't setRebuild() have the same effect? If not, why not?
Comment #9
seanb#7
1. Ok, let's wait for feedback from the framework managers.
2. Thanks!
3. Fixed.
4. Fixed.
5. Fixed.
6. Fixed.
7. Fixed.
8. Fixed.
9. Fixed.
10. Fixed.
11. Fixed.
12. Fixed.
13. Fixed.
14. Fixed.
15. Refactored the methods a bit since I agree some things were being done in awkward places. Most important changes:
getMediaType()the get the media type in a uniform way instead of it magically being set as a property somewhere and depending on it being there.createFileItem()so we don't have to store the validators and upload location as properties (they are only used once).updateFormState()to store the created media items in the form state instead of a property on the class. This allows us to abstract some code every subclass needs to implement and provide better documentation.#8
1. Fixed.
2. Fixed.
3. We need this for #3023797: Let users choose what to do after selecting and/or adding items in the media library when users could potentially return to the media library after adding new media, so let's add it there instead of this issue.
4. Fixed.
5. :)
6. Fixed. See 3.
7. This is no longer relevant for this issue since the code has been removed until #3023797: Let users choose what to do after selecting and/or adding items in the media library, but FYI.
That didn't appear to work. Form input is always processed in the following cases (see
formBuilder::doBuildForm()), so the only way to stop the form processing is setting empty input in the form state.if ($form_state->isProgrammed() || (!empty($input) && (isset($input['form_id']) && ($input['form_id'] == $form_id))))Comment #10
seanbAfter testing the patch with #2113931: File Field design update: Upload field. I noticed some small changes we should make to allow everything to work together, and some small UX improvements.
Besides that I removed a unnecessary call to
OpenModalDialogCommandand updated to way we fetch the field ID (since that was changed in #3020716: Add vertical tabs style menu to media library).Comment #11
phenaproximaI'm not sure we need both with-input and without-input classes. .media-library-add-form, by itself, can target the form without any input.
Can this rule get a comment explaining why :last-child is added?
I know I wrote this originally, but now this array_merge seems weird to me. IMHO, we should just explicitly set the class four times, like so:
I think this will be less intrusive, not to mention more readily understandable, than array_merge().
I hope this doesn't constitute a BC break. Even if it does, I don't think we can keep it around -- the very form it refers to is disappearing, and that code path is dead as a doornail. So if this is a BC break, then as far as I as a subsystem maintainer am concerned, it's a necessary one in an experimental module. No need to change anything here, just pointing it out.
This isn't exactly a static cache (or at least, that terminology has a different connotation), so can this simply say "The media type being created by this form"?
I'm tempted to say that we should make this private, specifically to force subclasses into using $this->getMediaType() where possible, which means that any validation or processing they do in getMediaType() will always run (which, IMHO, reduces the number of possible vectors for bugs). For now, let's hold off, but this is yet another question I want to defer to a framework manager.
This exception message no longer makes sense. How about "The '$selected_type' media type does not exist"?
As noted above, I think we can remove this class.
I think that buildActions() should be responsible for building the _entire_ actions element, including the #type => 'actions' piece of it.
Nit: These calls are chainable.
I think we should rename this method to prepareMediaEntityForSave(). Longer, yes, but clearer! preSaveMediaEntity() sounds like a pre-save hook, which is completely different :)
hasAnyErrors() is static, so this should be $form_state::hasAnyErrors(). Also, we don't need to use !empty() -- hasAnyErrors() returns a boolean, so calling empty() is redundant.
This isn't needed. We can just do $media_type = parent::getMediaType($form_state).
Nice!
Is there some reason we need to render the upload help now, rather than just set it on $form['upload_help'] and let it be rendered later? If so, we need to document that with a comment.
Should be
$form_state::hasAnyErrors().Let's throw \RuntimeException instead of just \Exception.
Comment #12
seanb#11
1. The add form has 2 different states that look quite different. The first step is showing the source field. The second step is showing the fields for the created media items. The only way to differentiate between the 2 states from the frontend are these classes. So even though we can technically remove 1 of the classes, detecting the other state through :not('with-input') does not feel like the best solution to me.
2. Fixed.
3. Fixed.
4. Thanks.
5. Fixed.
6. Leaving this for now.
7. Fixed. We can remove the entire exception since the state validation enforces a valid selected type :)
8. See reply in 1.
9. Fixed.
10. Fixed.
11. Fixed.
12. Fixed.
13. Fixed.
14. Yay!
15. Fixed.
16. Fixed.
17. Fixed.
Comment #13
seanbNot sure what happened to the interdiff. Here is the right one.
Comment #14
larowlanThere is a lot of work gone into this, its really appreciated.
There is a lot of css/js here so adding Needs frontend framework manager review tag.
do we need RTL equivalents here?
should we use getElementById here instead of jQuery?
note to self, do we check access on this
we should pass along the request here
the only place I see this used is in the routing and in tests.
we deliberately decided not to make controllers services in the D8 design process because we didn't want to bloat the service container.
I don't see a reason to do it here either.
Am I missing something?
We have ContainerInjectionInterface if you need to inject stuff into the controller and we have the ControllerResolver if you need to instantiate the class in a test.
Why do we hide them with CSS and not use form modes?
we're calling this in the array_map which means we're doing the getStorage and the getSourceFieldame each time, but really we could do it once and pass it along
I think we need an anti-tamper token here too
should we inject this?
i feel we should be passing the request object here, this is a non-static method, we have container injection, we should fetch it and pass it along
Once we do that, we make the parameter required and it no longer needs the singleton
should we inject here instead of using the singleton?
yeah, defs should be adding an anti-tamper token to prevent people meddling with the allowed types
the last one is always selected?
are we sure a global static is the right choice? - from the uses I see, could be a constant
what controls are in place to prevent someone from editing the dom on this and adding in references to media entities they cannot access?
we should pass on the request here, obtained from DI on the plugin
Comment #15
seanb@larowlan Thanks for the review. I think most of the feedback is applicable to #3020716: Add vertical tabs style menu to media library. It's kind of confusing that I am building patches on top of patches, but I guess we can't avoid that.
Could you copy the review over to the other issue as well?
Comment #16
larowlanSure, @xjm asked me to review this one - sorry for the crossed wires
Comment #17
phenaproximaOK, so in addition to general framework manager sign-off, these are the two questions we specifically need framework managers to answer for us:
1. This patch introduces the idea of attaching a "media_library_add" form class to source plugin definitions. Should we, can we, implement PluginWithFormsInterface? See #7.1.
2. In AddFormBase, can we/should we make the $mediaType member private? This would force all subclasses to use the protected getMediaType() method, which is where validation or other logic surrounding the media type would go. See #11.6.
Comment #18
phenaproximaNow that #3020716: Add vertical tabs style menu to media library is in, we probably need a reroll here.
Comment #19
seanbReroll done!
Regarding #14. Most is fixed/answered in #3020716-69: Add vertical tabs style menu to media library, below is what is left for this patch:
6. Since the alt text, title and upload field are provided by a single widget, we need to hide the relevant part via CSS. We also have #2987921: Media Library add form should suppress extraneous components of image fields using a form alter, not CSS to improve this.
7. Fixed in this patch.
Comment #20
lauriiiThis should be
media-library-add-form--without-inputandmedia-library-add-form--with-input.This was a very partial review since I don't seem to be able to get the uploading working. I get the following error when trying to upload either file or image:
Comment #21
lauriiiI got the patch working after @phenaproxima suggested me to run database updates.
Let's add a class directly to the img element and target that.
Is there another way this markup could be generated? Generating markup like this in #prefix and #suffix isn't necessarily the cleanest approach. Also, the id given for the element is not ensured to be unique.
Not sure why this includes a double underscore. Feel free to come up with a better class name but this should be something like
media-libary-add-media-formThis should be
media-libary-add-media-form__previewThis should be
media-libary-add-media-form__fieldsThis should be
media-libary-add-media-form__source-fieldComment #22
seanb#20
Fixed.
#21
1. We clear the selection when the dialog closes. In #3023797: Let users choose what to do after selecting and/or adding items in the media library we want to improve this by allowing users to stay in the media library after adding new items. In this case the dialog will not be closed and the selection will be kept. The current patch only supports selecting OR uploading. Not both at the same time.
2. The items are not saved in that step. We have #3023801: Allow newly uploaded files to be deleted from the media library without saving them to improve this by allowing users to delete uploaded items directly (before saving). I think this would solve the issue.
3. Same as 2. This will probably be fixed by #3023801: Allow newly uploaded files to be deleted from the media library without saving them.
4. Fixed. I don't think we need that display block so removed it.
5. We can add a wrapper inside the form via a container element, but this has some limitations when rebuilding the form via AJAX. Not sure if there is a better way? Core uses this approach in several places. Might be a limitation of the form API.
6. Fixed. Used
media-library-add-form__media*<code> since media is an element inside the <code>media-library-add-formblock. According to the BEM convention elements and blocks should be separated by a double underscore.7. Fixed.
8. Fixed.
9. Fixed.
Comment #23
shaalThe bug reported on #3031662 still exist in this last patch. There is no warning for the user when uploading a file bigger than what's allowed.
From a UX perspective, could we load all the media options interfaces (for image, video, etc.) and show only the active one using css? Currently, switching between media types displays a spinner and takes between 0.5 - 1 seconds to load.
Comment #24
lauriiiI don't see mentions of #21.1-3 on those issues. We should make sure those problems get documented somewhere specifically so they get addressed one way or another.
Comment #25
phenaproximaThanks, @lauriii! Documented in #3023797-11: Let users choose what to do after selecting and/or adding items in the media library.
Comment #26
seanbAdded #3023797-10: Let users choose what to do after selecting and/or adding items in the media library and #3023801-4: Allow newly uploaded files to be deleted from the media library without saving them.
*edit LOL phenaproxima and I really want to get this done apparently :)
Comment #27
phenaproximaI had a long discussion with @xjm about framework manager review, and what it means. The conclusion I reached is that this issue does not need framework manager review, so I'm removing the tag.
Why?
All this does not, I think, add up to a "significant" change to Drupal core. From an architecture, API, and BC standpoint, I think we're safe.
Comment #28
phenaproxima@seanB and I demonstrated this patch to the UX team in the usability meeting. The findings were very productive!
Comment #29
phenaproximaSee #3023797-12: Let users choose what to do after selecting and/or adding items in the media library for the new direction that issue is taking based on the UX feedback.
Comment #30
seanbHere is a new patch that takes users back to the media library after adding a new file.
Comment #32
seanbLet's inject the UI builder. Sorry about that.
Comment #33
phenaproximaNit: This should be "source-specific". Also, we should mention that these are to be keyed by operation, and the values are the fully qualified class names of the forms to use.
I'm not sure I understand the rationale for this. Why wouldn't we just add them to currentSelection? How will that cause duplication?
"so they are..."
I think we should mark this class @internal too, with a warning that it should not be extended or used by any code outside of Media Library.
This almost seems like a circular dependency -- the UI builder already has the form builder, which in turn builds this form, which also has the form builder...scary stuff. Not sure if there's any reason to be concerned, but it's a bit unnerving.
Make what configurable? Can we be a bit more specific?
This isn't really returning an array of action elements; it's returning an element containing the actions.
Let's rephrase this: "Creates media items from source field input values."
"field" and "item" should be plural.
I think this method is strangely named. How about something like processInputValues() or createMediaItemsFromInputValues()? Not sure what would be best here, but I don't think updateFormState() is descriptive enough.
Let's rename this as well -- createMediaFromValue() is probably descriptive enough.
So, question -- isn't this logic pretty much the same thing as
return $form?What is this? Can we get a comment here?
Can't we just
return $library_uihere?Let's copy that @internal warning from AddFormBase here too.
Nice comment here. Thanks!
I think this could use a comment. What will setting $element['#value'] to an empty array accomplish? Is there any relevant method in ManagedFile that we can refer to?
The fact that this knows the ID of the wrapper is a bit troublesome, because it shows a lack of encapsulation. That said, given how the AJAX system works, I'm not sure what else we could do here. Also, since this code is also in Media Library, and it's marked internal, it's not a big deal for now.
We need to start this description with (optional) and mention that it defaults to FALSE.
Same here.
I'm not a huge fan of passing random boolean flags around to do stuff like this, but it makes even less sense to have the calling code create and pass in a FormState object. So I guess I'm okay with this for now...hopefully we can refactor it later.
Can we wait for a specific element instead?
I thought this was a duplicate at first :) It might be better to assert this by the name of the field, rather than the label.
Same here.
Can't we click on the link by its text? That'd be so much clearer. I believe we could even do
This could just be one (long) line:
$page->find('css', '.media-library-view .js-click-to-select-checkbox input')->click(). find() will automatically target the first one."added the widget"? I think we're missing an "in" :)
Comment #34
seanb#33
1. Fixed.
2. Fixed. Passing the media IDs to the current selection via DrupalSettings needed this workaround. After discussing this with phenaproxima and samuel.mortenson we concluded that a custom AJAX command was the cleanest way to update the selection.
3. Removed this code.
4. Fixed.
5. In a previous version we reopened the dialog with a URL. This had 2 drawbacks. First, it needs an extra request to the server. Second, since the whole dialog is replaced this is an a11y issue. I can see how reopening the library from the form, with a new/fresh instance of the same form looks a bit weird, which is why we need to force the form API to get us a fresh copy of the form by setting the empty input in the form state. I don't see a good way around that though. The form needs to render a new copy of the library somehow.
6. Fixed.
7. Fixed.
8. Fixed.
9. Fixed.
10. Fixed.
11. Fixed.
12. Not necessarily. Depends on whether the wrapper is set on the element triggering this. The wrapper is normally set to replace the whole media library, but when there is an error you just want to replace the form.
13. Fixed.
14. Fixed. I think we can in this case.
15. Fixed.
16. Yay!
17. Fixed.
18. I see your point, but the library UI and the form are changing/replacing each other in the design. If someone has a better way of doing this I am very much open to suggestions!
19. Fixed.
20. Fixed. No longer optional here.
21. Again, if anyone has suggestion on how to improve this, I am very much open to suggestions!
22. The media library tests are currently consistently using assertWaitOnAjaxRequest(). We could change this if needed, but I suggest we do that consistently in a followup.
23. Fixed.
24. Fixed.
25. Fixed.
26. Fixed.
Comment #35
phenaproximaAwww yeah. Sam saves the day -- that's a much nicer solution.
We should probably namespace this a bit -- updateMediaLibrarySelection might be a better name.
If we always supply an array in response.mids, even if it's empty, we don't need the if check.
I think the word "current" in this name is redundant. Can we just call this UpdateSelectionCommand?
Idea -- let's add a flag in here which allows the command to either _add_ the IDs to the current selection, or completely _replace_ the current selection with the new IDs. I think this will serve us well in the future.
Supernit: This line is 82 characters long.
I think we can use $page->clickLink() for this.
Comment #36
seanb#35
1. Fixed.
2. Fixed.
3. Fixed.
4. Let's add that when we actually need it. We could implement it later in a backwards compatible way for sure.
5. Fixed.
6. Fixed.
Also forgot to answer #23
This is a nginx error. Afaict Drupal can't do anything about this since nginx doesn't accept the file and stops the request before it gets to Drupal. If you increase the nginx limit, the PHP limit will be used and that could solve the issue. Let's use the other issue to see what we can do.
Since the media library loads a view with 25 items for each tab this could be a performance issue. If you configure too much media types for a field with a library that contains lots of items, you could get easily get in trouble. We should probably protect site builders from that.
Comment #37
phenaproximaAnother partial review...
For paranoia's sake, let's throw exceptions if $form_state doesn't have media_library_state, and if $this->mediaType is null after the call to load(). That will be preferable to the possibility of calling methods on null values.
Do the tests assert that these CSS classes are applied and removed as expected? If not, they probably should. :)
Mink doesn't support multi-file uploading, so we'll probably have to be sure to manually test this before commit.
Which elements are hidden? This should probably be a little more specific and say what is hidden, and why.
This could use a comment explaining that we hide the field here because it is not configurable at the form display level. If there's an issue to make it configurable, we should reference it here, with a @todo to remove this bit.
I'm not entirely sure we need to pass the entity storage handler to this method; we already have the entity type manager as a protected property on this class, and it's pretty harmless to have this method call $this->entityTypeManager->getStorage(). This is a method that subclasses are likely to override, so I feel like streamlining the method signature would be preferable.
Is this still needed?
Comment #38
seanb#37
1. Fixed.
2. Fixed.
3. Yeah, that sucks.
4. Fixed.
5. Fixed.
6. See #14.7
7. Fixed. Whoops!
Comment #39
phenaproximaAfter these changes, I have read the patch enough. From there, we'll need screenshots, plus a demo video, and manual testing (i.e., for multiple uploads and general stuff). Then RTBC.
Do we really need to use Object.values()? I ask because response.mids is just a simple list of integers, which _should_ JSON-ize as an array which can be directly forEach'ed over.
I think this should probably be InvalidArgumentException.
This exception could be a little more helpful with a message like "The '$selected_type_id' media type does not exist." Also, this should probably be InvalidArgumentException.
Also, can we add some unit test coverage for these exceptions? I ask because, if the media type is invalid in some way, the entire form is unusable. So we should probably ensure this validation works.
Nit: Longer than 80 characters.
Comment #40
phenaproximaI had an idea for a way to not continually pass $form_rebuild down through three method calls -- let's just set a flag on $state!
Then, later on, in buildMediaTypeAddForm():
We could also add a new getter/setter on MediaLibraryState specifically for this (which might be cleaner and doesn't require us to pollute the parameter bag); something like
$state->isRebuildingMediaAddForm()and$state->setRebuildMediaAddForm().Comment #41
seanb#39
1. Apparently it becomes an object somewhere, so yeah, I'm affraid we do.
2. Fixed.
3. Fixed.
4. Fixed.
#40
I think having this as an official part of the state might be too much. It's just an internal workaround we are using. And since we can pass a state object to
buildUi(), we don't need the$form_rebuildparameter at all.Comment #42
phenaproximaNit: "contains" should be "contain".
Otherwise, this is ready to go with a bit of manual testing, screenshots, and demo videos. Nice!
Comment #43
phenaproximaAdding screenshots to the IS.
Comment #44
phenaproximaComment #45
phenaproximaCreated a short demo video. I was surprised by the error message showing up floated to the left of the vertical tabs, though, so kicking back for that. :)
Comment #46
phenaproximaMeant to send it back to "Needs work"...
Comment #47
dwwThis mostly looks great. You two have been doing amazing work on all these media issues!
Partial drive-by review, mostly nit-tastic quibbling. I'm out of time right now so I didn't go over every line, but a few things stuck out. Please ignore as appropriate. ;)
I don't fully grok this comment, nor the name of the member. ;) It's an array of strings. It's called "forms". The doc says "The classes used..." the example looks like a form_id, not a class name. Can we get a live example in this comment? Can the help text match? Can the member be named something more self-documenting? $formClasses or $formIds or whatever?
Ahah! ;) That helps. So $forms is supposed to be keyed by form_id with a classname as the value, huh? Let's say so above.
FYI: I just saw @xjm complain in the JSON:API code review that she wanted an explanation why something is @internal, and what might go wrong if you try to use it. I can see her point. On the surface, it's not clear why I'm not supposed to extend this myself...
the return value says it's an "element", but the function name says "buildEntityForm". Should this be called buildEntityFormElement() or something?
"...so hide it by..."
Yes, please! ;)
Nit: "The value for the source field of the media item."
s/an new/a new/
Comment doesn't seem to match the permissions we're granting here.
Thanks again!
-Derek
Comment #48
phenaproxima+1 for this. I suggest we say something "Media Library is an experimental module and its internal code may be subject to change in minor releases. External code should not instantiate or extend this class." And let's copy that warning to everything we've marked
@internal. (Which should be just about everything, until we've at least fixed our must-have stable blockers.)Comment #49
seanbThanks for the screenshots, video and reviews @phenaproxima and @dww!
#47
1. Fixed, expanded the comment.
2. Fixed.
3. Fixed.
4. Fixed.
5. Fixed.
6. :)
7. Fixed.
8. Fixed.
9. Fixed.
#48
Fixed.
Comment #51
seanbAh, figured out what caused #45. We are changing the state to rebuild the form, but also changing the state to build the links.
We shouldn't change the state for the links, just change the query parameters after they are fetched from the state.
Comment #52
seanbCopy of #3023801-10: Allow newly uploaded files to be deleted from the media library without saving them, since the feedback applies to this patch.
----------------------------------------------------------
Comment #10 Pancho commented about 5 hours ago
RTL 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.)
$delta is so much more descriptive than the generic $i. You purged three or four $i keys, so how about bringing the last one in line, too?
4.)
Not your fault, but $mids is so confusing, because unlike $nids, $tids or $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 $mIds for 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, $tIds or $vIds as 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 #53
seanbPhenaproxima requested a small change to the @internal description. So that was done.
About #52:
1. It's exactly 80 characters as far as I can tell?
2. Fixed. It's now
$added_media3. Fixed.
4. Fixed. Changed it to
$media_idsRegarding the RTL display of "1 of 5 items selected".
Setting the page to RTL changes the direction of the text. Apparently numbers automatically get reversed. I think when the string is translated this should be automatically fixed, but I'm not an expert on RTL implementation. We could force the div containing the text to the ltr direction, but I'm pretty sure we should not do that.
Comment #54
phenaproximaA few comment nitpicks.
I think we should rephrase this a bit. I propose something like this:
"An array of form class names, keyed by ID. The ID represents the operation the form is used for."
Maybe we should avoid mentioning Media Library here for now, since it's still experimental.
For consistently, we should rename 'mids' to 'mediaIds'. (And make a similar change in the corresponding AJAX command class.)
Nice.
We should not have
|nullthing in the type; instead, the description should be prefixed with(optional).Referring to a vertical tab link here is confusing. How about "These are internal parameters that can influence the response in undesirable ways" instead?
s/the link can replace/the response will contain
Comment #55
seanb#54
1. Fixed.
2. Fixed.
3. Yay!
4. Fixed.
5. The query parameters we are changing here are exactly for the vertical tabs links, so I think this is more clear.
6. Fixed.
Comment #56
phenaproximaI think this has undergone enough review, testing, and poking and prodding. We have other things we gotta do, and we gotta get to 'em; this unblocks two or three major must-have issues in our roadmap. Let's do it! RTBC on green.
Comment #57
seanbNot sure where comment #55 went? Here is the patch again.
Comment #59
seanbRandom fail outside this patch. Everything is now green, back to RTBC.
Comment #60
panchoThere's a nice screencast of this feature with #3023801: Allow newly uploaded files to be deleted from the media library without saving them added on top.
However:
A.) The fact that, once a new media file is being added, all preexisting media seems to disappear, is confusing. Keeping the preexisting media at the bottom of the upload form seems no option, so we need to reassure the user, where he/she is:
B.) Additional nitpick:[edit: this belongs to the followup #3023801: Allow newly uploaded files to be deleted from the media library without saving them]There is an afterglow after hovering a delete "X" button that is larger than the "X" even after the "delete" hover text disappeared, see 00:18 or 00:50
Sorry I have to reset this to Needs work... :/
Comment #61
seanbThanks for the feedback @Pancho.
We are going to fix that in #3023797: Let users choose what to do after selecting and/or adding items in the media library. Also see comment #21 and #28.
A.1. Opening a dialog from a dialog has proven to be an a11y issue (see #3020716-18: Add vertical tabs style menu to media library). Changing the dialog title would go unnoticed of we are not reopening the dialog. So I think we should look for a solution in the form (if this is really an issue at all).
A.2. Actually we are not in a different context, but this will get better after we fix #3023797: Let users choose what to do after selecting and/or adding items in the media library and remember + show the previously selected items.
A.3. Since we are not opening a second dialog, making the close button behave like a back button is not an existing pattern. I would strongly advise against this.
A.4. Not really sure about that one, we are trying not to add too many buttons. The close button would effectively mean 'Cancel', and I think this is an existing pattern for cancelling a modal action. So I would slightly prefer that.
A.5. Will be fixed in #3023797: Let users choose what to do after selecting and/or adding items in the media library.
B. This feedback is not related to this issue. Also, the "glow" is showing where the focus is, and this is standard behavior. I think removing the focus styling from a button is an a11y issue. But if needed, can you add this to the actual issue of the delete buttons #3023801: Allow newly uploaded files to be deleted from the media library without saving them?
Comment #62
panchoI can see some of your points, and certainly this will get improved in followups. Apart from that, Media (as a relatively new Core module) might have a different standard for RTBC features than the (more settled) rest of Core does.
Fact is that as much as I'm welcoming the new shiny feature (and that's why I'm reviewing it), usability-wise it's half-baked IMHO, and I think the points I raised shouldn't be brushed away that easily. But in the end, if they are relevant, at some point someone else will probably come up with the same criticisms, otherwise not.
Setting back to "Needs review." RTBC is more than I could justify here, but I'm sure it's gonna be fine in the end.
Comment #63
seanbI most certainly was not trying to "brush away" any of your concerns. Sorry if I gave this impression. Your reviews are very much appreciated.
We are splitting up the development of the media library in many small, separate issues. This makes them easier to review and commit. Issues could have great improvements but not be perfect. In those cases it is acceptable to create follow ups. I don't think this is a new RTBC standard. However, deciding if it's ok if things can be fixed in a follow up is ultimately not up to me. Most important thing we is we are not adding regressions.
Leaving to 'Needs review' is ok for now. Let's ask the UX team if the points you have raised need to be addressed here, or if they can be addressed in follow up issues (most already have a follow up issue). Even though this issue blocks a lot of the other work, we need to do a fair evaluation and can not accept regressions.
Comment #64
panchoFair point, that's certainly the case, and indeed I didn't hit any regressions.
Probably you're right and - no matter what's missing - it's better we're getting this committed ASAP, so we're no longer piling up combined patches everywhere, given the impressive speed of development here! :)
Setting back to RTBC, as it was ahead of my UX review in #60.
Comment #65
lauriiiI haven't reviewed the backend parts in detail but +1 RTBC for the frontend parts.
Comment #67
gábor hojtsyReviewed as well. Discussed the form extension point with @seanB. I initially misunderstood that the media info altering would be on the type level (this requiring code changes when you manually create new media types on the UI), but it is indeed on the source level. So modules providing new sources can/should provide the file upload info as well.
That was the only concern I had, and also based on the rest of the reviews, this looks great. Thanks all! Let's work on the followups.
Comment #68
seanbYay! Thanks all, on the next ones...