Problem/Motivation

A project we're working on needs two different form modes for the media-instance Add forms on e.g. a Node field. The difference is per field, and so configuration would be in the field widget.

In this case the need arises because some media fields need to use manual crop for the image while others need a focal point method, and so a single form mode (media_library) is insufficient to the task.

Steps to reproduce

The media library hard-codes both the form mode and the preview image style using the name 'media_library', the form mode being set in AddFormBase.php. There are no workarounds.

Proposed resolution

A patch is presented which introduces a new select element on the media library widget, together with the needed changes to the MediaLibraryState object to pass this value along to the place it's needed in AddFormBase::buildForm().

Remaining tasks

I think this works. Test code has been adjusted so it still works, but no new test code has been added.

This patch includes some changes to source formatting mostly to reduce line lengths to somewhat reasonable limits.

User interface changes

A new Select element is added to the Media Library Form widget. It defaults to the existing value (Media Library | media_library).

API changes

A new class, "media_library_form_mode--" + the form mode, is added to the <form> element.

Data model changes

The MediaLibraryState has been extended, but initializations have compatible defaults.

It is possible that media embed code might need adjusting as well (as has been done here for ckeditor).

Release notes snippet

Issue fork drupal-3323232

Command icon Show commands

Start within a Git clone of the project using the version control instructions.

Or, if you do not have SSH keys set up on git.drupalcode.org:

Comments

rivimey created an issue. See original summary.

nivethasubramaniyan’s picture

StatusFileSize
new45.43 KB

Fixing patch issue

nivethasubramaniyan’s picture

StatusFileSize
new46.72 KB

Fixing CCF #2

Status: Needs review » Needs work

The last submitted patch, 3: 3323232-3.patch, failed testing. View results

rivimey’s picture

StatusFileSize
new36.88 KB

@NivethaSubramaniyan thanks for your help, hadn't expected any :-)

I was asked to reduce the number of code style improvements (aka whitespace changes!) and did that before I saw your patch. I believe I've incorporated all the changes you made (good catch on the tests missing form mode var). Although I've retained media library state tests having a class variable I don't think it's actually used (yet).

This patch will probably fail for the same reason the last did - config schema update failure. Not yet sure why.

rivimey’s picture

StatusFileSize
new36.83 KB

Some further work, fix patch format.

heddn’s picture

  1. +++ b/core/modules/ckeditor5/src/Plugin/CKEditor5Plugin/MediaLibrary.php
    @@ -81,10 +81,14 @@ public function getDynamicPluginConfig(array $static_plugin_config, EditorInterf
    +    // @todo where should this be pulled from? The field widget specifies.
    

    this would have to be something set on the media-embed filter plugin config and we get it from there.

  2. +++ b/core/modules/media_library/src/Form/AddFormBase.php
    @@ -117,7 +118,9 @@ protected function getMediaType(FormStateInterface $form_state) {
    +    $this->mediaType = $this->entityTypeManager
    

    please revert this whitespace change.

  3. +++ b/core/modules/media_library/src/Form/AddFormBase.php
    @@ -152,6 +161,7 @@ public function buildForm(array $form, FormStateInterface $form_state) {
    +      Html::getClass('media_library_form_mode--' . $add_form_mode),
    

    no need to add a css class here.

  4. +++ b/core/modules/media_library/src/Form/AddFormBase.php
    @@ -191,7 +201,8 @@ public function buildForm(array $form, FormStateInterface $form_state) {
    -        $form['media'][$delta] = $this->buildEntityFormElement($media, $form, $form_state, $delta);
    

    Can we add the form mode as a stored value in form_state? I see that as a more normal mechanism to pass around data for forms.

  5. +++ b/core/modules/media_library/src/Form/AddFormBase.php
    @@ -191,7 +201,8 @@ public function buildForm(array $form, FormStateInterface $form_state) {
    +        $form['media'][$delta] = $this->buildEntityFormElement(
    +          $media, $form, $form_state, $delta, $add_form_mode);
    

    please don't change the whitespace here.

  6. +++ b/core/modules/media_library/src/Form/AddFormBase.php
    @@ -245,11 +256,15 @@ abstract protected function buildInputElement(array $form, FormStateInterface $f
    +  protected function buildEntityFormElement(MediaInterface $media, array $form,
    +    FormStateInterface $form_state, int $delta, string $add_form_mode = 'media_library') {
    

    This isn't needed if we pass the form mode as a stored value in form state.

  7. +++ b/core/modules/media_library/src/Form/FileUploadForm.php
    @@ -257,13 +257,24 @@ public function processUploadElement(array $element, FormStateInterface $form_st
    +   * Build the sub form for media entities, but modify base to slim it down.
    

    Please revert this change.

  8. +++ b/core/modules/media_library/src/Form/FileUploadForm.php
    @@ -257,13 +257,24 @@ public function processUploadElement(array $element, FormStateInterface $form_st
    +      $element['fields'][$source_field]['widget'][0]['#process'][] = [
    

    please revert this whitespace change.

  9. +++ b/core/modules/media_library/src/Form/FileUploadForm.php
    @@ -271,6 +282,9 @@ protected function buildEntityFormElement(MediaInterface $media, array $form, Fo
    +   * Removes the remove_button, preview, title, description and filename
    

    please revert this change.

  10. +++ b/core/modules/media_library/src/Form/FileUploadForm.php
    @@ -358,6 +372,7 @@ protected function createFileItem(MediaTypeInterface $media_type) {
    +    // @todo $media->bundle is not part of MediaInterface.
    

    please revert this.

  11. +++ b/core/modules/media_library/src/Form/FileUploadForm.php
    @@ -381,6 +396,7 @@ public function removeButtonSubmit(array $form, FormStateInterface $form_state)
    +    // @todo $media->bundle is not part of MediaInterface.
    

    please revert this.

  12. +++ b/core/modules/media_library/src/MediaLibraryState.php
    @@ -71,11 +81,12 @@ public function __construct(array $parameters = []) {
    +  public static function create($opener_id, array $allowed_media_type_ids, $selected_type_id, $form_mode, $remaining_slots, array $opener_parameters = []) {
    
    +++ b/core/modules/media_library/src/Plugin/Field/FieldWidget/MediaLibraryWidget.php
    @@ -262,14 +286,20 @@ public static function setMediaTypesValue(array &$element, $input, FormStateInte
    +
    

    This breaks a lot of contrib modules when we add the form mode parameter. We'll need to add it as the last parameter and do a BC dance.

  13. +++ b/core/modules/media_library/src/MediaLibraryUiBuilder.php
    @@ -239,7 +239,14 @@ protected function buildMediaTypeMenu(MediaLibraryState $state) {
    -      $link_state = MediaLibraryState::create($state->getOpenerId(), $state->getAllowedTypeIds(), $allowed_type_id, $state->getAvailableSlots(), $state->getOpenerParameters());
    +      $link_state = MediaLibraryState::create(
    

    please revert most of the whitespace changes here.

  14. +++ b/core/modules/media_library/src/MediaLibraryUiBuilder.php
    @@ -310,7 +317,9 @@ protected function buildMediaTypeAddForm(MediaLibraryState $state) {
    -    return $this->formBuilder->buildForm($plugin_definition['forms']['media_library_add'], $form_state);
    +
    +    $form_class = $plugin_definition['forms']['media_library_add'];
    +    return $this->formBuilder->buildForm($form_class, $form_state);
    

    this change isn't needed. can we revert it.

  15. +++ b/core/modules/media_library/src/Plugin/Field/FieldWidget/MediaLibraryWidget.php
    @@ -217,6 +230,17 @@ public function settingsForm(array $form, FormStateInterface $form_state) {
    +    $form_modes = $this->entityDisplayRepository
    +      ->getFormModeOptions('media');
    
    @@ -262,14 +286,20 @@ public static function setMediaTypesValue(array &$element, $input, FormStateInte
    +    $form_modes = $this->entityDisplayRepository->getFormModeOptions('media');
    

    In the first case, we line wrap. In the second, we do it one longer line. I prefer the second as its less connotative dissonance to read across 2 lines when the things are fairly short. Let's do the same thing in both places.

  16. +++ b/core/modules/media_library/src/Plugin/Field/FieldWidget/MediaLibraryWidget.php
    @@ -262,14 +286,20 @@ public static function setMediaTypesValue(array &$element, $input, FormStateInte
    -    $media_type_labels = [];
    ...
    +      $media_type_labels = [];
    

    If we don't remove this line and move it down below, it makes the code more happy when applying in 9.4/9.5. Let's not fix up anything except the summary additions.

  17. +++ b/core/modules/media_library/src/Plugin/Field/FieldWidget/MediaLibraryWidget.php
    @@ -262,14 +286,20 @@ public static function setMediaTypesValue(array &$element, $input, FormStateInte
    +
    

    Whitespace change. Please revert.

  18. +++ b/core/modules/media_library/src/Plugin/Field/FieldWidget/MediaLibraryWidget.php
    @@ -496,7 +526,17 @@ public function formElement(FieldItemListInterface $items, $delta, array $elemen
    +    // @todo where should this be pulled from? The field widget specifies.
    

    The field widget should have this as a config option.

  19. +++ b/core/modules/media_library/src/Plugin/Field/FieldWidget/MediaLibraryWidget.php
    @@ -531,7 +571,9 @@ public function formElement(FieldItemListInterface $items, $delta, array $elemen
    -      if ($triggering_element && ($trigger_parents = $triggering_element['#array_parents']) && end($trigger_parents) === 'media_library_update_widget') {
    

    whitespace noise. please revert.

  20. +++ b/core/modules/media_library/tests/src/FunctionalJavascript/WidgetAccessTest.php
    @@ -58,14 +58,23 @@ public function testWidgetAccess() {
    +    $form_mode = 'media_library';
    

    this is a test, no need for a todo here.

  21. +++ b/core/modules/media_library/tests/src/FunctionalJavascript/WidgetAccessTest.php
    @@ -58,14 +58,23 @@ public function testWidgetAccess() {
    +    $state = MediaLibraryState::create(
    

    please revert the whitespace fixes here.

  22. +++ b/core/modules/media_library/tests/src/FunctionalJavascript/WidgetAccessTest.php
    @@ -125,6 +134,9 @@ public function testWidgetAccess() {
    +      'query' => array_merge($url_options['query'], ['media_library_form_mode' => '   ']),
    

    is the form mode intentionally empty here?

  23. +++ b/core/modules/media_library/tests/src/Kernel/MediaLibraryAccessTest.php
    @@ -108,11 +108,20 @@ public function testFieldWidgetEntityCreateAccess() {
    +    $state = MediaLibraryState::create(
    

    please revert the whitespace changes here.

  24. +++ b/core/modules/media_library/tests/src/Kernel/MediaLibraryAccessTest.php
    @@ -158,10 +167,14 @@ public function testEditorOpenerAccess($media_embed_enabled, $can_use_format) {
    +    // @todo where should this be pulled from? The field widget specifies.
    

    no need to ask. this is a test.

  25. +++ b/core/modules/media_library/tests/src/Kernel/MediaLibraryAccessTest.php
    @@ -213,13 +226,23 @@ public function testFieldWidgetEntityEditAccess() {
    +    $state = MediaLibraryState::create(
    

    please revert whitespace.

  26. +++ b/core/modules/media_library/tests/src/Kernel/MediaLibraryAccessTest.php
    @@ -302,8 +326,17 @@ public function testFieldWidgetEntityFieldAccess(string $field_type) {
    +    // @todo where should this be pulled from? The field widget specifies.
    

    no need to ask, this is a test.

  27. +++ b/core/modules/media_library/tests/src/Kernel/MediaLibraryAccessTest.php
    @@ -302,8 +326,17 @@ public function testFieldWidgetEntityFieldAccess(string $field_type) {
    +    $state = MediaLibraryState::create(
    

    please revert whitespace.

  28. +++ b/core/modules/media_library/tests/src/Kernel/MediaLibraryAccessTest.php
    @@ -339,11 +373,20 @@ public function testViewAccess() {
    +    // @todo where should this be pulled from? The field widget specifies.
    

    same here. and through the rest of the tests. for both the form mode and the whitespace changes.

rivimey’s picture

StatusFileSize
new37.31 KB

Reverted more ws

Fix settingsForm() (no form mode selector if only one media type set up);

Changes to MediaLibraryState to follow.

rivimey’s picture

StatusFileSize
new38.81 KB
new19.38 KB

MediaLibraryState change: move new form_mode to end of sig.

rivimey’s picture

Status: Needs work » Needs review
rivimey’s picture

StatusFileSize
new39.71 KB
new3.67 KB

Add in a default view mode constant and use; use default form mode for more things

rivimey’s picture

StatusFileSize
new39.57 KB
new2.31 KB

Status: Needs review » Needs work

The last submitted patch, 12: 3323232-12.patch, failed testing. View results

rivimey’s picture

StatusFileSize
new4.36 KB
new40.45 KB

Three fixes:
- Fix medialib test module schema - add form mode to widget
- Additional form mode default strings - quoted string to use of const var;
- Fix use of static form mode - using as object but decl static => object.

rivimey’s picture

Issue summary: View changes
rivimey’s picture

Status: Needs work » Needs review

Status: Needs review » Needs work

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

heddn’s picture

Status: Needs work » Needs review
StatusFileSize
new24.34 KB
new27.54 KB

Here's some clean-up. I'll have some comments coming next.

heddn’s picture

Status: Needs review » Needs work
  1. +++ b/core/modules/media_library/src/MediaLibraryState.php
    @@ -41,14 +43,25 @@
    +  public const DEFAULT_FORM_MODE = 'media_library';
    
    +++ b/core/modules/media_library/src/Plugin/Field/FieldWidget/MediaLibraryWidget.php
    @@ -45,6 +46,11 @@
    +  const DEFAULT_VIEW_MODE = 'media_library';
    

    These are duplicates of each other. Let's use the value from State everywhere instead of the widget.

  2. +++ b/core/modules/media_library/tests/src/FunctionalJavascript/WidgetAccessTest.php
    @@ -125,6 +125,9 @@ public function testWidgetAccess() {
    +    $this->drupalGet('media-library', [
    

    What is this testing?

  3. +++ b/core/modules/media_library/tests/src/Kernel/MediaLibraryAddFormTest.php
    @@ -35,6 +35,13 @@ class MediaLibraryAddFormTest extends KernelTestBase {
    +  protected $form_mode = 'media_library';
    
    @@ -112,7 +119,7 @@ public function testMediaTypeAddForm() {
    +    $state = MediaLibraryState::create('test', ['image', 'remote_video'], $selected_type_id, -1, [], $this->form_mode);
    

    Let's use the constant from State instead of this value here. Let's use the constants in all the creates directly instead of a class level property.

  4. +++ b/core/modules/media_library/tests/src/Kernel/MediaLibraryStateTest.php
    @@ -21,6 +21,11 @@ class MediaLibraryStateTest extends KernelTestBase {
    +  protected $form_mode = MediaLibraryState::DEFAULT_FORM_MODE;
    
    @@ -70,14 +75,14 @@ public function testMethods() {
    +    $state = MediaLibraryState::create($opener_id, $allowed_media_type_ids, $selected_media_type_id, $remaining_slots, [], $this->form_mode);
    

    Instead of setting this as a class level property, let's do individually on each of the state constructions. More configurablity and it will be clearer what is going on.

rivimey’s picture

@heddn, re:
(1), these aren't duplicates - one is a form mode and one a view mode. I haven't done any work around the view mode but decided that giving this particular constant string a defined name was good for clarity and stopped me thinking it was a form mode string I'd missed!

I could put both in the same place, but given the State class is only about forms, I decided against that.

(2) It's testing that the validation of a form-mode string does reject invalid strings.

(3) ok - I thought of that but rejected it because that would mean it automatically used the 'right' value, whereas in testing we're trying to find out if the right value is the right value, if that makes sense.

(4) ok - I went for something simple as there were multiple uses.

heddn’s picture

  1. If we aren't touching view mode, then we shouldn't touch it at all. Let's keep out of scope changes to a minimum.
  2. Hmm, there was already an earlier invalid value, right? How would this do anything that wasn't already happening?
  3. If we need to use a wrong value, by all means don't use the constant. But if we are using the right value, then we should use the constant.
rivimey’s picture

StatusFileSize
new15.12 KB
new41.42 KB

(1) ok, deleted.

(2) deleted. however, I don't think that the tests cover any value other than 'media_library'.

(3) adjusted (& updated a few places to change likewise).

(4) updated to use local form_mode var.

heddn’s picture

Why didn't this build on top of #18? Or at least it doesn't seem like it builds?

rivimey’s picture

StatusFileSize
new16.6 KB
new39.27 KB
  • Remove change to add DEFAULT_VIEW_MODE.
  • remove form_mode fail test.
  • use DEFAULT_FORM_MODE const in tests.
  • use local variable $form_mode in tests.
  • adjust settingsForm() to avoid need for changing core indent.
rivimey’s picture

StatusFileSize
new38.72 KB
new7.51 KB

Incorporate multiple changes from #18 & Improve comments:

  • A number of small improvements.
    * Integrate form_modes fetch into array construction;
    * update further comments;
    * use (string) as form_mode type when possible, enabling a better validation condition;
    * add explicit public for DEFAULT_FORM_MODE.
  • Revert earlier line-length reformat
  • Improve comment wording; use $media_state as it was intended
  • Undo change to ckeditor5 - rely on defaults
rivimey’s picture

StatusFileSize
new3.13 KB
new38.81 KB

Cope with explicitly-passed NULL form mode to create/validate state.
Modify settingsSummary change because for some reason patch refused to apply hunk occasionally.

rivimey’s picture

Status: Needs work » Needs review
rivimey’s picture

StatusFileSize
new1.27 KB
new39.1 KB

Updated patch to fix phpcs notices.

_utsavsharma’s picture

StatusFileSize
new1.58 KB
new38.81 KB

Fixed CCF for #26.
Please review.

Status: Needs review » Needs work

The last submitted patch, 29: 3323232-29.patch, failed testing. View results

rivimey’s picture

Status: Needs work » Needs review
StatusFileSize
new38.82 KB
new1014 bytes

Re #29 sadly that is the wrong fix - I would have liked it to be the right one, but in fact the code has to be able to accept NULL for form_mode even though there is a default arg value because some code passes NULL explicitly.

My patch #28 doesn't work because I included a change to autoload.php. Removed.

heddn’s picture

Status: Needs review » Needs work
+++ b/core/modules/media_library/config/schema/media_library.schema.yml
@@ -8,6 +8,9 @@ field.widget.settings.media_library_widget:
+    form_mode:

we'll need an update hook to add a default value for the form_mode anywhere the widget is used.

heddn’s picture

Issue tags: +Needs change record

This will also need a CR at some point. Several contrib modules have the form mode hard coded. For example, focal_point uses a hard-coded value.

rivimey’s picture

@heddn I don't understand your request for a hook - the form_mode will be stored in the same schema that stores the existing state...

Is it that some other code thinks it 'knows' media_library is the right form to use and hard-codedly fetches that? I'm not sure how anything can be done except file issues to fix it once this change is merged?

For reference, I believe this is the focal_point module function that would need changing:

function focal_point_form_media_library_add_form_upload_alter(array &$form, FormStateInterface $form_state) {
  // Get any media items that are in the process of being added.
  // @see \Drupal\media_library\Form\AddFormBase::getAddedMediaItems().
  $media = $form_state->get('media') ?: [];
  /** @var \Drupal\media\MediaInterface $item */
  foreach ($media as $delta => $item) {
    $element = &$form['media'][$delta]['fields'];
    // As a kindness to alter hooks like this one, Media Library includes the
    // name of the source field in the form structure.
    // @see \Drupal\media_library\Form\AddFormBase::buildEntityFormElement()
    $source_field = $element['#source_field_name'];
    // If the source field is configured to use Focal Point, add a #process
    // callback which replaces the static preview thumbnail with the Focal Point
    // widget.
    $component = \Drupal::service('entity_display.repository')
      ->getFormDisplay('media', $item->bundle(), 'media_library')
      ->getComponent($source_field);
    if ($component && $component['type'] === 'image_focal_point' && isset($element[$source_field])) {
      $element[$source_field]['widget'][0]['#process'][] = '_focal_point_replace_media_library_preview';
    }
  }
}

Version: 10.1.x-dev » 11.x-dev

Drupal core is moving towards using a “main” branch. As an interim step, a new 11.x branch has been opened, as Drupal.org infrastructure cannot currently fully support a branch named main. New developments and disruptive changes should now be targeted for the 11.x branch, which currently accepts only minor-version allowed changes. For more information, see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

graber’s picture

Styling of links got broken after some recent core update. Going for a local solution and documenting here in case this issue got more attention again eventually:
in implementation of hook_preprocess_links__media_library_menu()

$variables['#attached']['library'][] = 'core/drupal.vertical-tabs';

Probably a vertical tabs #type should be used here instead of the '#theme' => 'links__media_library_menu', that has a couple of preprocess in various themes but not much, currently basically those are links and not much more.

graber’s picture

Ok, actually it's a core issue and has nothing to do with the work here. Reproduced on a clean instance with no patch.
Ignore my last comment please.

DieterHolvoet made their first commit to this issue’s fork.

dieterholvoet’s picture

I started an MR targeting 11.x with a rebased version of 3323232-29.patch.

dieterholvoet’s picture

graber’s picture

StatusFileSize
new23.06 KB

Here's a re-roll for 10.3 with test changes removed for more simplicity.

graber’s picture

StatusFileSize
new23.08 KB

No comment.

Version: 11.x-dev » main

Drupal core is now using the main branch as the primary development branch. New developments and disruptive changes should now be targeted to the main branch.

Read more in the announcement.