Problem/Motivation

When a new Media Type is created over the UI, the source field is automatically created. However, the form shows this field after the "Save" button, which doesn't feel right:

Proposed resolution

Provide better defaults for the form display of media entities when creating a type on the UI.

Remaining tasks

User interface changes

API changes

Data model changes

Support from Acquia helps fund testing for Drupal Acquia logo

Comments

marcoscano created an issue. See original summary.

Berdir’s picture

Suprised to see it even below the save button, isn't that supposed to be #weight 99 or something due to being a type actions?

marcoscano’s picture

Let's start with the failing test.

marcoscano’s picture

Status: Active » Needs review
FileSize
4.07 KB
937 bytes

In the end I changed the location of the test, because inside MediaUiFunctionalTest the type is created programmatically, so the fix doesn't apply. Inside MediaTypeCreationTest everything works as expected.

marcoscano’s picture

+++ b/core/modules/media/tests/modules/media_test_source/src/Plugin/media/Source/TestDifferentDisplays.php
@@ -22,6 +22,7 @@ class TestDifferentDisplays extends Test {
+    parent::prepareViewDisplay($type, $display);

@@ -31,6 +32,7 @@ public function prepareViewDisplay(MediaTypeInterface $type, EntityViewDisplayIn
+    parent::prepareFormDisplay($type, $display);

+++ b/core/modules/media/tests/modules/media_test_source/src/Plugin/media/Source/TestWithHiddenSourceField.php
@@ -22,6 +22,7 @@ class TestWithHiddenSourceField extends Test {
+    parent::prepareViewDisplay($type, $display);

@@ -29,6 +30,7 @@ public function prepareViewDisplay(MediaTypeInterface $type, EntityViewDisplayIn
+    parent::prepareFormDisplay($type, $display);

By the way, these are not strictly part of the patch, but I thought it might be a good idea to have inside our test code the best possible example of the method implementations, and IMHO it would be important here to call the base class method, where the fix lives, for instance.

Status: Needs review » Needs work

The last submitted patch, 4: 2934162-4-test-only-FAIL.patch, failed testing. View results

marcoscano’s picture

Status: Needs work » Needs review

The fail patch correctly fails.

chr.fritsch’s picture

Status: Needs review » Needs work

This looks really good. Just found one thing:

+++ b/core/modules/media/tests/src/FunctionalJavascript/MediaTypeCreationTest.php
@@ -53,6 +53,11 @@ public function testMediaTypeCreationFormWithDefaultField() {
+    $this->drupalGet("/media/add/$mediaTypeMachineName");
+    $assert_session->elementNotExists('css', 'form #edit-actions + div');

Could we also verify somehow that the source field is now directly under the name field?

Berdir’s picture

I usually do that by checking the position of a string in the text, not sure if there is an official way, at least there wasn't in Simpletest.

So basically get the text, then do a strpos for "Name", "File", "Url Alias" or so, and check that the position of Name is lower than that of "File" and so on.

marcoscano’s picture

Status: Needs work » Needs review
FileSize
4.47 KB
1.4 KB

Oh that's a neat trick, thanks! :)

Berdir’s picture

  1. +++ b/core/modules/media/src/MediaSourceBase.php
    @@ -330,7 +330,10 @@ public function prepareViewDisplay(MediaTypeInterface $type, EntityViewDisplayIn
       public function prepareFormDisplay(MediaTypeInterface $type, EntityFormDisplayInterface $display) {
    -    $display->setComponent($this->getSourceFieldDefinition($type)->getName());
    +    // Make sure the source field is placed just after the "name" basefield.
    +    $display->setComponent($this->getSourceFieldDefinition($type)->getName(), [
    +      'weight' => $display->getComponent('name')['weight'] + 0.1,
    +    ]);
    

    I see what you're trying to do, but the weight is defined as an integer in the schema, so should we really set it to a float here? I think it will be cast to an integer again at some point due to the config schema casting/validation, but that also means it is somewhat unpredictable..

    I'd just do a + 1, worst case is that it then has the same weight as the next field, which is still better than right now. Or we explicitly re-set all weights but I think that's overkill.

    You also blindly rely on the name component being visible, but we for example have issues tha are discussing to hide it by default (Although, has commented there, I think that's the wrong approach). But better safe than sorry. Check that you have a name component, fall back to -50 or something? If there's no name, it should probably be first..

  2. +++ b/core/modules/media/tests/modules/media_test_source/src/Plugin/media/Source/TestDifferentDisplays.php
    @@ -31,6 +32,7 @@ public function prepareViewDisplay(MediaTypeInterface $type, EntityViewDisplayIn
        */
       public function prepareFormDisplay(MediaTypeInterface $type, EntityFormDisplayInterface $display) {
    +    parent::prepareFormDisplay($type, $display);
         $display->setComponent($this->getSourceFieldDefinition($type)->getName(), [
           'type' => 'entity_reference_autocomplete_tags',
         ]);
    

    I don't think this is working as you think it does.

    setComponent() doesn't merge, not like that. Especially not the weight, you can see in the method that if there's no weight in the input, it explicitly sets it to the heighest weight. So pretty sure the second call would then unset your weight again.

    That means doing this properly either requires you to get the current component configuration, set the weight and set it again or offer someting like a getDefaultComponentWeight($display) method that can be re-used by the child instead of calling the parent.

  3. +++ b/core/modules/media/tests/modules/media_test_source/src/Plugin/media/Source/TestWithHiddenSourceField.php
    @@ -22,6 +22,7 @@ class TestWithHiddenSourceField extends Test {
        */
       public function prepareViewDisplay(MediaTypeInterface $type, EntityViewDisplayInterface $display) {
    +    parent::prepareViewDisplay($type, $display);
         $display->removeComponent($this->getSourceFieldDefinition($type)->getName());
       }
    

    and this is pretty bogus too, if all we do is remove the component again.

marcoscano’s picture

Thanks @Berdir!

chr.fritsch’s picture

Status: Needs review » Needs work
+++ b/core/modules/media/tests/modules/media_test_source/src/Plugin/media/Source/TestDifferentDisplays.php
@@ -22,7 +22,6 @@ class TestDifferentDisplays extends Test {
   public function prepareViewDisplay(MediaTypeInterface $type, EntityViewDisplayInterface $display) {
-    parent::prepareViewDisplay($type, $display);
     $display->setComponent($this->getSourceFieldDefinition($type)->getName(), [
       'type' => 'entity_reference_entity_id',
     ]);
@@ -32,7 +31,6 @@ public function prepareViewDisplay(MediaTypeInterface $type, EntityViewDisplayIn

Why not calling the parent, getting the component, changing the type and then re-setting the component like @berdir suggested

marcoscano’s picture

Status: Needs work » Needs review
FileSize
3.82 KB
1.59 KB

Thanks @chr.fritsch, here we go.

chr.fritsch’s picture

Status: Needs review » Needs work
+++ b/core/modules/media/tests/modules/media_test_source/src/Plugin/media/Source/TestDifferentDisplays.php
@@ -22,18 +22,20 @@ class TestDifferentDisplays extends Test {
-    $display->setComponent($this->getSourceFieldDefinition($type)->getName(), [
-      'type' => 'entity_reference_entity_id',
-    ]);
+    $source_name = $this->getSourceFieldDefinition($type)->getName();

Now re-add the parent::prepareViewDisplay(), then everything should be in place

marcoscano’s picture

Status: Needs work » Needs review
FileSize
3.92 KB
1.36 KB

oops, sorry.

chr.fritsch’s picture

Nice, +1 for RTBC from me

phenaproxima’s picture

Status: Needs review » Needs work
  1. +++ b/core/modules/media/src/MediaSourceBase.php
    @@ -330,7 +330,12 @@ public function prepareViewDisplay(MediaTypeInterface $type, EntityViewDisplayIn
    +    $source_weight = ($name_component && array_key_exists('weight', $name_component)) ? $name_component['weight'] + 1 : -50;
    

    We should change array_key_exists() to isset(), to catch null values.

  2. +++ b/core/modules/media/tests/src/FunctionalJavascript/MediaTypeCreationTest.php
    @@ -53,6 +53,15 @@ public function testMediaTypeCreationFormWithDefaultField() {
    +    $form_text = $assert_session->elementExists('css', 'form')->getText();
    

    This could get screwed up if any other form appears on the page, for any reason. Can we find the form using a more specific selector?

  3. +++ b/core/modules/media/tests/src/FunctionalJavascript/MediaTypeCreationTest.php
    @@ -53,6 +53,15 @@ public function testMediaTypeCreationFormWithDefaultField() {
    +    $this->assertTrue(strpos($form_text, 'Name') < strpos($form_text, 'Test source'));
    +    $this->assertTrue(strpos($form_text, 'Test source') < strpos($form_text, 'Vertical Tabs'));
    +    // The "Published" checkbox should be the last element.
    +    $this->assertTrue(strpos($form_text, 'Leave blank to use the time of form submission.') < strpos($form_text, 'Published'));
    

    I'm not sure how else to do this, but asserting the relative positions of text seems quite brittle to me, and prone to things like false positives or false negatives. I'll try to think of an alternate approach to that...

marcoscano’s picture

Status: Needs work » Needs review

Thanks @phenaproxima!

Addressed #18, except for #18.3:

I'm not sure how else to do this, but asserting the relative positions of text seems quite brittle to me, and prone to things like false positives or false negatives. I'll try to think of an alternate approach to that...

We can always play with css selectors, with something like this:

    $form_selector = '#media-' . Html::cleanCssIdentifier($mediaTypeMachineName) . '-add-form';
    // Source field is right after the name field.
    $assert_session->elementExists('css', "$form_selector #edit-name-wrapper + #edit-field-media-test-wrapper");
    // Status checkbox is right before the Save button.
    $assert_session->elementExists('css', "$form_selector #edit-status-wrapper + #edit-actions");
    // No element exists after the Save button.
    $assert_session->elementNotExists('css', "$form_selector #edit-actions + div");

which works, but IMHO I still prefer the strpos() approach though... Any other ideas you can think of?

Thanks!

marcoscano’s picture

forgot to attach the patch, sorry.

phenaproxima’s picture

I found a better way to assert the position of the elements! :)

robpowell’s picture

Assigned: Unassigned » robpowell
effulgentsia’s picture

I think we should also patch core/profiles/standard/config/optional/core.entity_form_display.media.*.yml with some hard-coded weight that's not the current value of 26.

+++ b/core/modules/media/src/MediaSourceBase.php
@@ -330,7 +330,12 @@ public function prepareViewDisplay(MediaTypeInterface $type, EntityViewDisplayIn
+    $source_weight = ($name_component && isset($name_component['weight'])) ? $name_component['weight'] + 1 : -50;

Are we sure we want to do it this way? The weight of 'name' is set by Media::baseFieldDefinitions() to -5. The weight of the URL alias field is set by path_entity_base_field_info() to 30. That gives us a lot of numbers in between, including 0 and -1. Would it be better to just pick one of those, and thereby leaving some room for contrib to put things in-between? Or alternatively, changing 'name' to -20 and setting the source field to -10, or something along those lines?

robpowell’s picture

Assigned: robpowell » Unassigned
robpowell’s picture

@phenaproxima this worked locally for me.

Only local images are allowed.

Small-ish comments below.

  1. +++ b/core/modules/media/src/MediaSourceBase.php
    @@ -330,7 +330,12 @@ public function prepareViewDisplay(MediaTypeInterface $type, EntityViewDisplayIn
    +    $source_weight = ($name_component && isset($name_component['weight'])) ? $name_component['weight'] + 1 : -50;
    

    I don't think this var is named right as this is really the new component's weight. Also, what happens when the condition is False?

  2. +++ b/core/modules/media/tests/src/FunctionalJavascript/MediaTypeCreationTest.php
    @@ -1,6 +1,7 @@
    +use Drupal\Component\Utility\Html;
    

    This needs a space between namespace and use.

  3. +++ b/core/modules/media/tests/src/FunctionalJavascript/MediaTypeCreationTest.php
    @@ -53,6 +54,27 @@ public function testMediaTypeCreationFormWithDefaultField() {
    +    // come before the vertical tabs.
    

    this comment could be more clear "before the test field"

phenaproxima’s picture

Addressed both @effulgentsia and @robpowell's feedback here...

I think we should also patch core/profiles/standard/config/optional/core.entity_form_display.media.*.yml with some hard-coded weight that's not the current value of 26.

Done.

Would it be better to just pick one of those, and thereby leaving some room for contrib to put things in-between?

Yes, it would! I decided to make the source field's weight name + 5.

I don't think this var is named right as this is really the new component's weight.

Discussed with @robpowell, and renamed the variable to $source_field_weight.

marcoscano’s picture

Status: Needs review » Reviewed & tested by the community

Looks good to me

xjm’s picture

In reviewing this I also noticed #2935159: Give default comment fields a heavier weight so they are at the bottom and new fields don't get "lost" , but that happens with any fields, not just Media fields, so it's out of scope for the initiative.

xjm’s picture

Status: Reviewed & tested by the community » Needs work
+++ b/core/profiles/standard/config/optional/core.entity_form_display.media.file.default.yml
@@ -22,7 +22,7 @@ content:
       progress_indicator: throbber
     third_party_settings: {  }
     type: file_generic
-    weight: 26
+    weight: 0
     region: content
   name:
     type: string_textfield
diff --git a/core/profiles/standard/config/optional/core.entity_form_display.media.image.default.yml b/core/profiles/standard/config/optional/core.entity_form_display.media.image.default.yml

diff --git a/core/profiles/standard/config/optional/core.entity_form_display.media.image.default.yml b/core/profiles/standard/config/optional/core.entity_form_display.media.image.default.yml
index 6f2323b778..4488b02eeb 100644

index 6f2323b778..4488b02eeb 100644
--- a/core/profiles/standard/config/optional/core.entity_form_display.media.image.default.yml

--- a/core/profiles/standard/config/optional/core.entity_form_display.media.image.default.yml
+++ b/core/profiles/standard/config/optional/core.entity_form_display.media.image.default.yml

+++ b/core/profiles/standard/config/optional/core.entity_form_display.media.image.default.yml
+++ b/core/profiles/standard/config/optional/core.entity_form_display.media.image.default.yml
@@ -24,7 +24,7 @@ content:

@@ -24,7 +24,7 @@ content:
       preview_image_style: thumbnail
     third_party_settings: {  }
     type: image_image
-    weight: 26
+    weight: 0
     region: content
   name:

I think we probably also need to change them for the audio and video form displays now?

marcoscano’s picture

Status: Needs work » Needs review
FileSize
7.63 KB
1.55 KB

Not sure if it was strictly needed, because the name and source fields already had weights 0 and 1, but I changed them in any case, just to be consistent with the other types (which have weights -5 and 0, respectively).

Also, needed a reroll.

chr.fritsch’s picture

Status: Needs review » Reviewed & tested by the community

Lets do this.

xjm’s picture

  • xjm committed 27ff71a on 8.5.x
    Issue #2934162 by marcoscano, phenaproxima, chr.fritsch, robpowell,...
xjm’s picture

Status: Reviewed & tested by the community » Fixed

Committed and pushed to 8.5.x. Thanks!

Status: Fixed » Closed (fixed)

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