Problem/Motivation

After selecting an item in the media library, the selection form is currently hard coded to insert the selected items in the media library entity reference widget. It would be great if the media library also supports other field types. A good example of this is using the media library to embed media in a WYSIWYG field (see #2801307: [META] Support WYSIWYG embedding of media entities and #2994699: Create a CKEditor plugin to select and embed a media item from the Media Library).

Proposed resolution

Add an event to handle the selection based on the opener ID in the media library state, similar to handling the access (see #3038254: Delegate media library access to the "thing" that opened the library).

Remaining tasks

  • Write patch
  • Review
  • Commit

User interface changes

None

API changes

Add a new event to handle the media library selection.

Data model changes

None

Release notes snippet

CommentFileSizeAuthor
#56 interdiff-3044649-54-56.txt577 bytesphenaproxima
#56 3044649-56.patch32.42 KBphenaproxima
#54 interdiff-3044649-52-54.txt2.63 KBphenaproxima
#54 3044649-54.patch32.42 KBphenaproxima
#52 interdiff-3044649-49-52.txt11.63 KBphenaproxima
#52 3044649-52.patch32.35 KBphenaproxima
#49 interdiff-3044649-45-49.txt5.18 KBphenaproxima
#49 3044649-49.patch30.62 KBphenaproxima
#45 interdiff-3044649-43-45.txt4.83 KBphenaproxima
#45 3044649-45.patch30.21 KBphenaproxima
#44 3044649-43.patch32.14 KBwim leers
#44 interdiff.txt6.09 KBwim leers
#42 interdiff-3044649-41-42.txt5.56 KBphenaproxima
#42 3044649-42.patch32.23 KBphenaproxima
#41 interdiff-3044649-40-41.txt2.12 KBphenaproxima
#41 3044649-41.patch33.2 KBphenaproxima
#40 interdiff-3044649-39-40.txt1.99 KBphenaproxima
#40 3044649-40.patch31.88 KBphenaproxima
#39 interdiff-3044649-32-39.txt12.14 KBphenaproxima
#39 3044649-39.patch30.62 KBphenaproxima
#37 interdiff-3044649-32-37.txt4.66 KBphenaproxima
#37 3044649-37.patch25.84 KBphenaproxima
#32 3044649-32.patch22.6 KBwim leers
#32 interdiff.txt5.8 KBwim leers
#30 3044649-30.patch22.48 KBwim leers
#30 interdiff.txt25.58 KBwim leers
#27 3044649-27.patch25.27 KBwim leers
#27 interdiff.txt6.41 KBwim leers
#26 3044649-26.patch24.59 KBseanb
#26 interdiff-23-26.txt1.02 KBseanb
#23 3044649-23.patch24.55 KBseanb
#20 3044649-19.patch24.57 KBseanb
#20 interdiff-16-19.txt1.45 KBseanb
#16 3044649-16.patch24.55 KBseanb
#16 interdiff-14-16.txt3.59 KBseanb
#14 3044649-14.patch25.11 KBseanb
#14 interdiff-12-14.txt7.33 KBseanb
#12 3044649-12.patch25.6 KBseanb
#4 3044649-4-do-not-test.patch17.1 KBwim leers
#2 3044649-2-3038254-26.patch59.03 KBseanb
#2 3044649-2-do-not-test.patch17.28 KBseanb

Comments

seanB created an issue. See original summary.

seanb’s picture

Status: Active » Needs review
StatusFileSize
new17.28 KB
new59.03 KB

Since #3038254: Delegate media library access to the "thing" that opened the library already contains a lot of ground work implementing events for the media library, I based the patch on the work done there.

phenaproxima’s picture

Status: Needs review » Needs work

I love this architecture.

  1. +++ b/core/modules/media_library/src/Event/MediaLibraryEvents.php
    @@ -27,4 +27,21 @@ final class MediaLibraryEvents {
    +  /**
    +   * Name of the event fired when a user selected items in the media library.
    +   *
    +   * This event allows modules to handle the selected items in the media library
    +   * based on the media library state. Example use cases that require different
    +   * handling of selected media items:
    +   * - when used in an entity reference field widget;
    +   * - when used in CKEditor.
    +   *
    +   * @Event
    +   *
    +   * @see \Drupal\media_library\Event\MediaLibrarySelectionEvent
    +   *
    +   * @var string
    +   */
    +  const SELECTION = 'media_library.selection';
    

    I think we should differentiate that this is not actually fired when someone "selects something" in the media library -- that implies that this event is fired if the user clicks on an item in the media library to select it, which is not the case. Maybe we should rephrase some of this comment to explain that this is fired once the user has finished interacting with the media library and has finished the selection process. To wit, perhaps SELECTION_COMPLETE might be a better name for the event? Not sure...

  2. +++ b/core/modules/media_library/src/Event/MediaLibrarySelectionEvent.php
    @@ -0,0 +1,95 @@
    +  /**
    +   * Sets the AJAX response.
    +   *
    +   * @param \Drupal\Core\Ajax\AjaxResponse $response
    +   *   The AJAX response.
    +   */
    +  public function setResponse(AjaxResponse $response) {
    +    $this->response = $response;
    +  }
    

    I'm not sure we should be allowing people to completely overwrite the response here. Maybe it would be wiser to only allow event subscribers to add AJAX commands? (i.e., $event->getResponse()->addCommand(), and remove setResponse() entirely)

  3. +++ b/core/modules/media_library/src/EventSubscriber/MediaLibraryWidgetSubscriber.php
    @@ -85,6 +87,35 @@ public function onMediaLibraryAccess(MediaLibraryAccessEvent $event) {
    +   * @throws \Exception
    +   */
    +  public function onMediaLibrarySelection(MediaLibrarySelectionEvent $event) {
    

    This method does not throw \Exception, so we don't need the @throws. :)

  4. +++ b/core/modules/media_library/src/EventSubscriber/MediaLibraryWidgetSubscriber.php
    @@ -85,6 +87,35 @@ public function onMediaLibraryAccess(MediaLibraryAccessEvent $event) {
    +    $state = $event->getState();
    +
    +    // We only want to respond to opener IDs of media library entity reference
    +    // widgets.
    +    $widget_id = MediaLibraryWidget::getOpenerWidgetId($state->getOpenerId());
    

    Nit: We don't really need $state as its own variable here. We can just call $event->getState()->getOpenerId().

  5. +++ b/core/modules/media_library/src/Form/AddFormBase.php
    @@ -64,11 +73,14 @@ abstract class AddFormBase extends FormBase {
    +    $this->eventDispatcher = $event_dispatcher;
    

    Well, this sucks, but because we are beta experimental I think we need to have the $event_dispatcher parameter default to NULL, and then throw a deprecation notice if it's not passed.

wim leers’s picture

StatusFileSize
new17.1 KB

Rebased #2's do-not-test patch on top of @seanB's #3038254-31: Delegate media library access to the "thing" that opened the library. Did not address anything in #3.

wim leers’s picture

To be able to provide truly constructive feedback rooted in real-world needs, I first worked on #2998005 to see if I could make this work against D8.8 HEAD (without either this patch applied or #3038254: Delegate media library access to the "thing" that opened the library).

The answer is: yes, I could — see #2998005-15: [PP-1] Support Drupal core's Media Library. The latest iteration is a bit simpler: #2998005-24: [PP-1] Support Drupal core's Media Library. The relevant code: entity_embed_form_views_form_media_library_widget_alter() + entity_embed_form_views_form_media_library_widget_ajax_update().

It overrides the #ajax submit callback; it changes it from \Drupal\media_library\Plugin\views\field\MediaLibrarySelectForm::updateWidget() to entity_embed_form_views_form_media_library_widget_ajax_update().

While I was able to make it work, it also happened to confirm the need for infrastructure like the one being proposed here. It'd mean I won't have to have a form alter hook that detects which "opener" it is anymore and then swap out the #ajax submit callback. It'd mean I have an official, supported API, rather than having to reach into implementation details. That being said, we have code all over Drupal core and contrib relying on particular form structures. So I think it would be acceptable to not have the infrastructure this patch is adding.


  1. +++ b/core/modules/media_library/src/Event/MediaLibrarySelectionEvent.php
    @@ -0,0 +1,95 @@
    +   * @return \Drupal\Core\Ajax\AjaxResponse
    +   *   The AJAX response.
    ...
    +   * @param \Drupal\Core\Ajax\AjaxResponse $response
    +   *   The AJAX response.
    

    Why must this be an AJAX response? Isn't this limiting us unnecessarily for the future? Shouldn't this be up to the event subscriber to decide?

  2. +++ b/core/modules/media_library/src/Form/AddFormBase.php
    @@ -724,20 +737,21 @@ public function updateWidget(array &$form, FormStateInterface $form_state) {
    +    // Return the AJAX response to handle the selection and close the media
    +    // library dialog.
    +    return $selection_event->getResponse()
    +      ->addCommand(new CloseDialogCommand());
    
    +++ b/core/modules/media_library/src/Plugin/views/field/MediaLibrarySelectForm.php
    @@ -127,21 +127,16 @@ public function viewsForm(array &$form, FormStateInterface $form_state) {
    +    // Return the AJAX response to handle the selection and close the media
    +    // library dialog.
    +    return $selection_event->getResponse()
    +      ->addCommand(new CloseDialogCommand());
    

    Note that the CloseDialogCommand being always present means that for the WYSIWYG (text editor) integration, it'll have to override the pre-generated AjaxResponse completely, because \Drupal\editor\Ajax\EditorDialogSave MUST run before CloseDialogCommand.

    EDIT: never mind, \Drupal\Core\Ajax\AjaxResponse::addCommand() has an optional $prepend = FALSE parameter; that'll allow the event subscriber for the WYSIWYG (text editor) integration to prepend its \Drupal\editor\Ajax\EditorDialogSave command 👍

wim leers’s picture

I didn't want to end at #5, I wanted to 100% vet it. So I went ahead and created an alternative
#2998005 patch that builds on top of #3038254-31: Delegate media library access to the "thing" that opened the library and #3044649-4: Delegate media library selection handling to the "thing" that opened the library.

It'd work beautifully, except that we would unfortunately still need entity_embed_form_views_form_media_library_widget_alter() to attach the editor/drupal.editor.dialog asset library. That's the sole place where we need to go beyond the infrastructure that #3038254 and #3044649 (this issue) provide. That asset library is necessary to make EditorDialogSave AJAX commands work.

See #2998005-28: [PP-1] Support Drupal core's Media Library.

seanb’s picture

Why must this be an AJAX response? Isn't this limiting us unnecessarily for the future? Shouldn't this be up to the event subscriber to decide?

Good point. We designed this to work in a modal and communicate with the opener via JS/AJAX, but I guess there is no technical reason why the media library can't be opened via a link and return a "whatever" response when the user is done. I guess this also means the closeDialog command needs to move to the opener implementation which would address the other point you raised.

wim leers’s picture

+1 to both; although the CloseDialogCommand remark is kinda moot.

Actually … the fact that the dialog needs to be closed is a valid reason for it to be required to use an AjaxResponse. So it might be better to leave it as-is :)

phenaproxima’s picture

I discussed this with @effulgentsia today.

He thinks (and I agree) that adding entity-specific stuff to MediaLibraryState, as we are doing in #3038254: Delegate media library access to the "thing" that opened the library, is a strange code smell. It's strange enough that it will likely hold up that issue for a bit.

However, the current patch in this issue doesn't seem to need any entity-specific stuff at all. Which means that this patch does not need to be blocked by that patch.

So, let's flip the order. Let's add the event subscriber-based architecture in this issue and commit it first, then reroll #3038254: Delegate media library access to the "thing" that opened the library on top of that work. I validated this idea with @effulgentsia and he is for it. So let's get it done!

wim leers’s picture

Can you explain that concern some more? Is the concern that this prevents non-entity things from using the Media Library selection UI?

P.S.: why is that on this issue instead of on #3038254: Delegate media library access to the "thing" that opened the library?

seanb’s picture

seanb’s picture

Status: Needs work » Needs review
StatusFileSize
new25.6 KB

Moved the event subscriber-based architecture to this issue to not be blocked in #3038254: Delegate media library access to the "thing" that opened the library. Interdiff didn't really make sense so best to look at this from scratch.

phenaproxima’s picture

Status: Needs review » Needs work

Nice work!

  1. +++ b/core/modules/media_library/src/Event/MediaLibrarySelectionEvent.php
    @@ -0,0 +1,95 @@
    +  /**
    +   * Sets the AJAX response.
    +   *
    +   * @param \Drupal\Core\Ajax\AjaxResponse $response
    +   *   The AJAX response.
    +   */
    +  public function setResponse(AjaxResponse $response) {
    +    $this->response = $response;
    +  }
    

    I don't think we need to allow people to override the response entirely. Calling code should be able to just do $event->getResponse()->addCommand().

  2. +++ b/core/modules/media_library/src/Form/AddFormBase.php
    @@ -64,11 +73,14 @@ abstract class AddFormBase extends FormBase {
    +  public function __construct(EntityTypeManagerInterface $entity_type_manager, MediaLibraryUiBuilder $library_ui_builder, EventDispatcherInterface $event_dispatcher) {
         $this->entityTypeManager = $entity_type_manager;
         $this->libraryUiBuilder = $library_ui_builder;
         $this->viewBuilder = $this->entityTypeManager->getViewBuilder('media');
    +    $this->eventDispatcher = $event_dispatcher;
    

    I think we are going to need to default the $event_dispatcher to NULL and default it to \Drupal::service('event_dispatcher') (and trigger a deprecation notice) if it's not passed.

  3. +++ b/core/modules/media_library/src/Form/FileUploadForm.php
    @@ -66,8 +69,8 @@ class FileUploadForm extends AddFormBase {
    +  public function __construct(EntityTypeManagerInterface $entity_type_manager, MediaLibraryUiBuilder $library_ui_builder, EventDispatcherInterface $event_dispatcher, ElementInfoManagerInterface $element_info, RendererInterface $renderer, FileSystemInterface $file_system) {
    +    parent::__construct($entity_type_manager, $library_ui_builder, $event_dispatcher);
    

    Since the $event_dispatcher parameter is new, it should be the last one, and default to NULL.

  4. +++ b/core/modules/media_library/src/Form/OEmbedForm.php
    @@ -44,13 +45,15 @@ class OEmbedForm extends AddFormBase {
    +  public function __construct(EntityTypeManagerInterface $entity_type_manager, MediaLibraryUiBuilder $library_ui_builder, EventDispatcherInterface $event_dispatcher, UrlResolverInterface $url_resolver, ResourceFetcherInterface $resource_fetcher) {
    +    parent::__construct($entity_type_manager, $library_ui_builder, $event_dispatcher);
    

    Same here.

  5. +++ b/core/modules/media_library/src/Plugin/Field/FieldWidget/MediaLibraryWidget.php
    @@ -67,11 +67,25 @@ class MediaLibraryWidget extends WidgetBase implements ContainerFactoryPluginInt
    +  /**
    +   * The prefix to use with a widget ID for media library opener IDs.
    +   *
    +   * @var string
    +   */
    +  protected static $openerIdPrefix = 'widget:';
    +
    +  /**
    +   * The separator to use for widget IDs separating the field ID and the suffix.
        *
        * @var string
        */
    -  protected static $openerIdPrefix = 'field:';
    +  protected static $widgetIdSeparator = ':';
    

    I'm not sure if we really need these protected static members. They're completely internal implementation details of public methods, so maybe we should just hard-code them.

  6. +++ b/core/modules/media_library/src/Plugin/Field/FieldWidget/MediaLibraryWidget.php
    @@ -447,9 +471,11 @@ public function formElement(FieldItemListInterface $items, $delta, array $elemen
    +    // Create a unique opener ID with a fixed prefix for entity reference field
    +    // widgets. The opener ID could be used to check access to the media library
    +    // and used by the media library select/add forms to add the selected/added
    +    // media items to the widget.
    +    $opener_id = static::$openerIdPrefix . $field_widget_id;
    

    This comment should not mention access checking, since we're not adding that in this patch. :)

  7. +++ b/core/modules/media_library/src/Plugin/Field/FieldWidget/MediaLibraryWidget.php
    @@ -834,19 +860,43 @@ protected static function setFieldState(array $element, FormStateInterface $form
    +   * @see \Drupal\media_library\MediaLibraryState
    +   */
    +  public static function getFieldNameFromOpenerId($opener_id) {
    

    Why do we need the @see?

Although I do realize this patch has lots of implicit test coverage (all Media Library tests would break if this did not work), I think we should add some explicit test coverage as well, just to prove that the events are properly dispatched and handled. (I think a basic kernel test, with an accompanying test module, oughta do the trick.)

seanb’s picture

Status: Needs work » Needs review
StatusFileSize
new7.33 KB
new25.11 KB

Fixed #13.

While writing the kernel test I came to the conclusion there is very little we can actually test for the event. We basically can only test the event exists and that it is dispatched. Maybe if it returns an AJAX response? All these things do not really add any value imho.
We already have test coverage for the media library that proves the MediaLibraryWidgetSubscriber event works since that code is now responsible for updating the field values on selection.

If there are more tests needed please let me know what we actually want to assert, I would be happy to add it.

phenaproxima’s picture

Status: Needs review » Needs work
+++ b/core/modules/media_library/src/Plugin/Field/FieldWidget/MediaLibraryWidget.php
@@ -893,10 +877,10 @@
+    // Media library widget IDs are always prefixed with 'widget:' property in

s/property//

Apart from doc comment updates I have asked @seanB for in person, I think I'm out of things to complain about.

seanb’s picture

Status: Needs work » Needs review
Issue tags: +DevDaysTransylvania
StatusFileSize
new3.59 KB
new24.55 KB

Fixed #15.

phenaproxima’s picture

Issue tags: -DevDaysTransylvania +DevDaysCluj

Looks great! I'd like a second opinion from Wim before we officially greenify this, but it's RTBC from my perspective.

The last submitted patch, 14: 3044649-14.patch, failed testing. View results

phenaproxima’s picture

I'm tagging this for backport to 8.7.x. I'm not sure this is technically a feature request; it is really just refactoring internal Media Library mechanisms. And although it's not a bugfix, it would allow Entity Embed to integrate with Media Library before 8.8, which is advantageous since we are intending to move parts of that module (including its integration with Media Library) into core. (See #2998005: [PP-1] Support Drupal core's Media Library).

seanb’s picture

StatusFileSize
new1.45 KB
new24.57 KB

Missed one last thing. Sorry about that..

The last submitted patch, 16: 3044649-16.patch, failed testing. View results

phenaproxima’s picture

Status: Needs review » Needs work
Issue tags: +Needs reroll

Looks like it needs a reroll =\

seanb’s picture

Status: Needs work » Needs review
StatusFileSize
new24.55 KB

Rerolled.

phenaproxima’s picture

Issue tags: -Needs reroll

Removing the "Needs reroll" tag.

Status: Needs review » Needs work

The last submitted patch, 23: 3044649-23.patch, failed testing. View results

seanb’s picture

Status: Needs work » Needs review
StatusFileSize
new1.02 KB
new24.59 KB

This should fix it. Not sure how I missed that before.

wim leers’s picture

StatusFileSize
new6.41 KB
new25.27 KB
  1. +++ b/core/modules/media_library/src/Event/MediaLibraryEvents.php
    @@ -0,0 +1,29 @@
    + * @see \Drupal\media_library\Event\MediaLibraryAccessEvent
    

    s/Access/Selection/ :)

  2. +++ b/core/modules/media_library/src/Event/MediaLibraryEvents.php
    @@ -0,0 +1,29 @@
    +   * - when used in CKEditor.
    

    s/CKEditor/a text editor/

  3. +++ b/core/modules/media_library/src/Event/MediaLibrarySelectionEvent.php
    @@ -0,0 +1,85 @@
    +   * The selected media IDs.
    ...
    +   * @var array
    ...
    +   * @param int[] $selected_ids
    ...
    +   * @return int[]
    

    Inconsistent.

  4. +++ b/core/modules/media_library/src/EventSubscriber/MediaLibraryWidgetSubscriber.php
    @@ -0,0 +1,52 @@
    +   * @throws \Exception
    

    No exception is thrown; should be removed.

  5. +++ b/core/modules/media_library/src/EventSubscriber/MediaLibraryWidgetSubscriber.php
    @@ -0,0 +1,52 @@
    +    // Create a comma separated list of media IDs, insert them in the hidden
    

    Nit: s/comma separated/comma-separated/

  6. +++ b/core/modules/media_library/src/Form/AddFormBase.php
    @@ -64,11 +73,18 @@ abstract class AddFormBase extends FormBase {
    +   * @param \Symfony\Component\EventDispatcher\EventDispatcherInterface $event_dispatcher
    

    Nit: typehint inaccurate.

  7. +++ b/core/modules/media_library/src/Form/AddFormBase.php
    @@ -247,7 +264,7 @@ protected function buildEntityFormElement(MediaInterface $media, array $form, Fo
    -    $parents = $form['#parents'];
    +    $parents = isset($form['#parents']) ? $form['#parents'] : [];
    

    Noting that @phenaproxima asked about this in #3038254-23: Delegate media library access to the "thing" that opened the library.6 and @seanB answered this in the next comment.

wim leers’s picture

Status: Needs review » Reviewed & tested by the community

Rebased #2998005 on top of #27, and #2998005-41: [PP-1] Support Drupal core's Media Library still works perfectly fine 👍 I wanted that extra piece of validation before RTBC'ing :)

alexpott’s picture

I'm going to talk to @Wim Leers, @seanB and @phenaproxima about this issue at devdays cluj because I have some concerns about the architecture. I'm not sure that an event is really the right way to go but we'll hash that out and I'll make a further comment here about the resulting discussion.

wim leers’s picture

Status: Reviewed & tested by the community » Needs review
StatusFileSize
new25.58 KB
new22.48 KB

@alexpott expressed concerns about using events for solving this issue, since the expectation for events is that multiple subscribers can chime in. In this case, only a single thing is supposed to have a say in this.

Creating a new plugin type seems overkill, tagged services are also overkill. What seems appropriate in this case is to let the specific media library opener nominate a particular service to generate a response upon selection (and the same service could also handle access checking, see #3038254: Delegate media library access to the "thing" that opened the library).

@seanB, @phenaproxima and I pair-programmed this together at Drupal Dev Days.

phenaproxima’s picture

Status: Needs review » Needs work

Nitpicks!

  1. +++ b/core/modules/media_library/media_library.services.yml
    @@ -6,3 +6,5 @@ services:
    +  media_library.field_widget:
    +    class: Drupal\media_library\MediaLibraryFieldWidgetOpener
    

    I think we should rename this to media_library.opener.field_widget.

  2. +++ b/core/modules/media_library/src/Form/AddFormBase.php
    @@ -64,11 +71,18 @@ abstract class AddFormBase extends FormBase {
    +   * @param \Drupal\Core\DependencyInjection\ClassResolverInterface|null $class_resolver
    +   *   (optional) The class resolver.
    

    Nit: I don't think this should be type hinted with |null, or marked optional. It's not. It's only optional for backwards compatibility reasons, and the parameter documentation should reflect the intention.

  3. +++ b/core/modules/media_library/src/Form/AddFormBase.php
    @@ -724,20 +739,21 @@ public function updateWidget(array &$form, FormStateInterface $form_state) {
    +    $response = $this->classResolver->getInstanceFromDefinition($service_id)->respondToSelection(
    +      $current_media_ids,
    +      $media_library_state
    +    );
    +    return $response->addCommand(new CloseDialogCommand());
    

    Nit: This can all be one long chained call.

  4. +++ b/core/modules/media_library/src/Form/FileUploadForm.php
    @@ -65,9 +66,11 @@ class FileUploadForm extends AddFormBase {
    +   * @param \Drupal\Core\DependencyInjection\ClassResolverInterface|null $class_resolver
    +   *   (optional) The class resolver.
    

    Same complaint as above.

  5. +++ b/core/modules/media_library/src/Form/OEmbedForm.php
    @@ -48,9 +49,11 @@ class OEmbedForm extends AddFormBase {
    +   * @param \Drupal\Core\DependencyInjection\ClassResolverInterface|null $class_resolver
    +   *   (optional) The class resolver.
    

    Ditto.

  6. +++ b/core/modules/media_library/src/MediaLibraryFieldWidgetOpener.php
    @@ -0,0 +1,43 @@
    +    throw new \Exception('not yet implemented');
    

    Might want a better method? Also, we should add a @see comment referring to the access delegation issue.

  7. +++ b/core/modules/media_library/src/MediaLibraryFieldWidgetOpener.php
    @@ -0,0 +1,43 @@
    +    if (!$widget_id) {
    +      return new \UnexpectedValueException();
    +    }
    

    This should probably be \InvalidArgumentException, and needs a message.

  8. +++ b/core/modules/media_library/src/Plugin/views/field/MediaLibrarySelectForm.php
    @@ -127,21 +124,16 @@ public function viewsForm(array &$form, FormStateInterface $form_state) {
    +    $response = \Drupal::service($service_id)->respondToSelection(
    +      $selected_ids,
    +      $media_library_state
    +    );
    +    return $response->addCommand(new CloseDialogCommand());
    

    Nit: This can be one chained call.

wim leers’s picture

Status: Needs work » Needs review
StatusFileSize
new5.8 KB
new22.6 KB

#31:

  1. Are you sure? Other places in core do this too for required-but-optional-because-BC reasons. I personally agree with you (and that's what I did originally), but core doesn't seem to agree with us :)
  2. See 2 :).
  3. Ditto.

The last submitted patch, 30: 3044649-30.patch, failed testing. View results

alexpott’s picture

Re #32.2 core is pretty confused on constructor argument change. @Berdir and I have talked about writing a docs page / policy issue for a while but we've never got round to it. I agree with @phenaproxima here let's document the correct usage not the deprecated usage.

phenaproxima’s picture

Regarding hashing: let's add two new methods to MediaLibraryState: getOpenerParameter() and setOpenerParameter(). When invoked, they get or set values in a media_library_opener_parameters sub-array, which could be automatically serialized and included in the output of getHash(), and would be represented this way in the generated query strings:

/path/to/page?media_library_opener_id=whatever&media_library_opener_parameters[foo]=bar&media_library_parameters[baz]=wambooli

This would allow openers to easily get and set parameters which should be tamper-proofed by the hash.

Status: Needs review » Needs work

The last submitted patch, 32: 3044649-32.patch, failed testing. View results
- codesniffer_fixes.patch Interdiff of automated coding standards fixes only.

phenaproxima’s picture

Status: Needs work » Needs review
Issue tags: +Needs tests
StatusFileSize
new25.84 KB
new4.66 KB

Here's an implementation of that idea. We'll need some tests to ensure that the new methods work, and that any values therein are taken into account by getHash().

Status: Needs review » Needs work

The last submitted patch, 37: 3044649-37.patch, failed testing. View results
- codesniffer_fixes.patch Interdiff of automated coding standards fixes only.

phenaproxima’s picture

Status: Needs work » Needs review
StatusFileSize
new30.62 KB
new12.14 KB

This fixes MediaLibraryTest on my machine. I fixed some incorrect method calls, and made sure the opener parameters are handled in a consistent way. Still needs explicit tests, though.

phenaproxima’s picture

StatusFileSize
new31.88 KB
new1.99 KB

Improved documentation on MediaLibraryState.

phenaproxima’s picture

StatusFileSize
new33.2 KB
new2.12 KB

A few more small documentation changes, and added explicit tests of the opener parameter methods on MediaLibraryState. I don't think the explicit test coverage needs to be particularly extensive; with the changes we're making in this issue, the functional tests will break hard if the opener parameters are buggy.

phenaproxima’s picture

Issue tags: -Needs tests
StatusFileSize
new32.23 KB
new5.56 KB

So, in re-reading #41, I realized that setOpenerParameter() is not necessary. Since MediaLibraryState::create() can take an array of opener-specific parameters, there is no need to introduce this additional mutability. So I removed it :)

seanb’s picture

I have some small nits, but in general I think this looks good.

  1. +++ b/core/modules/media_library/src/Form/AddFormBase.php
    @@ -64,11 +71,18 @@ abstract class AddFormBase extends FormBase {
    +   *   (optional) The class resolver.
    
    +++ b/core/modules/media_library/src/Form/FileUploadForm.php
    @@ -65,9 +66,11 @@ class FileUploadForm extends AddFormBase {
    +   *   (optional) The class resolver.
    
    +++ b/core/modules/media_library/src/Form/OEmbedForm.php
    @@ -48,9 +49,11 @@ class OEmbedForm extends AddFormBase {
    +   *   (optional) The class resolver.
    

    This is not optional right?

  2. +++ b/core/modules/media_library/src/MediaLibraryState.php
    @@ -12,14 +12,13 @@
    + * - media_library_opener_id: The ID of a container service which implements
    
    @@ -31,6 +30,13 @@
    + * @see \Drupal\media_library\MediaLibraryOpenerInterface
    

    Now that we changed the opener to be a service, I thought about renaming the opener ID to something like service ID. After talking to phenaproxima, we decided that since these services are kind of special, the "opener" concept is probably something we should keep to help us describe/explain how this works.

  3. +++ b/core/modules/media_library/src/MediaLibraryState.php
    @@ -244,4 +258,28 @@ public function getAvailableSlots() {
    +  public function getOpenerParameters() {
    ...
    +  public function getOpenerParameter($key) {
    

    Do we really need both?

  4. +++ b/core/modules/media_library/src/Plugin/Field/FieldWidget/MediaLibraryWidget.php
    @@ -107,6 +109,11 @@ public function __construct($plugin_id, $plugin_definition, FieldDefinitionInter
    +    $this->uiBuilder = $ui_builder;
    

    I don't think this is used?

wim leers’s picture

StatusFileSize
new6.09 KB
new32.14 KB

#34: 👍

Patch review:

  1. +++ b/core/modules/media_library/src/Form/AddFormBase.php
    @@ -64,11 +71,18 @@ abstract class AddFormBase extends FormBase {
    +   * @param \Drupal\Core\DependencyInjection\ClassResolverInterface|null $class_resolver
    +   *   (optional) The class resolver.
    
    +++ b/core/modules/media_library/src/Form/FileUploadForm.php
    @@ -65,9 +66,11 @@ class FileUploadForm extends AddFormBase {
    +   * @param \Drupal\Core\DependencyInjection\ClassResolverInterface|null $class_resolver
    +   *   (optional) The class resolver.
    
    +++ b/core/modules/media_library/src/Form/OEmbedForm.php
    @@ -48,9 +49,11 @@ class OEmbedForm extends AddFormBase {
    +   * @param \Drupal\Core\DependencyInjection\ClassResolverInterface|null $class_resolver
    +   *   (optional) The class resolver.
    

    Nit: per #34, (optional) and |null should be removed.

  2. +++ b/core/modules/media_library/src/Form/AddFormBase.php
    @@ -724,20 +739,19 @@ public function updateWidget(array &$form, FormStateInterface $form_state) {
    +    // Allow the opener service to handle the selection.
    

    Nit: "handle" is pretty vague. Could be more specific.

  3. +++ b/core/modules/media_library/src/MediaLibraryOpenerInterface.php
    @@ -0,0 +1,53 @@
    + * Openers that require special parameters or metadata should retrieve them from
    

    "special" sounds … scarily artsy. We want precision. So let's change it to "additional".

  4. +++ b/core/modules/media_library/src/MediaLibraryState.php
    @@ -12,14 +12,13 @@
    + * - media_library_opener_id: The ID of a container service which implements
    

    Nit: do we have non-container services? 😛 Let's omit "container" here.

    EDIT: oh wow we apparently do this all over core. Weird. Never mind then!

  5. +++ b/core/modules/media_library/src/MediaLibraryState.php
    @@ -31,6 +30,13 @@
    + * This object can also carry an optional array of arbitrary values, under the
    + * media_library_opener_parameters key. These values are included in the hash
    + * generated by ::getHash(), but not otherwise validated by this object. They
    + * may be validated or used in specialized ways by specific opener services.
    

    This wording really confused me. Clarified.

  6. +++ b/core/modules/media_library/src/Plugin/views/field/MediaLibrarySelectForm.php
    @@ -53,10 +51,6 @@ public function viewsForm(array &$form, FormStateInterface $form_state) {
    -    // Add the view to the form state so the opener ID can be fetched from the
    -    // view request object in ::updateWidget().
    -    $form_state->set('view', $this->view);
    
    @@ -120,27 +114,23 @@ public function viewsForm(array &$form, FormStateInterface $form_state) {
    +   * @param \Symfony\Component\HttpFoundation\Request $request
    +   *   The current request.
    

    This seems like out-of-scope refactoring, but it's both minimal and makes things simpler, so I'm fine with keeping this in this patch.

So, basically only nits. Thanks for getting it to green.

phenaproxima’s picture

StatusFileSize
new30.21 KB
new4.83 KB

Thanks all!

All feedback is fixed. With regard to #43.3, the answer is no, we don't need both; there is a hard technical reason why we need the plural getOpenerParameters() in \Drupal\media_library\MediaLibraryUiBuilder::buildMediaTypeMenu(), but the singular getOpenerParameter() is a convenience method. So I removed that with @seanB's blessing.

wim leers’s picture

Status: Needs review » Reviewed & tested by the community
  • Previous RTBC patch (#27): 9 files changed, 278 insertions, 56 deletions.
  • Current patch (#45): 11 files changed, 225 insertions, 102 deletions.

So:

  1. patch with an architecture that results in a narrower scope/API surface, per @alexpott's suggestions (yay!)
  2. fewer insertions, more deletions (yay!)
alexpott’s picture

Status: Reviewed & tested by the community » Needs work
  1. This look much much nicer than the event system and cleaner too. Thank you for changing the solution.
  2. +++ b/core/modules/media_library/src/MediaLibraryOpenerInterface.php
    @@ -0,0 +1,53 @@
    + * Openers that require additional parameters or metadata should retrieve them
    + * from the MediaLibraryState object.
    

    Add an @see to the MediaLibraryState

  3. +++ b/core/modules/media_library/src/MediaLibraryOpenerInterface.php
    @@ -0,0 +1,53 @@
    +   *   The user for which to check access, or NULL to check access for the
    +   *   current user. Defaults to NULL.
    

    If the NULL ness is true that we need = NULL on the method.

  4. +++ b/core/modules/media_library/src/MediaLibraryOpenerInterface.php
    @@ -0,0 +1,53 @@
    +  public function checkAccess(MediaLibraryState $state, AccountInterface $account);
    ...
    +  public function getSelectionResponse(array $selected_ids, MediaLibraryState $state);
    

    It's odd that the state argument is in different places.

  5. +++ b/core/modules/media_library/src/Plugin/views/field/MediaLibrarySelectForm.php
    @@ -120,27 +114,23 @@ public function viewsForm(array &$form, FormStateInterface $form_state) {
    +    $service_id = $media_library_state->getOpenerId();
    +    return \Drupal::service($service_id)
    +      ->getSelectionResponse($selected_ids, $media_library_state)
    +      ->addCommand(new CloseDialogCommand());
    

    Should we more explicit about interfaces. I.e something like

    $service = \Drupal::service($media_library_state->getOpenerId());
    if (!($service instanceof MediaLibraryOpenerInterface)) {
      throw \RuntimeException('Some message');
    }
    
wim leers’s picture

Excellent nits, I wish I spotted those too :)

phenaproxima’s picture

Status: Needs work » Needs review
StatusFileSize
new30.62 KB
new5.18 KB

All feedback from #47 is addressed in here.

wim leers’s picture

Status: Needs review » Reviewed & tested by the community
alexpott’s picture

Status: Reviewed & tested by the community » Needs work

Discussed with @phenaproxima and after looking at the access patch I think we should have a very very thin service that gets an MediaLibraryOpenerInterface service from the container when given a MediaLibraryState object. That way the class resolver won't end up being injected everywhere. And we can keep the interface checking in a central location.

phenaproxima’s picture

Status: Needs work » Needs review
StatusFileSize
new32.35 KB
new11.63 KB

Here ya go! Meet \Drupal\media_library\OpenerResolverInterface and its offspring.

alexpott’s picture

+++ b/core/modules/media_library/src/OpenerResolver.php
@@ -0,0 +1,31 @@
+  /**
+   * {@inheritdoc}
+   */
+  public function get($service_id) {

Instead of a service ID i'd make this take an instance of MediaLibrabryState

phenaproxima’s picture

StatusFileSize
new32.42 KB
new2.63 KB

Done.

nuez’s picture

Status: Needs review » Reviewed & tested by the community
phenaproxima’s picture

StatusFileSize
new32.42 KB
new577 bytes

Fixed a typo in the doc comment of MediaLibraryOpenerInterface. No need to change from RTBC, since it's a one-line change (albeit a significant one).

alexpott’s picture

Status: Reviewed & tested by the community » Fixed

Committed and pushed ca0ac736fd to 8.8.x and 9a654bb02a to 8.7.x. Thanks!

Backported to 8.7.x since media library is experimental.

diff --git a/core/modules/media_library/src/Plugin/views/field/MediaLibrarySelectForm.php b/core/modules/media_library/src/Plugin/views/field/MediaLibrarySelectForm.php
index 4afac3cfb7..fcb8627ba7 100644
--- a/core/modules/media_library/src/Plugin/views/field/MediaLibrarySelectForm.php
+++ b/core/modules/media_library/src/Plugin/views/field/MediaLibrarySelectForm.php
@@ -6,7 +6,6 @@
 use Drupal\Core\Form\FormBuilderInterface;
 use Drupal\Core\Form\FormStateInterface;
 use Drupal\Core\Url;
-use Drupal\media_library\MediaLibraryOpenerInterface;
 use Drupal\media_library\MediaLibraryState;
 use Drupal\views\Plugin\views\field\FieldPluginBase;
 use Drupal\views\Render\ViewsRenderPipelineMarkup;

Fixed unused use on commit.

  • alexpott committed ca0ac73 on 8.8.x
    Issue #3044649 by phenaproxima, seanB, Wim Leers, alexpott: Delegate...

  • alexpott committed 9a654bb on 8.7.x
    Issue #3044649 by phenaproxima, seanB, Wim Leers, alexpott: Delegate...
wim leers’s picture

Backported to 8.7.x since media library is experimental.

Yay, this means #2998005: [PP-1] Support Drupal core's Media Library can also use this!

rosinegrean’s picture

Issue tags: -DevDaysCluj +DevDaysTransylvania

Status: Fixed » Closed (fixed)

Automatically closed - issue fixed for 2 weeks with no activity.