Problem/Motivation

In #2940029: Add an input filter to display embedded Media entities, we added the ability to embed media entities into WYSIWYG text editors and HTML content, using an input filter. By default, the media items are embedded by rendering them through their "full" view mode (although this can be overridden by changing a special data attribute in the raw HTML). The "full" view mode might not be entirely appropriate for an embedded representation of a media item, especially image media -- the image might be 10000x50000 pixels, which is way too big and compromises usability.

Proposed resolution

  • Modify the default display for Image media so that they use the "large" image style (which ships with the Image module) by default.
  • Ensure that this applies automatically to new image media types as well.
  • If an image media type is created programmatically, none of this magic will happen. It's only done when adding a new media type through the UI.
  • If for some some reason the 'large' image style has been deleted, don't use an image style, but do show a warning on the status report page that explains why it's a good idea to use an image style, and offer an action item so that privileged users can correct the problem.
  • Standard and Demo Umami profiles will ship with a media.image entity view display configured to use the 'large' image style.

Remaining tasks

Commit the patch!

User interface changes

None really.

API changes

None.

Data model changes

None.

Release notes snippet

By default, image media items from media fields and in the WYSIWYG editor will now use the ‘large’ image style that ships with the image module. Before this change, media images used the ‘Full’ display, which by default rendered the original image.

CommentFileSizeAuthor
#135 standard-default-image-view-mode-large-2.mov4.89 MBoknate
#135 standard.png590.91 KBoknate
#133 demo-umami-default-image-view-mode-large.mov6.6 MBoknate
#133 snake-river-image-embedded.png501.14 KBoknate
#131 3066802-interdiff--129-131.txt989 bytesoknate
#131 3066802-131.patch28.11 KBoknate
#130 3066802-130.patch2.28 KBoknate
#130 3066802-130--FAIL.patch897 bytesoknate
#129 3066802-interdiff--129--FAIL--to-129.txt1.41 KBoknate
#129 3066802-129.patch28.43 KBoknate
#129 3066802-129--FAIL.patch26.58 KBoknate
#128 3066802-interdiff--126-128.txt1.36 KBoknate
#128 3066802-128.patch27.79 KBoknate
#126 3066802-126.patch27.76 KBoknate
#126 3066802--interdiff-124-126.txt11.99 KBoknate
#124 3066802-interdiff--109-124.txt46.52 KBoknate
#124 3066802-124.patch20.82 KBoknate
#109 3066802-109.patch30.07 KBoknate
#109 3066802-interdiff--106-109.txt3.87 KBoknate
#107 3066802-interdiff--103-106.txt18.47 KBoknate
#107 3066802-interdiff--104-106.txt20.19 KBoknate
#107 3066802-106.patch30.07 KBoknate
#104 3066802-104.patch30.23 KBoknate
#104 3066802--interdiff-103-104.txt29.85 KBoknate
#103 3066802-103.patch28.81 KBoknate
#103 3066802--interdiff-101-103.txt640 bytesoknate
#102 3066802-102.png284.79 KBphenaproxima
#101 3066802-101.patch28.73 KBoknate
#101 3066802--interdiff-99-101.txt527 bytesoknate
#99 3066802-99.patch28.77 KBoknate
#99 3066802--interdiff-94-99.txt3.42 KBoknate
#99 3066802--interdiff-97-99.txt3.36 KBoknate
#97 3066802-97.patch28.74 KBoknate
#97 3066802--interdiff-94-97.txt1.21 KBoknate
#95 3066802-94.patch28.8 KBoknate
#95 3066802--interdiff-93-94.txt7.92 KBoknate
#93 3066802-93.patch28.58 KBoknate
#93 3066802-interdiff--91-93.txt492 bytesoknate
#91 3066802-90.patch28.62 KBoknate
#91 3066802-interdiff--88-90.txt504 bytesoknate
#89 3066802-88.patch28.6 KBoknate
#89 3066802--interdiff-86-88.txt15.15 KBoknate
#86 3066802-86.patch24.74 KBoknate
#86 3066802--interdiff-60-86.txt7.64 KBoknate
#86 3066802--interdiff-85-86.txt997 bytesoknate
#85 media-embed-no-image-style-warning.png282.46 KBoknate
#85 3066802-85.patch24.64 KBoknate
#85 3066802--interdiff-60-85.txt7.53 KBoknate
#60 3066802-60.patch22.23 KBoknate
#60 3066802--interdiff-54-60.txt7.47 KBoknate
#55 3066802--interdiff-48-54.txt9.09 KBoknate
#54 3066802-54.patch19.14 KBoknate
#54 3066802--interdiff-52-54.txt2.66 KBoknate
#53 3066802-53.patch18.97 KBoknate
#53 3066802--interdiff-48-53.txt8.19 KBoknate
#48 3066802-48.patch19.01 KBoknate
#48 3066802--interdiff-46-48.txt1.38 KBoknate
#46 3066802-46.patch19.06 KBoknate
#46 3066802--interdiff-44-46.txt6.88 KBoknate
#44 3066802--interdiff-43-44.txt784 bytesoknate
#44 3066802--interdiff-36-44.txt14.8 KBoknate
#44 3066802-44.patch18.79 KBoknate
#43 3066802-43.patch18.79 KBoknate
#43 3066802--interdiff-36-43.txt14.8 KBoknate
#43 3066802--interdiff-41-43.txt2.5 KBoknate
#41 3066802-41.patch19.27 KBoknate
#41 3066802--interdiff-36-41.txt14.67 KBoknate
#41 3066802--interdiff-40-41.txt8.99 KBoknate
#40 interdiff_36_40.txt6.05 KBanmolgoyal74
#40 3066802-40.patch21.48 KBanmolgoyal74
#38 interdiff_36_38.txt7.44 KBanmolgoyal74
#38 3066802-38.patch22.06 KBanmolgoyal74
#36 3066802-36.patch21.68 KBoknate
#36 3066802--interdiff-35-36.txt1.81 KBoknate
#36 3066802--interdiff-29-36.txt9.76 KBoknate
#35 3066802-35.patch21.64 KBoknate
#35 3066802--interdiff-29-35.txt9.64 KBoknate
#29 3066802-29.patch19.92 KBoknate
#29 3066802--interdiff-26-29.txt12.39 KBoknate
#26 3066802-26.patch19.05 KBoknate
#26 3066802--interdiff-25-26.txt3.53 KBoknate
#25 3066802-25.patch19.26 KBoknate
#25 3066802--interdiff-24-25.txt1.83 KBoknate
#24 3066802-24.patch19.24 KBoknate
#24 3066802--interdiff-23-24.txt4.92 KBoknate
#23 3066802-23.patch14.32 KBoknate
#23 3066802--interdiff-22-23.txt1.78 KBoknate
#22 3066802-22.patch14.25 KBoknate
#22 3066802--interdiff-18-22.txt7.91 KBoknate
#22 3066802--interdiff-21-22.txt1.19 KBoknate
#21 standard profile.png1.11 MBoknate
#21 umami test.png3.35 MBoknate
#21 3066802-21.patch14.21 KBoknate
#21 3066802--interdiff-18-21.txt7.93 KBoknate
#20 3066802-18.patch17.11 KBoknate
#20 3066802-interdiff--17-18.txt1.4 KBoknate
#20 confirmation message on creating new media type.png57.56 KBoknate
#19 media--embed-view-mode-480x480.png1.25 MBoknate
#17 3066802-interdiff--14-17.txt5.53 KBoknate
#17 3066802-17.patch16.19 KBoknate
#16 3066802-interdiff--14-16.txt5.53 KBoknate
#16 3066802-16.patch33.1 KBoknate
#14 3066802-14.patch11.59 KBoknate
#11 3066802-11.patch65.65 KBoknate
#11 3066802--interdiff-6-11.txt922 bytesoknate
#8 3066802-6.patch65.65 KBoknate
#8 3066802--interdiff-5-6.txt23.45 KBoknate
#7 3066802-5.patch75.11 KBoknate
#4 3066802-4.patch9.43 KBoknate
#3 3066802-3.patch5.03 KBoknate
#2 Screenshot 2019-08-13 at 12.22.27.png3.53 MBWim Leers
Support from Acquia helps fund testing for Drupal Acquia logo

Comments

phenaproxima created an issue. See original summary.

Wim Leers’s picture

Priority: Normal » Major
Issue tags: +Usability
FileSize
3.53 MB

Now that #2801307: [META] Support WYSIWYG embedding of media entities is mostly done, with #2994696: Render embedded media items in CKEditor and #2994699: Create a CKEditor plugin to select and embed a media item from the Media Library having landed, I think it's time we start pushing this forward at the same time, to improve usability and avoid a big DrupalWTF when people excitedly try this media embedding functionality in Drupal 8.8.

The WTF-aspect can easily be reproduced:

  1. Use your smartphone to take a photo.
  2. Go to /node/add/article.
  3. Click the button to embed media from the media library.
  4. Upload the photo you just took. It's probably pretty big. 8 megapixel? 15 megapixel?
  5. Click the "Insert selected" button.
  6. You've got embedded media! But … it takes up pretty much the entire CKEditor instance:

(Not to mention this is also what the end users get to see.)

oknate’s picture

Here's an initial patch that solves #2.

It installs the view mode with the media module, and adds optional configs for the media bundles installed by the standard profile.

I think maybe these optional configs should go into the standard profile. I wasn't sure. I borrowed them from media library and tweaked them ever so slightly, by using larger image styles. The fact that media library has them within the module, made me think we could include them either within the media library (because an embed button doesn't exist until you install media_library), but decided to put them in media, because if you install the standard profile and have some other contrib means of adding the drupal-media tag within content, it would be nice for it to work too.

@todo We'll need an update hook to install the configs on existing sites.

oknate’s picture

Oops, I forgot the code that switches the default view mode.

Wim Leers’s picture

Status: Active » Needs review
Issue tags: +Needs screenshots, +Needs update path

I think they can live in the media module to be honest. That's what the patch is currently doing, so 👍

A side benefit is that @Filter=media_embed can then also assume this exists!

Another side benefit would be that we can automatically install this config in an update hook, so existing sites can start using this without complex manual steps!

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

oknate’s picture

This patch adds a bunch of configs in order to get the DefaultConfigTest test to pass. These optional configs have a lot of dependencies. So this adds them, which fixes the test, but will force the install of these bundles on new installs.
The problem is, we don't want to force all of these to be installed. So either that test needs to change, or we need to move all of these optional configs to standard and demo_umami. I don't understand how media_library has similar configs without the dependencies and it passes the same test.

oknate’s picture

This patch drops the optional configs, and uses post update hooks to configure the displays. This was surprisingly easy, thanks to the Media Library module already having the code. I just had to copy it over, update some strings and remove extra code creating form displays.

The last submitted patch, 7: 3066802-5.patch, failed testing. View results

Status: Needs review » Needs work

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

oknate’s picture

Status: Needs work » Needs review
Issue tags: -Needs update path
FileSize
922 bytes
65.65 KB

Fixing dependency on the view mode I added. By the way, the reason I'm not using 'medium' or 'large' as I originally was, is because of this issue I encountered: #3030437: Remove dependency on 'medium' and 'thumbnail' image styles from media and media library modules.

oknate’s picture

oknate’s picture

Status: Needs review » Needs work

The patches are totally off. I think I forgot to merge in the latest changes to my working branch, and so there's a bunch of extraneous stuff in there.

oknate’s picture

Status: Needs work » Needs review
FileSize
11.59 KB

Rerolled the patch to remove extraneous changes.

oknate’s picture

Status: Needs review » Needs work

After discussing this with phenaproxima, I realized I made a mistake removing the configs in #8. The post update hook won't run on install, so unless configs are provided, they'll be missing on new installs.

oknate’s picture

Ignore this one, it has extraneous changes.

oknate’s picture

Adding back configs. Note, the image_style the patch is adding is 480x480. The same as the image module's default 'large' image style.

oknate’s picture

I'm noticing that media library has code that installs the display when a media type is created through the UI.

  // Configures media_library displays when a type is submitted.
  if ($form_object instanceof MediaTypeForm) {
    $form['actions']['submit']['#submit'][] = '_media_library_media_type_form_submit';
  }

and test coverage media_library/tests/src/Functional/MediaLibraryDisplayModeTest.php

Should we copy that over and do the same thing, so that a view display is set up for media types when added through the UI?

oknate’s picture

Here's a screenshot of the current patch's image style, 480x480, same as core 'large' image style. I think it's maybe too large.

Media Embed view mode

oknate’s picture

Addressing #18 (but needs test coverage). Here's the confirmation message when creating a new content type:
confirmation message

Maybe for the confirmation message, this is better: 'An embed view display has been created for the %type media type.'?

oknate’s picture

I realized it's a chicken and egg thing to try to include optional media view displays in the media option configs. You can't install those until the media module is installed. So removing once again. The media library can do it, but the media module can't. So moving the configs to standard and umami profiles.

I tested out some image sizes and for umami, I used their large style 'large_21_9'. For standard, I couldn't find one I liked, since I'm trying to accommodate 16:9 landscape images, so for now I'm using the 'media_embed' style included in the media module.

oknate’s picture

Fixing some dependency issues I noticed on the last patch.

Another thing to note. I dropped the configs for the bundles other than image. In the demo_umami profile, it seems to me that the default configuration for the other bundles are sufficient. They don't use an image field. The problem we're trying to solve here is that the default image bundle in demo_umami and the standard profile use a raw image:

content:
  field_media_image:
    label: visually_hidden
    settings:
      image_style: ''
      image_link: file
    third_party_settings: {  }
    type: image
    weight: 1
    region: content

So I think we really only need an entity_view_display config for that bundle in the those profiles.

For minimal installs and existing sites that are upgrading, there's the form submit hook that will add an entity_view_display using. the thumbnail and the view mode 'media_embed' we're adding.

oknate’s picture

Adding a dependency to the entity view display configs. The DemoUmamiProfileTest is still failing locally. I'm still trying to figure out how to fix it. But I wanted to post this update, since I think it's better.

oknate’s picture

Adding test coverage for #18 and #20, adding view display when media type added through the UI.

oknate’s picture

Status: Needs work » Needs review
FileSize
1.83 KB
19.26 KB

This fixes the demo umami test error.

oknate’s picture

A little cleanup.

oknate’s picture

Next steps:

  • We need opinions on the image styles selected in the entity view displays the patch adds.
  • We need to reach consensus on the out of the box entity view displays added in standard and demo umami. Right now I'm only creating them for the image media type. I think all the others don't need an image style, so they don't really need a default entity view display.
  • We need reviewers to assess the programmatic addition of configs and weigh in on whether we're doing just enough.
phenaproxima’s picture

Status: Needs review » Needs work

Nice work! This patch is looking great! It does need some changes (and some questions to be answered by committer-type folk), but overall this is a solid approach.

  1. +++ b/core/modules/media/media.module
    @@ -496,3 +499,73 @@ function media_filter_format_edit_form_validate($form, FormStateInterface $form_
    + * @throws \Drupal\Core\Entity\EntityStorageException
    

    We don't need the @throws, because this function never actually throws anything.

  2. +++ b/core/modules/media/media.module
    @@ -496,3 +499,73 @@ function media_filter_format_edit_form_validate($form, FormStateInterface $form_
    +  $values = [
    +    'targetEntityType' => 'media',
    +    'bundle' => $type->id(),
    +    'mode' => 'embed',
    +    'status' => TRUE,
    +  ];
    +  $display = EntityViewDisplay::create($values);
    

    Nit: $values does not need to be its own variable.

  3. +++ b/core/modules/media/media.module
    @@ -496,3 +499,73 @@ function media_filter_format_edit_form_validate($form, FormStateInterface $form_
    +  foreach (array_keys($display->getComponents()) as $name) {
    +    $display->removeComponent($name);
    +  }
    

    A quicker way to do this without looping: $display->set('content', []).

  4. +++ b/core/modules/media/media.module
    @@ -496,3 +499,73 @@ function media_filter_format_edit_form_validate($form, FormStateInterface $form_
    +  // Expose the thumbnail component with the default image style included with
    +  // the media module.
    +  $display->setComponent('thumbnail', [
    +    'type' => 'image',
    +    'label' => 'hidden',
    +    'settings' => [
    +      'image_style' => 'media_embed',
    +      'image_link' => '',
    +    ],
    +  ]);
    

    So...I'm not sure why we would want to expose the thumbnail in an embedded view mode. For a YouTube video, for example, we'd want to show the video itself (the iframe). What we should do here instead is take advantage of MediaSourceInterface::prepareViewDisplay().

    It makes sense for Media Library to configure the thumbnail, since that's what's normally displayed in the media library. But for an embed, we should use the defaults provided by prepareViewDisplay().

  5. +++ b/core/modules/media/media.module
    @@ -496,3 +499,73 @@ function media_filter_format_edit_form_validate($form, FormStateInterface $form_
    +  return (bool) $display->save();
    

    I know this was copied and pasted from Media Library, so this is not your fault at all. But looking at it with fresh eyes, this line makes no actual sense :)

    If $display->save() fails, it throws an exception. If it succeeds, it returns SAVED_NEW or SAVED_UDPATED, both of which cast to boolean TRUE. So in other words, this function will always return TRUE. Let's just remove this line here, and open a follow-up to fix it in Media Library.

  6. +++ b/core/modules/media/media.module
    @@ -496,3 +499,73 @@ function media_filter_format_edit_form_validate($form, FormStateInterface $form_
    +/**
    + * Implements hook_form_alter().
    + */
    +function media_form_alter(array &$form, FormStateInterface $form_state, $form_id) {
    

    I was going to suggest using hook_form_BASE_FORM_ID_alter() instead, but I see this is following what Media Library already does.

  7. +++ b/core/modules/media/media.module
    @@ -496,3 +499,73 @@ function media_filter_format_edit_form_validate($form, FormStateInterface $form_
    +    if (_media_configure_embed_view_display($type)) {
    +      \Drupal::messenger()->addStatus(t('Embed view display has been created for the %type media type.', [
    +        '%type' => $type->label(),
    +      ]));
    +    }
    

    Since the return value of _media_configure_embed_view_display() is useless, I would instead suggest this:

    try {
      _media_configure_embed_view_display($type);
     \Drupal::messenger()->addStatus(...);
    }
    catch (EntityStorageException $e) {
      \Drupal::messenger()->addError($e->getMessage());
      return;
    }
    

    Or, if we want to be harsher, we can just call the function and not bother catching the exception.

  8. +++ b/core/modules/media/media.post_update.php
    @@ -5,6 +5,80 @@
    +      'width' => 720,
    +      'height' => 405,
    

    How did we arrive at these dimensions?

  9. +++ b/core/modules/media/media.post_update.php
    @@ -5,6 +5,80 @@
    +  return t('The %label image style has been created successfully.', ['%label' => 'Media Embed']);
    

    Why not use ['%label' => $image_style->label()]?

  10. +++ b/core/modules/media/media.post_update.php
    @@ -5,6 +5,80 @@
    +  $values = [
    +    'id' => 'media.embed',
    +    'targetEntityType' => 'media',
    +    'label' => t('Embed'),
    +    'dependencies' => [
    +      'enforced' => [
    +        'module' => [
    +          'media',
    +        ],
    +      ],
    +      'module' => [
    +        'media',
    +      ],
    +    ],
    +  ];
    +  if (!EntityViewMode::load('media.embed')) {
    +    EntityViewMode::create($values)->save();
    +  }
    

    I think I'd like a release manager to validate this. Also, nit: $values doesn't need to be its own variable.

  11. +++ b/core/modules/media/media.post_update.php
    @@ -5,6 +5,80 @@
    +  // Embeds needs a special view display to make sure the out-of-the-box
    +  // display doesn't use very large images. It was not automatically
    +  // created for custom media types, so let's make sure this is fixed.
    +  $types = [];
    +  foreach (MediaType::loadMultiple() as $type) {
    +    $view_display_created = _media_configure_embed_view_display($type);
    +    if ($view_display_created) {
    +      $types[] = $type->label();
    +    }
    +  }
    +  if ($types) {
    +    return t('The Embed view display has been created for the following media types: @types.', [
    +      '@types' => implode(', ', $types),
    +    ]);
    +  }
    

    So, here's the thing -- most media types don't necessarily need a specialized embed display. The only ones which really do, at least for now, are the ones that use images as their source field. I'm wondering if, to simplify things, we should create the view displays selectively -- i.e., only do this for image media types. I'd like to hear a product manager's opinion on this idea before we proceed, though.

  12. +++ b/core/modules/media/tests/src/Functional/MediaEmbedDisplayModeTest.php
    @@ -0,0 +1,139 @@
    +  protected static $modules = [
    +    'field_ui',
    +    'media',
    +    'system',
    +  ];
    

    system is installed anyway; I don't think it needs to be explicitly listed in $modules.

oknate’s picture

Status: Needs work » Needs review
FileSize
12.39 KB
19.92 KB

Addressing #28:
1. ✅ Removed the throws statement in the comment.
2. ✅ Added chaining.
3. ✅ replaced loop with $display->set('content', []).
4. ✅ Changed the displays to use ::prepareViewDisplay(). That’s a handy method! I also updated the tests since they were testing for the thumbnail.
5. 👎Removed the return statement, and added a follow-up for media library: https://www.drupal.org/project/drupal/issues/3077822
But, later on, I added it back. We need a way to determine if the view display was updated, so that we can only display the success message when it was updated. There are two cases where it would not throw an error, but wouldn’t update the display. If the entity view display is empty (such as if they deleted it), or if the view mode is empty (if they deleted it). The second case is actually not handled in the Media Library module. I added an issue for it. #3077932: Prevent deletion of the Media library view mode
6. As you point out, this follows the model established in media_library. Also we don’t want to lock this into one form. In the off-chance that someone extends that form and it has a different form id.
7. Added try catch statement.
8. Those dimensions are just placeholders added by me. I just looked at a recent project I had worked on to get a general idea and I added similar dimensions. My personal preference is that we use a landscape aspect ratio so that landscape images aren’t really small. The possibilities here are endless.
9. Changed to $image_style->label(), per your recommendation. I could see the argument against this change, though. We’re hard-coding it a few lines above.

$image_style = ImageStyle::create([
  'name' => 'media_embed',
  'label' => 'Media Embed',
]);

So what's the point in this context?
10. ✅ Removed the variable $values.
12. Removed the dependency to ‘system’.

phenaproxima’s picture

Status: Needs review » Needs work

Thanks for the updates! This is shaping up.

  1. +++ b/core/modules/media/config/install/core.entity_view_mode.media.embed.yml
    @@ -0,0 +1,12 @@
    +langcode: en
    +status: true
    +dependencies:
    +  enforced:
    +    module:
    +      - media
    +  module:
    +    - media
    +id: media.embed
    +label: 'Embed'
    +targetEntityType: media
    +cache: true
    

    So this has cache: true (which is the default value)...yet we explicitly disable caching when rendering embedded media, instead relying on the caching of the host entity. I wonder if we should have cache: false in this view mode, for this reason. I'd like Wim to confirm or deny this idea.

  2. +++ b/core/modules/media/media.module
    @@ -496,3 +501,76 @@ function media_filter_format_edit_form_validate($form, FormStateInterface $form_
    + * @return bool
    + *   Returns true if the EntityViewDisplay display was configured.
    

    This return description should be rephrased. How about: "Returns TRUE if the entity view display was configured correctly, FALSE otherwise."

  3. +++ b/core/modules/media/media.module
    @@ -496,3 +501,76 @@ function media_filter_format_edit_form_validate($form, FormStateInterface $form_
    +  // If a site builder has deleted the 'Embed' view mode, do not
    +  // configure the display.
    +  $view_mode = EntityViewMode::load('media.embed');
    +  if (!$view_mode) {
    +    return FALSE;
    +  }
    

    Nice. This is a good use of the boolean return! :) I agree with your justification.

  4. +++ b/core/modules/media/media.module
    @@ -496,3 +501,76 @@ function media_filter_format_edit_form_validate($form, FormStateInterface $form_
    +  $display = EntityViewDisplay::load('media.' . $type->id() . '.embed');
    +
    +  if ($display) {
    +    return FALSE;
    +  }
    

    A comment over this bit would be useful. "If the embedded entity view display has already been configured, we don't need to redo it."

  5. +++ b/core/modules/media/media.module
    @@ -496,3 +501,76 @@ function media_filter_format_edit_form_validate($form, FormStateInterface $form_
    +  foreach ($display->getComponents() as $name => $config) {
    +    if (!empty($config['type']) && $config['type'] == 'image' && $config['settings']['image_style'] === '') {
    +      $config['settings']['image_style'] = 'media_embed';
    +      $display->setComponent($name, $config);
    +    }
    +  }
    

    This will use our image style for every image-formatted component in the display. I think that's a bit overreaching. Instead, we should check if the source field is an ImageItem (or a subclass thereof), and if it is, we should configure only the source field to use our special image style (assuming it's using the image formatter, of course, and has no image style already applied). So that will be a bit more verbose, but I think it will also be less "invasive". Something like this:

    $source_field_definition = $type->getSource()->getSourceFieldDefinition($type);
    
    if (is_a(ImageItem::class, $source_field_definition->getItemDefinition()->getClass(), TRUE)) {
      $component = $display->getComponent($source_field_definition->getName());
      if ($component && $component['type'] === 'image' && $component['settings']['image_style'] === '') {
      // ...
      }
    }
    
catch’s picture

+++ b/core/modules/media/media.post_update.php
@@ -5,6 +5,79 @@
+}
+
+/**
+ * Create and configure Embed view display for media types.
+ */
+function media_post_update_display_modes() {
+  // Ensure the custom view modes are created.

This update depends on the image style, but post updates aren't guaranteed to run in any particular order, so we should merge the two updates into one.

webchick’s picture

@effulgentsia did some great research around the existing Image module, YouTube embeds, etc. and came to the conclusion that the best thing to do here is to have the view mode's image style default to "Large" (which we should be able to do because it's provided by Image module, which is a dependency of Media module). This will create the best consistency with other fields on the site, and ensure that if a site builder adjusts this size to be different, Media stays consistent.

webchick’s picture

Additionally, per the bug exposed on the UX call, this should change the image from "Link to full-sized image" (or whatever it's called) to "No link" so that links to things can be added to images from CKEditor.

oknate’s picture

Status: Needs work » Needs review
Issue tags: -Needs screenshots
FileSize
9.64 KB
21.64 KB

Addressing #30:
1. Leaving this for Wim to confirm or deny. I suppose someone could use the ‘embed’ view mode elsewhere and then, you’d want it to cache in that circumstance. But also, the way the embeds render, I think this setting is irrelevant.
2. ✅ Replaced with your verbiage, thanks.
5. ✅ Removed the loop and used your code that identifies the source field and only updates that one. This is good in the off-chance that $type->getSource()->prepareViewDisplay() returns more than just the source component.

Addressing #31:

We're dropping the new image style, so this is no longer relevant, but thank for pointing that out. I didn't know that. I thought they ran alphabetically.

Addressing #33:

Removed the new image_style and replaced it with large.

large 480x480

That means this screenshot is relevant again, and we can remove the "needs screenshots" tag.

oknate’s picture

Addressing #34:

Removing the link to the file. I eyeballed this one as I was running out of time to work on it. I will go back and test it later today.

phenaproxima’s picture

Status: Needs review » Needs work

I'm glad we were able to agree on a scope for this patch. This is cleaner, better, and constantly improving.

  1. +++ b/core/modules/media/media.module
    @@ -496,3 +502,84 @@ function media_filter_format_edit_form_validate($form, FormStateInterface $form_
    +  $display = EntityViewDisplay::load('media.' . $type->id() . '.embed');
    +
    +  // If the embedded entity view display has already been configured, we don't
    +  // need to redo it.
    +  if ($display) {
    +    return FALSE;
    +  }
    

    We should use \Drupal::service('entity_display.repository')->getViewDisplay('media', $type->id(), 'embed') here, rather than calling static methods of EntityViewDisplay. It will create the display (without saving) if it doesn't exist. We can return FALSE if $display->isNew().

  2. +++ b/core/modules/media/media.module
    @@ -496,3 +502,84 @@ function media_filter_format_edit_form_validate($form, FormStateInterface $form_
    +  // Set the image style to the default included in the the media module.
    +  if (is_a(ImageItem::class, $source_field_definition->getItemDefinition()->getClass(), TRUE)) {
    

    Let's change the comment a bit. I would suggest "If this media type is using an image field as its source field, set the image style to the 'large' image style included with the Image module."

  3. +++ b/core/modules/media/media.module
    @@ -496,3 +502,84 @@ function media_filter_format_edit_form_validate($form, FormStateInterface $form_
    +      $config['settings']['image_style'] = 'large';
    +      $config['settings']['image_link'] = '';
    +      $display->setComponent($source_field_name, $config);
    

    What's $config? This should be:

    $component['settings']['image_style'] = 'large';
    $component['settings']['image_link'] = '';
    $display->setComponent($source_field_name, $component);
    

    Also, we should be returning early from this function if the 'large' image style doesn't exist.

  4. +++ b/core/modules/media/media.post_update.php
    @@ -5,6 +5,50 @@
    +use Drupal\image\Entity\ImageStyle;
    

    This is no longer needed.

  5. +++ b/core/modules/media/media.post_update.php
    @@ -5,6 +5,50 @@
    +      'dependencies' => [
    +        'enforced' => [
    +          'module' => [
    +            'media',
    +          ],
    +        ],
    +        'module' => [
    +          'media',
    +        ],
    +      ],
    

    We don't need to set the 'module' dependencies manually. The fact that it's an enforced dependency makes that redundant. So this should be:

    'dependencies' => [
      'enforced' => [
        'module' => ['media'],
      ],
    ],
    
  6. +++ b/core/modules/media/media.post_update.php
    @@ -5,6 +5,50 @@
    +    $view_display_created = _media_configure_embed_view_display($type);
    +    if ($view_display_created) {
    +      $types[] = $type->label();
    +    }
    

    Nit: There's no need for $view_display_created to be its own variable.

  7. +++ b/core/modules/media/tests/src/Functional/MediaEmbedDisplayModeTest.php
    @@ -0,0 +1,129 @@
    +    // Create a non-image media type through the UI.
    +    $athos = 'athos';
    +    $edit = [
    +      'label' => 'Athos',
    +      'id' => $athos,
    +      'source' => 'file',
    +    ];
    +    $this->drupalPostForm('admin/structure/media/add', $edit, 'Save');
    +    $this->drupalPostForm(NULL, [], t('Save'));
    

    Why do we need to do drupalPostForm() twice? That shouldn't be necessary...

  8. +++ b/core/modules/media/tests/src/Functional/MediaEmbedDisplayModeTest.php
    @@ -0,0 +1,129 @@
    +    $this->assertSession()->pageTextContains("The Embed view display has been created for the Athos media type.");
    

    Nit: Let's call $assert_session = $this->assertSession() at the top of the method, and reuse that throughout. I'd like Media to standardize on one style of asserting things.

  9. +++ b/core/modules/media/tests/src/Functional/MediaEmbedDisplayModeTest.php
    @@ -0,0 +1,129 @@
    +    $dartagnan = 'dartagnan';
    

    This isn't really needed.

  10. +++ b/core/modules/media/tests/src/Functional/MediaEmbedDisplayModeTest.php
    @@ -0,0 +1,129 @@
    +    $this->assertSession()->pageTextNotContains("The Embed view display has been created for the $dartagnan media type.");
    

    This is a false assertion. It should be asserting the absence of the text "The Embed view display has been created for the d'Artagnan media type." (It should not have the label of the media type, not its ID.) This is yet another reason why we shouldn't be keeping the media type ID in its own variable. :)

  11. +++ b/core/modules/media/tests/src/Functional/MediaEmbedDisplayModeTest.php
    @@ -0,0 +1,129 @@
    +   * Assumes the source type has a source field.
    

    There is no need for this line. All media types are required to have a source field.

  12. +++ b/core/modules/media/tests/src/Functional/MediaEmbedDisplayModeTest.php
    @@ -0,0 +1,129 @@
    +    if ($type_id == 'image') {
    

    Except in certain unusual cases, we should use === everywhere.

  13. +++ b/core/modules/media/tests/src/Functional/MediaEmbedDisplayModeTest.php
    @@ -0,0 +1,129 @@
    +      $this->assertNotEmpty($field);
    

    This can be $this->assertInternalType('array', $field).

  14. +++ b/core/modules/media/tests/src/Functional/MediaEmbedDisplayModeTest.php
    @@ -0,0 +1,129 @@
    +      $this->assertSame('large', $field['settings']['image_style']);
    

    We should also assert that the image_link setting is empty.

  15. +++ b/core/modules/media/tests/src/Functional/MediaEmbedDisplayModeTest.php
    @@ -0,0 +1,129 @@
    +      $source_field = $type->getSource()->getSourceFieldDefinition($type);
    +      $field = $display->getComponent($source_field->getName());
    +      $this->assertNotEmpty($field);
    

    This can be a little cleaner, IMHO, in this form:

    $component = $display->getComponent($type->getSource()->getSourceFieldDefinition($type)->getName());
    $this->assertInternalType('array', $component);
    

    Overall, this method should assert the view display exists and has an array component for the source field. Then, if the type's source plugin is 'image', it should also assert that the component has the correct image_style and image_link configuration. So generally I think we should refactor this whole method in that direction.

  16. +++ b/core/modules/media/tests/src/Functional/Update/MediaUpdateTest.php
    @@ -114,4 +116,19 @@ public function testEnableStandaloneUrl() {
    +    $this->assertNull(ImageStyle::load('embed'));
    

    This, and its related use is not needed.

  17. +++ b/core/modules/media/tests/src/Functional/Update/MediaUpdateTest.php
    @@ -114,4 +116,19 @@ public function testEnableStandaloneUrl() {
    +    $this->assertNull(EntityViewDisplay::load('media.file.embed'));
    +    $this->assertNull(EntityViewDisplay::load('media.image.embed'));
    

    Aren't there more media types for us to assert?

  18. +++ b/core/profiles/standard/config/optional/core.entity_view_display.media.image.embed.yml
    @@ -0,0 +1,30 @@
    +langcode: en
    +status: true
    +dependencies:
    +  config:
    +    - core.entity_view_mode.media.embed
    +    - field.field.media.image.field_media_image
    +    - image.style.large
    +    - media.type.image
    +  module:
    +    - image
    +id: media.image.embed
    +targetEntityType: media
    +bundle: image
    +mode: embed
    +content:
    +  field_media_image:
    +    label: visually_hidden
    +    settings:
    +      image_style: large
    +      image_link: ''
    +    third_party_settings: {  }
    +    type: image
    +    weight: 1
    +    region: content
    +hidden:
    +  created: true
    +  langcode: true
    +  name: true
    +  thumbnail: true
    +  uid: true
     
    

    I think there's an extra blank line at the end of this file. (There should only be one.)

anmolgoyal74’s picture

Status: Needs work » Needs review
FileSize
22.06 KB
7.44 KB

Status: Needs review » Needs work

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

anmolgoyal74’s picture

Status: Needs work » Needs review
FileSize
21.48 KB
6.05 KB

Updated with most of the suggestions.

oknate’s picture

Addressing #37 and (validating changes in #38 and #40):
1. ✅ Updated to if !$display->isNew(), and using entity display repository.
2. ✅ Updated in #38.
3. ✅ Updated in #38.
4. ✅ Updated in #38.
5. ✅ Updated in #38.
6. ✅ Updated in #38.
7. The two post forms were because functional tests can’t do Ajax, so that was a work-around (In the original test this was copied from in media library.) Based on the conversation we had on Slack, I’m switching the test to a functional javascript test in this patch, so it doesn’t have this wonkiness.
8. ✅ Updated in #38.
9. ✅ dropped the variable.
10. Updated to use the label in the assertion.
11. ✅ Updated in #38.
12. ✅ Updated in #38
13. Changed to $this->assertInternalType('array', $field);
14. ✅ Updated in #38
15. ✅ Updated.
16. ✅ Updated.
17. ✅ Additional types added in #38
18. ✅ Updated in #38

Status: Needs review » Needs work

The last submitted patch, 41: 3066802-41.patch, failed testing. View results

oknate’s picture

Status: Needs work » Needs review
FileSize
2.5 KB
14.8 KB
18.79 KB

I didn't test the update test when working on the last patch. I had the version in #38 which failed, oops. Now that I tested it, I can see the only types are File and Image in this context:

The Embed view display has been created for the following media types: File, Image.

I could have just removed the extras, as #40 does, but decided to abstract it, so that if these dump files change and if more media types are added in an updated dump file, it would still work.

The test starts with these:

$this->databaseDumpFiles = [
      __DIR__ . '/../../../../../system/tests/fixtures/update/drupal-8.4.0.bare.standard.php.gz',
      __DIR__ . '/../../../fixtures/update/drupal-8.4.0-media_installed.php',
      __DIR__ . '/../../../fixtures/update/drupal-8.media-add-additional-permissions.php',
    ];
oknate’s picture

phenaproxima’s picture

Status: Needs review » Needs work

Almost there -- unfortunately, there's a big flaw in the test. :( But I don't think it'll take much effort to fix.

  1. +++ b/core/modules/media/tests/src/FunctionalJavascript/MediaEmbedDisplayModeTest.php
    @@ -0,0 +1,127 @@
    +    // Wait for form to complete with ajax.
    

    Should be 'AJAX'.

  2. +++ b/core/modules/media/tests/src/FunctionalJavascript/MediaEmbedDisplayModeTest.php
    @@ -0,0 +1,127 @@
    +    if ($type_id === 'image') {
    

    This is wrong. We are passing media type IDs into this method, and none of them are 'image'. We really should be loading the media type, getting the source field definition, and only asserting things if it's an image field. So, I think this method should look like this:

    $type = MediaType::load($type_id);
    $display = EntityViewDisplay::load(...);
    $this->assertInstanceOf(EntityViewDisplay::class, $display);
    $source_field_definition = $type->getSource()->getSourceFieldDefinition($type);
    $component = $display->getComponent($source_field_definition->getName());
    $this->assertInternalType('array', $component);
    
    if ($source_field_definition->getType() === 'image') {
      // Assert the image style and linking
    }
    <code>
    </li>
    
    <li>
    <code>
    +++ b/core/modules/media/tests/src/FunctionalJavascript/MediaEmbedDisplayModeTest.php
    @@ -0,0 +1,127 @@
    +      $this->assertNotEmpty($field['settings']['image_link']);
    

    This should be assertEmpty(). We want to prove that the image is not linked in an embed.

Then, I noticed an architectural thing:

+++ b/core/modules/media/media.module
@@ -496,3 +502,85 @@ function media_filter_format_edit_form_validate($form, FormStateInterface $form_
+  if (is_a(ImageItem::class, $source_field_definition->getItemDefinition()->getClass(), TRUE)) {
+    $source_field_name = $source_field_definition->getName();
+    $component = $display->getComponent($source_field_name);
+    if ($component && $component['type'] === 'image' && $component['settings']['image_style'] === '') {
+      $component['settings']['image_style'] = 'large';
+      $component['settings']['image_link'] = '';
+      $display->setComponent($source_field_name, $component);
+    }
+  }

Looking at this more closely, I think we might be violating encapsulation here. This kind of logic is exactly what MediaSourceInterface::prepareViewDisplay() was intended to do, and so IMHO the Image source plugin should be responsible for doing this in its prepareViewDisplay() method. I think that will be much cleaner.

So, I suggest that we remove this block, and modify \Drupal\media\Plugin\media\Source\Image::prepareViewDisplay() -- we'll need to override the method -- like so:

public function prepareViewDisplay(MediaTypeInterface $type, EntityViewDisplayInterface $display) {
  parent::prepareViewDisplay($type, $display);

  if ($display->getMode() === 'embed') {
    $field_name = $this->getSourceFieldDefinition($type)->getName();
    $component = $display->getComponent($field_name);
    $component['settings']['image_style'] = 'large';
    $component['settings']['image_link'] = '';
    $display->setComponent($field_name, $component);
  }
}
oknate’s picture

Status: Needs work » Needs review
FileSize
6.88 KB
19.06 KB

Addressing #45:
1. ✅Changed to uppercase 'AJAX'.
2. ✅Oops, I obviously got confused when I was refactoring that. You're right, I was conflating media bundles and media source plugins. Updated per your recommendation.
3. ✅Changed $this->assertNotEmpty($component['settings']['image_link']); to $this->assertEmpty($component['settings']['image_link']); D'oh!
4. ✅Moved the code configuring the display into prepareViewDisplay for the image source plugin. Brilliant! That is much more appropriate.

phenaproxima’s picture

Oh, this looks gooood...

  1. +++ b/core/modules/media/media.module
    @@ -521,34 +519,22 @@ function _media_configure_embed_view_display(MediaTypeInterface $type) {
    +  // If this media type is using an image field as its source field, this will
    +  // set the image style to the 'large' image style included with the Image
    +  // module.
    

    I would suggest moving this comment to Image::prepareViewDisplay() and rephrasing it slightly. Something like: For the `embed` display mode, use the `large` image style and do not link the image to anything.

  2. +++ b/core/modules/media/src/Plugin/media/Source/Image.php
    @@ -161,4 +162,18 @@ public function createSourceField(MediaTypeInterface $type) {
    +    parent::prepareViewDisplay($type, $display);
    +    if ($display->getMode() === 'embed') {
    

    Can we get a blank line after the parent::prepareViewDisplay() call, for readability?

oknate’s picture

Addressing #47.
1. ✅Done.
2. ✅Done.

phenaproxima’s picture

Thank you!

+++ b/core/modules/media/src/Plugin/media/Source/Image.php
@@ -167,6 +167,9 @@ public function createSourceField(MediaTypeInterface $type) {
+    // For the `embed` display mode, use the `large` image style and do not
+    //link the image to anything.

Second line of the comment needs a space after the //.

Also, I just realized this comment doesn't explain why we're doing this. So let's add some more wording. I suggest: "This is done in order to ensure the image does not become too unwieldy when embedded into formatted text, and to allow it to wrapped by an author-created link."

phenaproxima’s picture

Status: Needs review » Needs work
  1. +++ b/core/modules/media/media.post_update.php
    @@ -5,6 +5,43 @@
    +  // Embeds needs a special view display to make sure the out-of-the-box
    

    Should be "Embeds need..."

  2. +++ b/core/modules/media/media.post_update.php
    @@ -5,6 +5,43 @@
    +  // display doesn't use very large images. It was not automatically
    +  // created for custom media types, so let's make sure this is fixed.
    

    This is a bit inaccurate. The display is automatically created for custom media types, it just needs to be re-created for existing media types. Maybe we should change that sentence to "Ensure that all existing media types have an embed display."

  3. +++ b/core/modules/media/tests/src/Kernel/MediaEmbedFilterTest.php
    --- /dev/null
    +++ b/core/profiles/demo_umami/config/install/core.entity_view_display.media.image.embed.yml
    

    We should probably update DemoUmamiProfileTest to ensure this display exists and looks the way we expect.

  4. +++ b/core/profiles/demo_umami/config/install/core.entity_view_display.media.image.embed.yml
    --- /dev/null
    +++ b/core/profiles/standard/config/optional/core.entity_view_display.media.image.embed.yml
    

    Same for StandardTest.

Wim Leers’s picture

  1. #30.1: See \Drupal\Core\Entity\EntityDisplayModeBase::$cache, which says Whether or not the rendered output of this view mode is cached by default.. I agree we can set this to false. Well-spotted! 👏👍
  2. #30.4: I'd say it's less about avoiding redoing it, more about avoiding overriding it.
  3. #30.5: I'm … surprised that we're even adding a new image style. I would have avoided that. It looks like @oknate decided this in #11 because of the pre-existing #3030437: Remove dependency on 'medium' and 'thumbnail' image styles from media and media library modules. I'd never heard of that issue before. But I'm not sure I agree with it. Actually, I don't even understand it yet! I asked for clarifications in #3030437-3: Remove dependency on 'medium' and 'thumbnail' image styles from media and media library modules.
  4. #33 and #34 sound great! (And #33 relates to the previous bullet!)
  5. +++ b/core/modules/media/media.module
    @@ -496,3 +500,70 @@ function media_filter_format_edit_form_validate($form, FormStateInterface $form_
    +  // If a site builder has deleted the 'Embed' view mode, do not
    +  // configure the display.
    

    Nit: 80 cols.

  6. +++ b/core/modules/media/media.module
    @@ -496,3 +500,70 @@ function media_filter_format_edit_form_validate($form, FormStateInterface $form_
    +  // This will create an entity view display if one doesn't exist.
    

    Nit: This is a pointless comment IMHO.

  7. +++ b/core/modules/media/media.module
    @@ -496,3 +500,70 @@ function media_filter_format_edit_form_validate($form, FormStateInterface $form_
    +function media_form_alter(array &$form, FormStateInterface $form_state, $form_id) {
    ...
    +  // Configures media_embed displays when a type is submitted.
    +  if ($form_object instanceof MediaTypeForm) {
    +    $form['actions']['submit']['#submit'][] = '_media_type_form_submit';
    +  }
    

    🤔 👍 This really should not be happening using form alters, but using entity hooks, so that this also works when creating media types via either the Entity PHP API or via the JSON:API HTTP API or via the REST HTTP API or via GraphQL. But … this is following the pre-existing pattern from media_library_form_alter() where we did try to do that and it turned out to be impossible. (Not to mention you can't create config entities via REST/JSON:API/GraphQL yet anyway.)

    So +1.

  8. +++ b/core/modules/media/media.post_update.php
    @@ -5,6 +5,43 @@
    + * Create and configure Embed view display for media types.
    ...
    +function media_post_update_display_modes() {
    

    🤔

    Nit: s/display/displays/

    Also: it's odd to be talking about entity view displays and then see entity view display modes in the function name.

  9. +++ b/core/modules/media/media.post_update.php
    @@ -5,6 +5,43 @@
    +  // Ensure the custom view modes are created.
    +  if (!EntityViewMode::load('media.embed')) {
    

    This is plural, but we create only one view mode.

  10. +++ b/core/modules/media/media.post_update.php
    @@ -5,6 +5,43 @@
    +  // Embeds needs a special view display to make sure the out-of-the-box
    +  // display doesn't use very large images. It was not automatically
    

    🤔 This text is out of place here?

  11. +++ b/core/modules/media/media.post_update.php
    @@ -5,6 +5,43 @@
    +  // created for custom media types, so let's make sure this is fixed.
    

    "let's make sure this is fixed" -> this sounds really odd.

  12. +++ b/core/modules/media/src/Plugin/Filter/MediaEmbed.php
    @@ -29,7 +29,7 @@
    - *     "default_view_mode" = "full",
    + *     "default_view_mode" = "embed",
    

    🥳🥳🥳🥳

  13. +++ b/core/modules/media/tests/src/Functional/Update/MediaUpdateTest.php
    @@ -114,4 +115,28 @@ public function testEnableStandaloneUrl() {
    +    $labels = array_map(function ($type) {
    +      return $type->label();
    +    }, $types);
    +    $expected_labels = implode(', ', $labels);
    +    $this->assertSession()->pageTextContains('The Embed view display has been created for the following media types: ' . $expected_labels);
    

    🤔 Nit: I think this is needlessly complex. I don't think other update tests are asserting the message?

  14. +++ b/core/modules/media/tests/src/FunctionalJavascript/MediaEmbedDisplayModeTest.php
    @@ -0,0 +1,124 @@
    +   * Tests that media can automatically configure display modes.
    ...
    +  public function testViewMode() {
    

    🤔 Function name and description do not match.

  15. +++ b/core/modules/media/tests/src/FunctionalJavascript/MediaEmbedDisplayModeTest.php
    @@ -0,0 +1,124 @@
    +    EntityViewDisplay::load("media.porthos.embed")->delete();
    +    // Make sure the view display is not created when saving existing
    +    // media types.
    

    🤔 This comment seems wrong? We're deleting things, not "saving an existing media type"? It feels like I'm missing something.

  16. +++ b/core/modules/media/tests/src/FunctionalJavascript/MediaEmbedDisplayModeTest.php
    @@ -0,0 +1,124 @@
    +    // If for some reason a site builder deletes the embed view mode,
    +    // an entity view display should not be configured when creating a media
    

    Nit: 80 cols.

  17. +++ b/core/modules/media/tests/src/Kernel/MediaEmbedFilterTest.php
    @@ -58,24 +58,24 @@ public function providerTestBasics() {
    -      'data-entity-uuid + data-view-mode=full ⇒ specified view mode used' => [
    +      'data-entity-uuid + data-view-mode=embed ⇒ specified view mode used' => [
    

    Rather than replacing this test case, we should keep it, and add a new one. This is specifically testing that it's possible to specify a particular view mode after all.

oknate’s picture

Status: Needs work » Needs review

moving these notes to the next comment.

oknate’s picture

Addressing #51
5. ? I’m not sure what “80 cols.” means. I can see I can squeeze a few words into 80 characters, so I did that.
6. ✅Dropped the comment, per your recommendation.
7. Thanks for explaining that.
8. ✅ Changed to media_post_update_entity_view_displays().
9. ✅ Removing this comment, the code is self-explanatory.
10. ✅ Moved that text to the docblock for _media_configure_embed_view_display, and updated it to: “Embeds needs a special view display to make sure that the out-of-the-box configuration doesn't output very large raw images.”
11. “let's make sure this is fixed.” is in HEAD and it would be out of scope to change this here, right? That’s part of the media library’s post update docblock.
12. The comment didn’t come through, I just see four boxes: 🥳🥳🥳🥳. Can you clarify?
13. This was adapted from MediaLibraryUpdateFormAndViewDisplaysTest:

$this->assertSession()->pageTextContains('Media Library form and view displays have been created for the following media types: File, Image.’);

But then in #37.13, phenaproxima asked if there aren’t other bundles to test.

In #38 it was erroneous updated to:

diff -u b/core/modules/media/tests/src/Functional/Update/MediaUpdateTest.php b/core/modules/media/tests/src/Functional/Update/MediaUpdateTest.php
--- b/core/modules/media/tests/src/Functional/Update/MediaUpdateTest.php
+++ b/core/modules/media/tests/src/Functional/Update/MediaUpdateTest.php
@@ -4,7 +4,6 @@
 
 use Drupal\Core\Entity\Entity\EntityViewDisplay;
 use Drupal\FunctionalTests\Update\UpdatePathTestBase;
-use Drupal\image\Entity\ImageStyle;
 use Drupal\media\Entity\Media;
 use Drupal\Tests\media\Traits\MediaTypeCreationTrait;
 use Drupal\user\Entity\Role;
@@ -122,12 +121,19 @@
    * @see media_post_update_display_modes()
    */
   public function testPostUpdateDisplayModes() {
-    $this->assertNull(ImageStyle::load('embed'));
     $this->assertNull(EntityViewDisplay::load('media.file.embed'));
     $this->assertNull(EntityViewDisplay::load('media.image.embed'));
+    $this->assertNull(EntityViewDisplay::load('media.audio.embed'));
+    $this->assertNull(EntityViewDisplay::load('media.document.embed'));
+    $this->assertNull(EntityViewDisplay::load('media.remote_video.embed'));
+    $this->assertNull(EntityViewDisplay::load('media.video.embed'));
     $this->runUpdates();
     $this->assertInstanceOf(EntityViewDisplay::class, EntityViewDisplay::load('media.file.embed'));
     $this->assertInstanceOf(EntityViewDisplay::class, EntityViewDisplay::load('media.image.embed'));
+    $this->assertInstanceOf(EntityViewDisplay::class, EntityViewDisplay::load('media.audio.embed'));
+    $this->assertInstanceOf(EntityViewDisplay::class, EntityViewDisplay::load('media.document.embed'));
+    $this->assertInstanceOf(EntityViewDisplay::class, EntityViewDisplay::load('media.remote_video.embed'));
+    $this->assertInstanceOf(EntityViewDisplay::class, EntityViewDisplay::load('media.video.embed'));
     $this->assertSession()->pageTextContains('The Embed view display has been created for the following media types: File, Image.');
   }

Then I decided to fix it and abstract it with the code you think is too complex. So I’m going to set it back to where it was in #36.

14. ✅ changed to testEmbedEntityViewDisplays().
15. ✅ Updated the comment to be more clear: “Test that the entity view display is not regenerated when saving existing media types after the entity view display has been deleted. We only want to create it on the `add` form, not the `edit` form.”
16. ✅ Moved one word up a line.
17. Restored the full view mode data set.

I'm still working on #50, which asks for new test coverage.

oknate’s picture

A few more small updates.
#49. Adding space, and added additional explanation.
#50
1. ✅ Fixed the grammar, nice catch.
2. ✅ Changed the function comment to your suggested text: “Ensure that all existing media types have an embed display.”.
3, 4. Still @todo: I will work on this tonight.

oknate’s picture

Adding another interdiff.

oknate’s picture

DemoUmamiProfileTest::assertDefaultConfig and DemoUmamiProfileTest::testConfig already assures the config is installed in that profile. What were you thinking of as far as making sure it looks OK? I mean it's just using the large image style. What do we need to test?

Wim Leers’s picture

  • #53.5: that's exactly what it meant :) 👍
  • #53.11: Ah, this copy/pasted weird comments from media_library_post_update_display_modes()… never mind then!
  • #53.12: That was me cheering for this change :D
phenaproxima’s picture

DemoUmamiProfileTest::assertDefaultConfig and DemoUmamiProfileTest::testConfig already assures the config is installed in that profile. What were you thinking of as far as making sure it looks OK? I mean it's just using the large image style. What do we need to test?

I was wrong about DemoUmamiProfileTest; we can leave that one alone. StandardTest, though, has a testStandard() method which, among other things, does some testing of the media types bundled with Standard. So, my recommendation would be to assert that the Image media type has an "embed" view display enabled, and that is uses the Large image style and no link; for every other media type, we should assert that there is no embed display (i.e., when on the "Manage display" page, under "Custom settings", the "Embed" checkbox should exist and not be checked).

seanB’s picture

Some minor questions and feedback:

  1. +++ b/core/modules/media/media.module
    @@ -496,3 +500,72 @@ function media_filter_format_edit_form_validate($form, FormStateInterface $form_
    +function media_form_alter(array &$form, FormStateInterface $form_state, $form_id) {
    

    Can we use hook_form_FORM_ID_alter() instead?

  2. +++ b/core/modules/media/media.module
    @@ -496,3 +500,72 @@ function media_filter_format_edit_form_validate($form, FormStateInterface $form_
    +    catch (EntityStorageException $e) {
    +      \Drupal::messenger()->addError($e->getMessage());
    +      return;
    

    Just for my information, when does this happen? Is there a way we can improve this and provide actionable feedback to users?

  3. +++ b/core/modules/media/src/Plugin/media/Source/Image.php
    @@ -161,4 +162,23 @@ public function createSourceField(MediaTypeInterface $type) {
    +      $component['settings']['image_style'] = 'large';
    

    Users could have removed the `large` image style. What happens when that is the case?

    Maybe we could also use a test for this?

oknate’s picture

Addressing #58:
1. Added the requested test coverage. It does raise the question, why is the test coverage in demo_umami so different from standard? Perhaps we should copy (In a separate issue) the config verification in demo_umami to standard?
Addressing #59:
1. ✅Updated to use hook_form_FORM_ID_alter. I think this is the only form in core where the entity type is media_type and the operation is 'add', so this simplifies the code.
2. See #28.7. The catch statement was just "in case". I'll leave it for now, but I can't see a normal pathway for triggering it.
3. ✅I think one easy solution is to just recreate the ‘large’ image style if it’s been deleted. I’m adding code here to do that, and test coverage. The site builder can delete it again if they so choose. But without adding a configuration for the image style to use when creating media, I think restoring the sensible default makes sense. And I feel adding a configuration for the image style to use when creating media types might be overreaching. Since this is a default, I think it’s fine. I was debating adding the messenger service to the Image media source plugin and adding a message ‘The Large (480×480) image style has been recreated.’, but I think this so harmless we don’t need to notify the site builder. If they choose to modify the entity view display they’ll see it has been recreated, and adding an image style is harmless.

oknate’s picture

Issue summary: View changes

Updating proposed resolution

oknate’s picture

Issue summary: View changes

Updating proposed resolution.

oknate’s picture

Issue summary: View changes

Updating proposed resolution.

phenaproxima’s picture

+++ b/core/modules/media/src/Plugin/media/Source/Image.php
@@ -173,6 +174,25 @@ public function prepareViewDisplay(MediaTypeInterface $type, EntityViewDisplayIn
+      $storage = $this->entityTypeManager->getStorage('image_style');
+      $image_style = $storage->load('large');
+      // If the site builder has deleted the large image style, recreate it.
+      if (!$image_style) {
+        $image_style = $storage->create([
+          'name' => 'large',
+          'label' => 'Large (480×480)',
+        ]);
+        $image_style->addImageEffect([
+          'id' => 'image_scale',
+          'weight' => 0,
+          'data' => [
+            'width' => 480,
+            'height' => 480,
+            'upscale' => FALSE,
+          ],
+        ]);
+        $image_style->save();
+      }

I don't think I'm in favor of this. If the site builder deletes the Large image style, there's a 99% chance they did it on purpose, and they won't want to see it come back inexplicably without their input. Besides, thanks to the config dependency system, these "Embed" 'displays will be automatically deleted if the image style is.

IMHO, if they delete the Large image style, we should just fall back to the original-sized raw image. But let's get feedback from others before proceeding with that.

Wim Leers’s picture

#64: this reminds me of #2289551: Clarify what 'locked' means for a config entity and whether it's okay for code to rely on a locked config entity existing — half a dozen or so config entity types in Drupal core can be "locked", which prevents deletion. (Search for function isLocked().) But it was never generalized.

For an example, see \Drupal\Core\Datetime\DateFormatInterface::isLocked() + \Drupal\system\DateFormatAccessControlHandler::checkAccess().

oknate’s picture

What about if we ask them what image style they want for the embed in the case 'large' has been deleted?

oknate’s picture

Actually, I think Wim is proposing we prevent the 'large' style from being deleted. But won't that have backwards compatibility problems?

It might not if we recreate it and then lock it from being deleted.

Wim Leers’s picture

I didn't make myself clear enough. ImageStyle config entities would ideally be lockable, but they are NOT lockable today. I linked to four related issues about making more config entities lockable. I merely pointed out that that could solve it, but it does not solve it today.

seanB’s picture

I'm also not in favor of restoring deleted image styles. See #2988433: Automatically create and configure Media Library view and form displays for more background on that.

What was suggested in #2988433-60: Automatically create and configure Media Library view and form displays was falling back to the media_library image style instead of "large" when it doesn't exist. That is probably not perfect, but a better default than the big raw image. And the beauty is the media_library image style can not be deleted in the UI, because we fixed that :)

oknate’s picture

Using #70's suggestion of the media library view mode would work, but I don't think this is currently true:

the media_library image style can not be deleted in the UI,

#3077932: Prevent deletion of the Media library view mode

When I was working on this last week, I discovered this bug. I'm 99% certain I deleted the Media Library view mode through the UI.

oknate’s picture

Oh, Sean B explained to me that I was confusing the Image Style with the view mode in #71.

seanB’s picture

Whoops, did not mean to remove the related issues.

When I was working on this last week, I discovered this bug. I'm 99% certain I deleted the Media Library view mode through the UI.

The view mode can be deleted, the media_library image style not however. See media_library_image_style_access().

oknate’s picture

OK, sweet, let me confirm that's true, but if that's the case, I'm OK with using the media library image style as the fallback.

What do @wim-leers and @phenaproxima think?

phenaproxima’s picture

OK, sweet, let me confirm that's true, but if that's the case, I'm OK with using the media library image style as the fallback.

We can't, sadly. The media_library image style comes from Media Library, but the embedded view mode comes from Media. So that creates an implicit dependency on Media Library (and, since Media Library depends on Media, it's actually a circular dependency). We'd have to provide a new fallback image style in this patch. Which might actually be the best solution, since it echoes existing precedents in core. Let's get a committer (framework or product manager) to weigh in.

seanB’s picture

An embed image style, I like that.

oknate’s picture

Also, I can confirm the "delete" button doesn't exist in the contextual menu for the Media Library image style, but if you go there directly, you can still delete it:

#3079235: Media Library image style shouldn't be able to be deleted through the UI.

oknate’s picture

Regarding #75:

We had an embed image style but it was dropped in favor of the 'Large' image style. See #33 through #35.

This will create the best consistency with other fields on the site, and ensure that if a site builder adjusts this size to be different, Media stays consistent.

So do we want to create this 'Embed' style only when the 'Large' image style is deleted?

If we're going to surprise them, rather than restore a style they deleted, give 'em something completely different. An image style exactly like large, but named something else?

phenaproxima’s picture

I think we should do what we did for the media_library image style. Create a new 'embed' image style which, apart from the label and machine name, is identical to 'large', and remove the ability to delete it in the UI. It should never be automatically recreated.

webchick’s picture

Yeah, automatically re-creating something you've intentionally deleted sounds a bit too magical, and additionally could inspire mass paranoia that the inevitable robot overtaking of humanity is en route. ;)

We (@oknate, @phenaproxima, @effulgentsia, and I) talked this through, and decided on the following:

  • If the "Large" image style exists, use it. (Should work for ~80% of sites.)
  • If not (or at any other time the image style might be set to NULL), add a hook_requirements() warning to the status report that says something like, "Embedded media is not currently using an image style, which results in much larger image file downloads. Go to the XXX admin settings in order to change this." (Sorry, I'm not actually sure where this happens.)
webchick’s picture

Also, the reason "why not" a separate embedded image style rather than piggy-backing off of Large is because generally speaking (I think... if you have real-world experience that contradicts this, feel free to point that out), you want consistency between your site's image sizes, regardless of "how" they were put on the page. So that would require you to keep two separate image styles "in sync" when you made changes to one.

Wim Leers’s picture

I like #80 a lot! 👍 Well done, @oknate, @phenaproxima, @effulgentsia and @webchick, thanks for being so on top of this! 🙏

seanB’s picture

If not, add a hook_requirements() warning to the status report that says something like, "Embedded media is not currently using an image style, which results in much larger image file downloads. Go to the XXX admin settings in order to change this." (Sorry, I'm not actually sure where this happens.)

We have to be careful to not show the warning to users that removed the "large" image style and configured the embed view mode to a different style. It could be annoying/confusing to see a warning for something that you configured properly. Besides that, sounds good to me.

webchick’s picture

Right, sorry, this warning would only show up if the image style was set to NULL. (Edited #80 to make that clearer.)

oknate’s picture

No image style warning:
No image style warning

I tried to use the shorter version @webchick came up with:

Embedded media is not currently using an image style, which results in much larger image file downloads. Go to the XXX admin settings in order to change this

But I kept running into an issue, which is that there could potentially be fifty of these on the page, if there were fifty media image entity view displays that were misconfigured. So we needed some more specific info. Also, if the user doesn't have the field_ui module enabled, we can't add an action item. So I needed it to work without the link. So again, it was important to add more specific information about the problem.

This version will give enough info when Field UI isn't enabled and specific actionable information when it is.

oknate’s picture

Adding an assertion for the action item link href.

phenaproxima’s picture

Status: Needs review » Needs work
  1. +++ b/core/modules/media/config/install/core.entity_view_mode.media.embed.yml
    @@ -0,0 +1,12 @@
    +dependencies:
    +  enforced:
    +    module:
    +      - media
    +  module:
    +    - media
    

    I'm not sure we need both module: [media] and enforced: {module: [media]}. I think we can just use the enforced dependency.

  2. +++ b/core/modules/media/config/install/core.entity_view_mode.media.embed.yml
    @@ -0,0 +1,12 @@
    +cache: true
    

    In accordance with Wim's feedback in #51.1, we can set this to false.

  3. +++ b/core/modules/media/media.install
    @@ -118,6 +122,36 @@ function media_requirements($phase) {
    +    $view_mode = EntityViewMode::load('media.embed');
    +    if (!empty($view_mode)) {
    

    I think we should have a good long comment above this section, explaining what we're doing and why.

  4. +++ b/core/modules/media/media.install
    @@ -118,6 +122,36 @@ function media_requirements($phase) {
    +      foreach (MediaType::loadMultiple() as $type) {
    

    I think that, inside this foreach loop, we have a lot of nesting which is making it a bit harder to follow the logic. I'd prefer if we did early continues for failing return conditions, like so:

    foreach (MediaType::loadMultiple() as $type) {
      if (!$source_field_is_an_image) {
        continue;
      }
      // ...
      $component = $display->getComponent($field_name);
      if (!$component || $component['type'] !== 'image') {
        continue;
      }
    }
    
  5. +++ b/core/modules/media/media.install
    @@ -118,6 +122,36 @@ function media_requirements($phase) {
    +        if (is_a($source, Image::class, TRUE)) {
    

    We should be filtering by the field item type (which we can ascertain by $source_field_definition->getItemDefinition()->getClass()), not source plugin class. In this case, we want to target media types which have a source field that uses Drupal\image\Plugin\Field\FieldType\ImageItem or any subclass thereof.

  6. +++ b/core/modules/media/media.install
    @@ -118,6 +122,36 @@ function media_requirements($phase) {
    +            if ($component['settings']['image_style'] === '') {
    

    We also need to check if the $component['type'] is 'image', since that's the image formatter.

  7. +++ b/core/modules/media/media.install
    @@ -118,6 +122,36 @@ function media_requirements($phase) {
    +                $action_item = new TranslatableMarkup('If you would like to change this, <a href=":display">add an image style to the field %field_name</a>.', ['%field_name' => $source_field->label(), ':display' => $url]);
    

    Can the wording of the link be changed to "add an image style to the %field_name field"?

  8. +++ b/core/modules/media/media.install
    @@ -118,6 +122,36 @@ function media_requirements($phase) {
    +                'description' => new TranslatableMarkup('The %view_mode_label display for the media type %type is not currently using an image style on the field %field_name. Not using an image style can lead to much larger file downloads. @action_item', ['%view_mode_label' => $view_mode->label(), '%field_name' => $source_field->label(), '@action_item' => $action_item, '%type' => $type->label()]),
    

    Same here. Otherwise, this wording is great. Minor nit: the arguments should be on their own lines, I think, for clarity. Otherwise this is a super long line. So something like:

    'description' => new TranslatableMarkup('...', [
      '%view_mode_label' => $foo,
      '%type' => $type,
      // ...etc.
    ]),
    
  9. +++ b/core/modules/media/media.install
    @@ -118,6 +122,36 @@ function media_requirements($phase) {
    +              if ($module_handler->moduleExists('field_ui')) {
    +                $url = Url::fromRoute('entity.entity_view_display.media.view_mode', ['media_type' => $type->id(), 'view_mode_name' => 'embed'])->toString();
    ...
    +              }
    
    +++ b/core/modules/media/tests/src/FunctionalJavascript/MediaEmbedDisplayModeTest.php
    @@ -0,0 +1,150 @@
    +    $assert_session->linkByHrefExists('/admin/structure/media/manage/madame_bonacieux/display/embed');
    

    Nice. Covering our bases! 👍

    However, I think we also need to cover the cases where the Field UI module is not installed, or it is installed but the user does not have permission to administer the displays. To facilitate that, we might want to move Madame Bonacieux's stuff into its own test method.

  10. +++ b/core/modules/media/media.install
    @@ -118,6 +122,36 @@ function media_requirements($phase) {
    +              if ($module_handler->moduleExists('field_ui')) {
    +                $url = Url::fromRoute('entity.entity_view_display.media.view_mode', ['media_type' => $type->id(), 'view_mode_name' => 'embed'])->toString();
    +                $action_item = new TranslatableMarkup('If you would like to change this, <a href=":display">add an image style to the field %field_name</a>.', ['%field_name' => $source_field->label(), ':display' => $url]);
    +              }
    

    We also need to check if the current user has access to configure the media displays (i.e., they have the 'administer media display' permission).

  11. +++ b/core/modules/media/media.install
    @@ -118,6 +122,36 @@ function media_requirements($phase) {
    +              $requirements['media_embed_image_style_' . $type->id()] = [
    +                'title' => t('Media'),
    +                'description' => new TranslatableMarkup('The %view_mode_label display for the media type %type is not currently using an image style on the field %field_name. Not using an image style can lead to much larger file downloads. @action_item', ['%view_mode_label' => $view_mode->label(), '%field_name' => $source_field->label(), '@action_item' => $action_item, '%type' => $type->label()]),
    +                'severity' => REQUIREMENT_WARNING,
    +              ];
    

    This will, indeed, produce a warning for each media type which isn't using the image style. Good, I say! I think that having a clickable action item for each media type is more valuable than trying to aggregate all of these things into one big (and therefore confusing, overwhelming, and not really actionable) warning.

  12. +++ b/core/modules/media/media.module
    @@ -508,3 +511,64 @@ function media_field_widget_form_alter(&$element, FormStateInterface $form_state
    +  catch (EntityStorageException $e) {
    +    \Drupal::messenger()->addError($e->getMessage());
    +    return;
    +  }
    

    We don't need the return here.

  13. +++ b/core/modules/media/tests/src/Functional/Update/MediaUpdateTest.php
    @@ -114,4 +116,19 @@ public function testEnableStandaloneUrl() {
    +    $this->assertNull(ImageStyle::load('embed'));
    

    This line is no longer relevant.

  14. +++ b/core/modules/media/tests/src/Functional/Update/MediaUpdateTest.php
    @@ -114,4 +116,19 @@ public function testEnableStandaloneUrl() {
    +    $this->assertNull(EntityViewDisplay::load('media.file.embed'));
    +    $this->assertNull(EntityViewDisplay::load('media.image.embed'));
    +    $this->runUpdates();
    +    $this->assertInstanceOf(EntityViewDisplay::class, EntityViewDisplay::load('media.file.embed'));
    +    $this->assertInstanceOf(EntityViewDisplay::class, EntityViewDisplay::load('media.image.embed'));
    

    Why aren't we also testing that the displays are created for the remote_video, audio_file, and video_file media types?

    Also, shouldn't we test that they are configured as expected (as in MediaEmbedDisplayModeTest)?

  15. +++ b/core/modules/media/tests/src/FunctionalJavascript/MediaEmbedDisplayModeTest.php
    @@ -0,0 +1,150 @@
    + * Tests that media automatically configures an embed view mode and display.
    

    Nit: 'media' should be 'Media', since that's the proper name of the module.

  16. +++ b/core/modules/media/tests/src/FunctionalJavascript/MediaEmbedDisplayModeTest.php
    @@ -0,0 +1,150 @@
    +      // We need 'access content' for system.machine_name_transliterate.
    +      'access content',
    

    🙄 What a WTF. Thanks for the comment :)

  17. +++ b/core/modules/media/tests/src/FunctionalJavascript/MediaEmbedDisplayModeTest.php
    @@ -0,0 +1,150 @@
    +    // If for some reason a site builder deletes the 'large' image style, it
    +    // should be recreated when adding a new media type with an image source.
    

    This comment is not accurate. :)

  18. +++ b/core/modules/media/tests/src/FunctionalJavascript/MediaEmbedDisplayModeTest.php
    @@ -0,0 +1,150 @@
    +    $assert_session->pageTextContains('The Embed display for the media type Madame Bonacieux is not currently using an image style on the field Image.');
    

    We should also assert the rest of the warning, including the text of the link.

  19. +++ b/core/modules/media/tests/src/FunctionalJavascript/MediaEmbedDisplayModeTest.php
    @@ -0,0 +1,150 @@
    +      if (is_a(ImageStyle::load('large'), ImageStyleInterface::class, TRUE)) {
    

    I don't think we need to is_a() here. If we can load the image style at all, it's guaranteed to be an ImageStyleInterface instance.

  20. +++ b/core/profiles/standard/tests/src/Functional/StandardTest.php
    @@ -256,6 +256,19 @@ public function testStandard() {
    +      $page->hasUncheckedField('display_modes_custom[full]');
    

    What is this for?

  21. +++ b/core/profiles/standard/tests/src/Functional/StandardTest.php
    @@ -256,6 +256,19 @@ public function testStandard() {
    +        $page->hasCheckedField('display_modes_custom[embed]');
    

    This isn't an assertion. It should be $assert_session->checkboxChecked().

  22. +++ b/core/profiles/standard/tests/src/Functional/StandardTest.php
    @@ -256,6 +256,19 @@ public function testStandard() {
    +        $this->assertTrue($assert_session->optionExists('fields[field_media_image][type]', 'image')->isSelected());
    

    I don't think we need the assertTrue(). $assert_session->fieldValueEquals() should do the trick.

  23. +++ b/core/profiles/standard/tests/src/Functional/StandardTest.php
    @@ -256,6 +256,19 @@ public function testStandard() {
    +        $assert_session->elementTextContains('css', 'tr[data-drupal-selector="edit-fields-field-media-image"]', 'Image style: Large (480×480)');
    

    Oh man, that is detailed. I approve! 👍

  24. +++ b/core/profiles/standard/tests/src/Functional/StandardTest.php
    @@ -256,6 +256,19 @@ public function testStandard() {
    +        $page->hasUncheckedField('display_modes_custom[embed]');
    

    This should be $assert_session->checkboxNotChecked().

oknate’s picture

Assigned: Unassigned » oknate

Assigning to myself.

oknate’s picture

Status: Needs work » Needs review
FileSize
15.15 KB
28.6 KB

Addressing #87:
1. ✅ Updated to remove the non-enforced module dependency.
2. ✅ Set ‘cache’ to false.
3. ✅ Added a long comment.
4. ✅ Added continue statements to reduce indention.
5. ✅ Changed the logic to check for ImageItem on $source_field->getItemDefinition()->getClass().
6. ✅ Switched the word order, per your request.
7, 8. ✅ ✅ Moved the arguments to their own lines.
9. ✅ Added additional test coverage for ‘administer media display’ permission and with Field UI uninstalled. I don’t think we need to move this to a new test. Take a look and see if you agree.
10. ✅ Added the 'administer media display' permission, good call.
12. ✅ Removed ‘return’ statement.
13. ✅ Removed assertion.
14. “Why aren't we also testing that the displays are created for the remote_video, audio_file, and video_file media types?
Because this is an upgrade test, and when upgrading from Drupal 8.4, the media module doesn’t install those other media types. There are configs for these types in demo_umami and standard, but upgrading from Drupal.4 doesn’t install those configs.
Also, shouldn't we test that they are configured as expected (as in MediaEmbedDisplayModeTest)?” ✅ Yes, added that.
15. ✅Capitalized “Media”
17. ✅ Updated the comment.
18. ✅ Asserted the rest of the error message.
19. ✅ Removed the is_a().
20. ✅ We don’t need that. Removing.
21, 24. ❌ Because this field is hidden in a details element, I couldn’t get $assert_session->checkboxChecked() to work. I can’t figure out how to open a details element in these functional tests. I updated to
$this->assertTrue($page->hasCheckedField('display_modes_custom[embed]'));
and
$this->assertTrue($page->hasUncheckedField('display_modes_custom[embed]'));
and that seems to work OK.
22. ✅ Updated to fieldValueEquals().

Status: Needs review » Needs work

The last submitted patch, 89: 3066802-88.patch, failed testing. View results

oknate’s picture

Restoring the module dependency separate from the enforced module dependency. Perhaps it's a problem with that test, but for now, I'll just change the config to pass the test. I don't think it hurts to have both the module dependency and the enforced module dependency and that's what Drupal\KernelTests\Config\DefaultConfigTest::testModuleConfig is expecting.

Drupal\KernelTests\Config\DefaultConfigTest::testModuleConfig with data set "media" ('media')
Exception: core.entity_view_mode.media.embed: Drupal\Component\Diff\Engine\DiffOpAdd::__set_state(array(
   'type' => 'add',
   'orig' => false,
   'closing' => 
  array (
    0 => '  module:',
    1 => '    - media',
    2 => '_core:',
    3 => '  default_config_hash: dNliJ9x0voTDhaKX7YHU-xv3ZeWSiC-_dhaqV1URBwg',
  ),
))
oknate’s picture

Status: Needs work » Needs review
oknate’s picture

On slack, @phenaproxima said we should remove the enforced dependency. It's not necessary as it already depends on the media entity, so it's redundant.

phenaproxima’s picture

Status: Needs review » Needs work
Issue tags: +Needs framework manager review

Virtually everything else I found was nitpicks. This is really close to done. Since there is an update path, I'm tagging this for framework manager review (and ideally a framework manager will commit it).

  1. +++ b/core/modules/media/media.install
    @@ -118,6 +122,61 @@ function media_requirements($phase) {
    +    // When a new media type is created, we're configuring the entity view
    +    // display for embedded media.  For media types with an image source,
    +    // we're using the 'large' image style.  Unfortunately, if a site builder
    +    // has deleted the 'large' image style, we need some other image style to
    +    // use, but at this point, we can't really know the site-builder's
    +    // intentions.  So rather than do something surprising, we're leaving the
    +    // embedded media without an image style and adding a warning that the site
    +    // builder might want to add an image style.
    

    This could use a @see comment for _media_configure_embed_view_display(). Also, some supernits: every sentence is separated by two spaces after the period, but it should just be one. Also, this is using "site builder" and "site-builder" interchangeably; it should just standardize on "site builder".

    Otherwise, this is 💯

  2. +++ b/core/modules/media/media.install
    @@ -118,6 +122,61 @@ function media_requirements($phase) {
    +    if (!empty($view_mode)) {
    

    Supernit that you should feel completely free to ignore: in this case, if (!empty($view_mode)) is identical to if ($view_mode).

  3. +++ b/core/modules/media/media.install
    @@ -118,6 +122,61 @@ function media_requirements($phase) {
    +        $display = $display_repository->getViewDisplay('media', $type->id(), 'embed');
    

    Nit: I don't think we really need $display_repository to be its own variable.

  4. +++ b/core/modules/media/media.install
    @@ -118,6 +122,61 @@ function media_requirements($phase) {
    +        $item_class = $source_field_definition->getItemDefinition()->getClass();
    +        if (!is_a($item_class, ImageItem::class, TRUE)) {
    

    $item_class doesn't really need to be its own variable either.

  5. +++ b/core/modules/media/media.install
    @@ -118,6 +122,61 @@ function media_requirements($phase) {
    +        $field_name = $source_field_definition->getName();
    +        $component = $display->getComponent($field_name);
    

    Nit: Same with $field_name.

  6. +++ b/core/modules/media/media.install
    @@ -118,6 +122,61 @@ function media_requirements($phase) {
    +        if (empty($component) || $component['type'] !== 'image' || $component['settings']['image_style'] !== '') {
    

    Nit: I could change the last condition here to !empty($component['settings']['image_style']), since that's technically more "correct".

  7. +++ b/core/modules/media/media.install
    @@ -118,6 +122,61 @@ function media_requirements($phase) {
    +          $url = Url::fromRoute('entity.entity_view_display.media.view_mode', ['media_type' => $type->id(), 'view_mode_name' => 'embed'])->toString();
    

    Nit: I would put the route parameters onto a new line, for readability, like so:

    $url = Url::fromRoute('entity.entity_view_display.media.view_mode', [
      'media_type' => $type->id(),
      'view_mode_name' => 'embed',
    ]);
    

    ...and then use $url->toString() when creating the variables for the TranslatableMarkup.

  8. +++ b/core/modules/media/media.install
    @@ -118,6 +122,61 @@ function media_requirements($phase) {
    +          'description' => new TranslatableMarkup('The %view_mode_label display for the media type %type is not currently using an image style on the %field_name field. Not using an image style can lead to much larger file downloads. @action_item',
    

    Nit: It might read better to rearrange the words "media type %type" to "%type media type".

  9. +++ b/core/modules/media/media.module
    @@ -508,3 +511,63 @@ function media_field_widget_form_alter(&$element, FormStateInterface $form_state
    +  return $display->save();
    

    This should be (bool) $display->save(), so that we're acting in accordance with the @return part of the doc comment.

  10. +++ b/core/modules/media/media.module
    @@ -508,3 +511,63 @@ function media_field_widget_form_alter(&$element, FormStateInterface $form_state
    +function media_form_media_type_add_form_alter(&$form, FormStateInterface $form_state, $form_id) {
    

    &$form should be type hinted as an array.

  11. +++ b/core/modules/media/media.post_update.php
    @@ -5,6 +5,39 @@
    +      'dependencies' => [
    +        'enforced' => [
    +          'module' => ['media'],
    +        ],
    +      ],
    

    This shouldn't be using enforced dependencies, as per the last few comments.

  12. +++ b/core/modules/media/src/Plugin/media/Source/Image.php
    @@ -161,4 +162,27 @@ public function createSourceField(MediaTypeInterface $type) {
    +    // For the `embed` display mode, use the `large` image style and do not
    +    // link the image to anything. This is done in order to ensure the image
    +    // does not become too unwieldy when embedded into formatted text, and to
    +    // allow it to wrapped by an author-created link.  If the `large` image
    +    // style has been deleted, do not set an image style.
    +    if ($display->getMode() === 'embed') {
    +      $field_name = $this->getSourceFieldDefinition($type)->getName();
    +      $component = $display->getComponent($field_name);
    +      $component['settings']['image_link'] = '';
    +      $component['settings']['image_style'] = '';
    +      if ($this->entityTypeManager->getStorage('image_style')->load('large')) {
    +        $component['settings']['image_style'] = 'large';
    +      }
    +      $display->setComponent($field_name, $component);
    +    }
    

    👍

  13. +++ b/core/modules/media/tests/src/Functional/Update/MediaUpdateTest.php
    @@ -114,4 +117,42 @@ public function testEnableStandaloneUrl() {
    +  /**
    +   * Asserts the embed view display components for a media type.
    +   *
    +   * @param string $type_id
    +   *   The media type ID.
    +   */
    +  protected function assertViewDisplayConfigured($type_id) {
    +    $type = MediaType::load($type_id);
    +    $display = EntityViewDisplay::load('media.' . $type_id . '.embed');
    +    $this->assertInstanceOf(EntityViewDisplay::class, $display);
    +    $source_field_definition = $type->getSource()->getSourceFieldDefinition($type);
    +    $component = $display->getComponent($source_field_definition->getName());
    +    $this->assertInternalType('array', $component);
    +    if ($source_field_definition->getType() === 'image' && $component['type'] === 'image') {
    +      if (ImageStyle::load('large')) {
    +        $this->assertSame('large', $component['settings']['image_style']);
    +      }
    +      else {
    +        $this->assertEmpty($component['settings']['image_style']);
    +      }
    +      $this->assertEmpty($component['settings']['image_link']);
    +    }
    +  }
    

    It's too bad we need to repeat this code in another test, but I can't think of a more appropriate, shared place to put it. (Maybe MediaTypeCreationTrait would be correct? I dunno...I'd like a framework manager opinion on this.)

  14. +++ b/core/modules/media/tests/src/FunctionalJavascript/MediaEmbedDisplayModeTest.php
    @@ -0,0 +1,171 @@
    +    $assert_session->pageTextContains('The Embed display for the media type Madame Bonacieux is not currently using an image style on the Image field. Not using an image style can lead to much larger file downloads.');
    +    $assert_session->pageTextNotContains('If you would like to change this, add an image style to the Image field.');
    

    Maybe we should also add an $assert_session->linkByHrefNotExists().

  15. +++ b/core/modules/media/tests/src/FunctionalJavascript/MediaEmbedDisplayModeTest.php
    @@ -0,0 +1,171 @@
    +    $assert_session->pageTextContains('The Embed display for the media type Madame Bonacieux is not currently using an image style on the Image field. Not using an image style can lead to much larger file downloads. If you would like to change this, add an image style to the Image field.');
    +    $assert_session->linkByHrefExists('/admin/structure/media/manage/madame_bonacieux/display/embed');
    

    I would also add a call to $assert_session->linkExists() to ensure that the link has the correct text.

  16. +++ b/core/modules/media/tests/src/FunctionalJavascript/MediaEmbedDisplayModeTest.php
    @@ -0,0 +1,171 @@
    +    // The image style warning should not include an action link when the
    +    // Field UI module is uninstalled.
    +    $this->container->get('module_installer')->uninstall(['field_ui']);
    +    $this->drupalGet('/admin/reports/status');
    +    $assert_session->pageTextContains('The Embed display for the media type Madame Bonacieux is not currently using an image style on the Image field. Not using an image style can lead to much larger file downloads.');
    +    $assert_session->pageTextNotContains('If you would like to change this, add an image style to the Image field.');
    

    This could probably also benefit from a call to $assert_session->linkByHrefNotExists().

oknate’s picture

Status: Needs work » Needs review
FileSize
7.92 KB
28.8 KB

Addressing #94:
1. ✅ Added @see statement and removed double spaces between sentences.
2. ✅ Changed !empty($view_mode) to $view_mode.
3. ✅ Dropped the variable for

\Drupal::service('entity_display.repository')

4. ✅ Dropped the variable for $item_class.
5. ✅ Dropped the variable for $field_name.
6. ✅ Updated to !empty($component['settings']['image_style'])
7. ✅ Moved the route parameters to their own lines.
8. ✅ Flipped the word order.
9. ✅ Added the (bool) cast, so that SAVED_NEW or SAVED_UPDATED is converted to TRUE.
10. ✅ Added type hint.
11. ✅ Handled this morning already in #93.
13. Let’s add a folow-up to move this to a trait. I don’t want to hold back the issue by adding a new trait.
14, 16. ✅ ✅ Added $assert_session->linkByHrefNotExists().
15. ✅ Added $assert_session->linkExists().

Status: Needs review » Needs work

The last submitted patch, 95: 3066802-94.patch, failed testing. View results

oknate’s picture

Status: Needs work » Needs review
FileSize
1.21 KB
28.74 KB

This was just a cut-and-paste error. I changed something inadvertently.

Status: Needs review » Needs work

The last submitted patch, 97: 3066802-97.patch, failed testing. View results

oknate’s picture

Status: Needs work » Needs review
FileSize
3.36 KB
3.42 KB
28.77 KB

Fixing the test. I was wrong last time as to why it was failing.

phenaproxima’s picture

Status: Needs review » Needs work
+++ b/core/modules/media/media.post_update.php
@@ -5,6 +5,39 @@
+  if (!EntityViewMode::load('media.embed')) {
+    EntityViewMode::create([
+      'id' => 'media.embed',
+      'targetEntityType' => 'media',
+      'label' => t('Embed'),
+      'dependencies' => [
+        'enforced' => [
+          'module' => ['media'],
+        ],
+      ],
+    ])->save();
+  }

This is still using enforced dependencies, and it probably should not be. :)

I think that's all I see needing changing. Once that's done, I'm officially out of things to complain about. Fantastic work, this patch looks terrific!

oknate’s picture

Status: Needs work » Needs review
FileSize
527 bytes
28.73 KB

Good catch. Updated.

phenaproxima’s picture

Issue tags: +Needs product manager review
FileSize
284.79 KB

Code looks good, so I gave this a quick manual test.

For the most part, it behaved exactly the way you'd expect, and it was just glorious. Creating a link around an embedded image media item worked flawlessly in CKEditor. So hooray for that!!

But then I tried creating a new "Remote video" media type, called "alpha". I got the expected message about the new Embed view display being created for the media type, but it looked like this when I went to configure it:

The Embed view display for a new media type

I think this will be a bit of a WTF for site builders, to be honest. If I create a new remote video media type, I'd expect that only "Remote video URL" would be shown in an embed context. It seems like the line for $display->set('content', []) is not taking effect? Either way, I think we might need to increase the test coverage a bit.

Another thing that is kind of annoying here is that, in a new installation, we don't ship Embed view displays for any media type but Image. Yet we always create an Embed view display for each new media type. That can lead to a situation where, in a site that has updated to Drupal 8.8, all media types will have an active Embed view display, but in a new site, only the Image media type will. That inconsistency has me wondering if we should ship an Embed view display for all media types we currently ship with Standard and Umami, just to keep things nice and consistent. That's a product manager decision, so I'm tagging this as needing product manager review.

Also, a minor thing: the label of the view mode is "Embed", not "Embedded". I found that confusing, and I suspect site builders might as well. I personally think we should change the view mode's label to "Embedded" (no need to change the machine name). However, since that is purely a string change, we don't need to do it in this issue if we don't want to (and, in fact, can do it all the way until RC, as I understand it).

oknate’s picture

@phenaproxima's hunch is right, $display->set('content', []) leads to a broken config, which is surely worthy of another ticket.
Notice that the hidden section is missing the fields we removed?

uuid: 3129c941-fea6-4a58-b631-1a2fc27edbcf
langcode: en
status: true
dependencies:
  config:
    - core.entity_view_mode.media.embed
    - field.field.media.test_10_46.field_media_oembed_video_3
    - media.type.test_10_46
  module:
    - media
id: media.test_10_46.embed
targetEntityType: media
bundle: test_10_46
mode: embed
content:
  field_media_oembed_video_3:
    type: oembed
    weight: 0
    label: above
    settings:
      max_width: 0
      max_height: 0
    third_party_settings: {  }
    region: content
hidden:
  name: true

If we switch the code back to how it was in the media library module and here before #29, we get a config with the hidden fields:

uuid: 998e03ae-d58c-44f9-9f70-35c477a1ef6c
langcode: en
status: true
dependencies:
  config:
    - core.entity_view_mode.media.embed
    - field.field.media.test_10_57.field_media_oembed_video_4
    - media.type.test_10_57
  module:
    - media
id: media.test_10_57.embed
targetEntityType: media
bundle: test_10_57
mode: embed
content:
  field_media_oembed_video_4:
    type: oembed
    weight: 0
    label: above
    settings:
      max_width: 0
      max_height: 0
    third_party_settings: {  }
    region: content
hidden:
  created: true
  name: true
  thumbnail: true
  uid: true

If the fields are not in the hidden section of the config, they get magically added back to the content section when you go to edit the entity view display.

This is probably useful in some contexts, but it wasn't helpful in our context. I added an issue for it, #3079910: Fields missing from EntityViewDisplay are added to content not hidden when visiting form. Although I'm not even sure it's something that should be fixed.

The fix is just to revert that change. And now when I create a 'Remote Video' media type, the embed entity view display only shows the expected field.

Given the magical nature of these removed fields resurfacing, I agree that adding additional test coverage is in order. I'm not sure how far we should go. I guess as a minimum, we should add a test for Remote video and test when going to edit the display only the source field is in the content section.

For the label 'Embedded', should we also change the machine name? It seems odd to use the machine name 'embed' but then label it 'Embedded' when we can change it.

oknate’s picture

  • Updates 'embed' view mode to 'embedded'.
  • Adds additional test coverage including video:oembed source and that the appropriate fields show in field ui form for editing an entity view display.
oknate’s picture

Regarding:

Another thing that is kind of annoying here is that, in a new installation, we don't ship Embed view displays for any media type but Image.

The reason we decided to do this is that the default and the embedded entity view displays would be identical for those media types. So as far as I can see, the only advantage to including Embedded entity view displays for each media type in standard and demo_umami is that it would save them a click if they decide to customize the Embedded entity view display.

Here are two more advantages I thought of for including the Embedded view display for the other media types:

1) It would be clearer to them that an embedded entity view display for that type is available when they go to alter the display, without having to open the details element on the default display, check the Embedded checkbox and submit.
2) As phenaproxima pointed out, It would be more consistent. When you visit the display settings for the Image media type, you see an Embedded entity view display, so why not with the others?

But the counter-arguments:
1) Site builders will already be familiar with enabling additional entity view displays as needed.
2) Having the Embedded entity view display out of the box for the Image media type, where it's needed, makes it clear that the option is available if needed for the other media types. Not shipping the configs that are identical to the defaults is sensible and keeps the number of configs needed to be installed in a standard install smaller.

phenaproxima’s picture

Status: Needs review » Needs work
similarity index 72%
rename from core/modules/media/config/install/core.entity_view_mode.media.embed.yml

rename from core/modules/media/config/install/core.entity_view_mode.media.embed.yml
rename to core/modules/media/config/install/core.entity_view_mode.media.embedded.yml

Yikes. This is overkill, IMHO -- we do not need to change the machine name of the view mode to match the label. In the UI, though, I think it would be clearer to say "Embedded" than "Embed" -- so, really, I was only suggesting that the label be changed. For now, let's revert all of this, because it is out of scope. We can easily change the label in another issue, and we can do it all the way through beta if we want to. I'd rather discuss that with the UX team or a product manager first anyway.

oknate’s picture

Status: Needs work » Needs review
FileSize
30.07 KB
20.19 KB
18.47 KB

Reverted the machine name change. Kept the changes in the label and in comments.

phenaproxima’s picture

Status: Needs review » Needs work

Thanks for changing the label; I think that will be clearer, in all honesty. Hopefully product managers agree.

  1. +++ b/core/modules/media/media.install
    @@ -118,6 +122,63 @@ function media_requirements($phase) {
    +    // use, but at this point, we can't really know the site-builder's
    

    Nit: "site-builder" should be "site builder" (no hyphen).

  2. +++ b/core/modules/media/media.install
    @@ -118,6 +122,63 @@ function media_requirements($phase) {
    +        $display = \Drupal::service('entity_display.repository')
    +          ->getViewDisplay('media', $type->id(), 'embed');
    +        if (empty($display)) {
    +          continue;
    +        }
    

    So, getViewDisplay() will never return an empty display; it creates it if it doesn't already exist. So we should probably change that to if ($display->isNew()).

  3. +++ b/core/modules/media/tests/src/Functional/Update/MediaUpdateTest.php
    @@ -114,4 +117,42 @@ public function testEnableStandaloneUrl() {
    +    if ($source_field_definition->getType() === 'image' && $component['type'] === 'image') {
    

    I think this should be if ($source->getPluginId() === 'image'). The reason being that the logic which sets up the image style (or lack thereof) is confined to that specific plugin, and changing the if statement would make that a bit clearer.

  4. +++ b/core/modules/media/tests/src/FunctionalJavascript/MediaEmbedDisplayModeTest.php
    @@ -0,0 +1,211 @@
    +    if ($source_field_definition->getType() === 'image' && $component['type'] === 'image') {
    

    I would also suggest changing this to if ($type->getSource()->getPluginId() === 'image'), for the reason I stated previously.

  5. +++ b/core/modules/media/tests/src/FunctionalJavascript/MediaEmbedDisplayModeTest.php
    @@ -0,0 +1,211 @@
    +    // Assert the source field is enabled some other default fields are disabled.
    +    $assert_session->elementExists('css', 'input[name="' . $source_field_definition->getName() . '_settings_edit"]');
    +    $expected_hidden = [
    +      'uid',
    +      'created',
    +      'thumbnail'
    +    ];
    +    foreach ($expected_hidden as $field_name) {
    +      $assert_session->elementNotExists('css', 'input[name="' . $field_name . '_settings_edit"]');
    +    }
    

    The only flaw I can see here is that we are asserting that the uid, created, and thumbnail fields are not appearing, and then hoping that the lack of those three implies the absence of every other field (except the source field). What I might suggest is doing something like this instead:

    $assert_session->elementsCount('css', 'input[name$="_settings_edit"]', 1);
    $assert_session->buttonExists($source_field_definition->getName() . '_settings_edit');
    
oknate’s picture

Status: Needs work » Needs review
FileSize
3.87 KB
30.07 KB

#Addressing feedback in #108.
1-5. ✅Done.

I especially like the change suggested in 5, using a count on a wildcard of the css class. I'll have to remember that for situations like this. That's much better.

marcoscano’s picture

+++ b/core/modules/media/media.post_update.php
@@ -5,6 +5,38 @@
+function media_post_update_entity_view_displays() {
+  if (!EntityViewMode::load('media.embed')) {
+    EntityViewMode::create([
+      'id' => 'media.embed',
+      'targetEntityType' => 'media',
+      'label' => t('Embedded'),
+      'dependencies' => [
+        'module' => ['media'],
+      ],
+    ])->save();
+  }

Sorry to pop up out of the blue and ask questions at this point in the game... but would it make more sense to have this in media_library.post_update.php?

I could see the scenario of an existing site (that relies on entity_embed, not making the switch to media library soon) not wanting this new viewmode to be created automatically...

Thoughts?

phenaproxima’s picture

I don't think it does, for the simple reason that all of this configuration belongs to the Media module and is intended to be used with the MediaEmbed filter that is part of Media. If sites want to prevent the view mode from being created -- which sounds like an edge case to me -- they would be better off just editing that function and returning early so that the view displays are not created.

Wim Leers credited Nick_vh.

Wim Leers’s picture

Status: Needs review » Needs work
+++ b/core/modules/media/src/Plugin/media/Source/Image.php
@@ -161,4 +162,27 @@ public function createSourceField(MediaTypeInterface $type) {
+      if ($this->entityTypeManager->getStorage('image_style')->load('large')) {

This can use an image style because that is provided by core's image module and the media module depends on the image module. 👍

🤔 But …

There is also the responsive_image module which provided "responsive image styles".

Arguably this is crucial for Media to be successfully used for embedding: people will expect embedded media to behave responsively. That'd still only fix it for media of @MediaSource=image, but that'd still be a valuable improvement.

If we don't deal with that here, this would certainly need a follow-up for that aspect.

(Thanks to @Nick_vh pinging me with the question how media embedding in the text editor will handle responsive image styles!)

oknate’s picture

I would think if the user has the response_image style enabled, we could switch the image style to a responsive one. The demo_umami profile installs responsive_image out of the box with two image styles, 'wide' and 'narrow'.

So perhaps for that profile only, the config for the image media type should have a responsive image as the default.

phenaproxima’s picture

I don't think we should do that in this issue. That would expand the scope, which is the last thing we need right now; "making media responsive" is a bigger issue, and should be dealt with in a follow-up in a more holistic way.

Wim Leers’s picture

Issue tags: +Needs followup

#115++ — hence:

If we don't deal with that here, this would certainly need a follow-up for that aspect.

webchick’s picture

@oknate, @phenaproxima, and I ran through the patch. Verified that it works as intended, so w00t!

There was one question which is whether or not it makes sense to turn on the "Embedded" entity view display by default for all media types, or only Images. We discussed and agreed that in most cases, having them would be just straight up duplication of Defaults (for example, oEmbed doesn't even have any other options to even choose from other than "plain text" which... no), which would lead to lots of annoying "Dude, I *just* changed that, why did it not change it in CKEdit... oh." problems. Images OTOH have a very good reason to have an "Embedded" view mode, because as Wim pointed out in the original issue summary, without that you end up with 50GB, 10000px x 10000px photos (I may be embellishing slightly ;)) by default. So there, the annoying "double configuration" makes sense as a necessary evil.

Currently, the behaviour of the patch is that Images have it by default, existing media types don't, and any new media types get it. That's unfortunately a recipe for confusion. So the proposal is to instead leave it unchecked by default all the time, and only turn it on for media types that have a concrete reason to need it, which for now would be Images. Undoing this decision is just a checkmark away, so that seems good.

effulgentsia’s picture

Images OTOH have a very good reason to have an "Embedded" view mode, because as Wim pointed out in the original issue summary, without that you end up with 50GB, 10000px x 10000px photos (I may be embellishing slightly ;)) by default.

This might be pointing to a separate bug (or at least an unintuitive configuration default). Why is the "default" view mode for media images to display the image without an image style? I could understand that for the "full" view mode, but seems to me the "default" view mode should be to display with the Large image style. I'm fine with punting that to a separate issue though, unless folks working on this issue want it to affect this issue.

oknate’s picture

Adding a separate issue to address #118. #3080549: Add an image style by default for media image. We might want to postpone this issue until that one is investigated, as we might not even need an out of the box embedded view mode if we can set the default to what we were adding in the embed view mode in this issue.

Wim Leers’s picture

Hah… @effulgentsia makes an excellent point, as always 😅👏

Before #3017935: Media items should not be available at /media/{id} to users with 'view media' permission by default , the default view mode was used for the canonical route, and the canonical route was always enabled. But now that that is no longer the case … I think we indeed could choose to customize the default view mode instead of adding a new one.

That would remove much of the complexity in this patch.

phenaproxima’s picture

I discussed this with @effulgentsia and @webchick to solidify the plan here. We agreed on:

  • We'll remove the "Embed" view mode, and all related config, from this patch.
  • However, we will keep most of the code and test coverage. We like the fact that the Image source plugin tries to use the large image style, if available, when configuring a view display. It should do it for every new display, then, regardless of view mode. (In other words, just remove the if ($display->getMode() === 'embed') check.)
  • We also like the fact that the status report will warn you if you have any media displays which aren't using an image style. So let's keep this, but change it so it creates warnings for the default view mode, rather than the now-defunct embed view mode.
  • We will change the default view display we ship for the Image media type so it uses the large image style. This will not need an update path.
  • But, let's open a follow-up issue to ship a full view display for the Image media type, which doesn't use the image style. (i.e., this allows us to preserve the previous behavior when viewing a canonical media item).
webchick’s picture

+1; that sounds like what we talked about! :)

phenaproxima’s picture

Title: Add a new view mode for embedded media » Ensure that embedded media displays sanely by default
Issue tags: -Needs framework manager review

Re-titling to reflect the new directions we're taking. Also removing "needs framework manager review". Since there will be no update path in this patch, we don't need FM sign-off.

oknate’s picture

Status: Needs work » Needs review
FileSize
20.82 KB
46.52 KB

Here's an initial patch for the plan pivot.

  • Dropping the embed view mode
  • Configuring the default media image entity view display to:
    • Only enable one component, the image field.
    • Set the label to visually hidden.
    • Disable link to file by default (to prevent invalid HTML when embedding when linking media with drupalmedia button). I think this a more sensible default behavior anyway.
    • Set the image style to 'large' image style.
  • Removes the form submit hook for the media type add form. I think it's no longer necessary and can be handled within the media type's source's prepareViewDisplay method.
  • Moves the test coverage largely to MediaSourceImageTest.php. Since we're no longer planning on changing the media with a different source, this seems more appropriate.

One question that still needs to be resolved is the default_view_mode setting on the MediaEmbed filter:

 *   settings = {
 *     "default_view_mode" = "full",
 *   },

I tried changing the default_view_mode to 'default' this morning and seven tests broke. So we'll need to decide if it makes sense to change that to default, to make sure that changing it to default is feasible, and if so, to fix those tests and make the change.

phenaproxima’s picture

  1. +++ b/core/modules/media/media.install
    @@ -118,6 +121,59 @@ function media_requirements($phase) {
    +      $display = \Drupal::service('entity_display.repository')
    +        ->getViewDisplay('media', $type->id(), 'default');
    

    We don't need to pass 'default' to getViewDisplay(); it has that as the default argument already. :)

  2. +++ b/core/modules/media/media.install
    @@ -118,6 +121,59 @@ function media_requirements($phase) {
    +      $source = $type->getSource();
    +      $source_field_definition = $source->getSourceFieldDefinition($type);
    

    Nit: I don't think $source needs to be its own variable.

  3. +++ b/core/modules/media/src/Plugin/media/Source/Image.php
    @@ -161,4 +162,35 @@ public function createSourceField(MediaTypeInterface $type) {
    +    if ($display->getMode() === 'default') {
    +      // Remove components other than the source field.
    +      foreach (array_keys($display->getComponents()) as $name) {
    +        if ($name !== $field_name) {
    +          $display->removeComponent($name);
    +        }
    +      }
    +    }
    

    This seems like a very strong opinion for any source plugin to have. I would suggest moving this snippet elsewhere; perhaps to MediaTypeForm::save(), or an implementation of hook_entity_view_display_presave(). The source plugin should really only be concerned with how its source field is going to be displayed.

  4. +++ b/core/modules/media/tests/src/FunctionalJavascript/MediaDisplayTest.php
    @@ -101,10 +101,13 @@ public function testMediaDisplay() {
    +    $expected_image_src = file_url_transform_relative(file_create_url(\Drupal::token()->replace('public://styles/large/public/[date:custom:Y]-[date:custom:m]/example_1.jpeg')));
    

    😲 Methinks Drupal's file API is too complicated...

  5. +++ b/core/modules/media/tests/src/FunctionalJavascript/MediaDisplayTest.php
    @@ -101,10 +101,13 @@ public function testMediaDisplay() {
    +    $assert_session->elementExists('css', '.field__label.visually-hidden');
    +    $assert_session->elementNotExists('css', '.field--name-field-media-image a');
    

    These seem a bit loose. What if we did this:

    $field = $assert_session->elementExists('css', '.field--name-field-media-image');
    $assert_session->elementExists('css', '.field__label.visually-hidden', $field);
    $assert_session->elementNotExists('css', 'a', $field);
    

    In other words, scope these assertions to the field we're checking out, rather than the entire page.

  6. +++ b/core/modules/media/tests/src/FunctionalJavascript/MediaSourceImageTest.php
    @@ -57,16 +62,118 @@ public function testMediaImageSource() {
    +    $image_element = $assert_session->elementExists('css', '.field--name-field-media-image img');
    +    $expected_image_src = file_url_transform_relative(file_create_url(\Drupal::token()->replace('public://styles/large/public/[date:custom:Y]-[date:custom:m]/example_1.jpeg')));
    +    $this->assertContains($expected_image_src, $image_element->getAttribute('src'));
    +    $assert_session->elementExists('css', '.field--name-field-media-image .field__label.visually-hidden');
    +    $assert_session->elementNotExists('css', '.field--name-field-media-image a');
    

    Same idea here, I'd say.

  7. +++ b/core/modules/media/tests/src/FunctionalJavascript/MediaSourceImageTest.php
    @@ -57,16 +62,118 @@ public function testMediaImageSource() {
    +    // Ensure the image has the correct alt attribute.
    +    $assert_session->elementAttributeContains('css', '.field--name-field-media-image img', 'alt', 'Image Alt Text 1');
    

    I think we could just use $this->assertSame('Image Alt Text 1', $image_element->getAttribute('alt')) here, rather than repeating selectors.

oknate’s picture

Addressing #124

 *   settings = {
 *     "default_view_mode" = "default",
 *   },

I had to update the filter to fix this, to allow it to render view mode 'default'. This is actually a previously undiscovered bug. It should perhaps be broken out into a separate issue that blocks this one. I'll post more detail later. But on the current head if set the default view mode to 'default', you'll get the missing media indicator because this code will fail:

$this->entityRepository->loadEntityByConfigTarget('entity_view_mode', "media.$view_mode_id");

causing this to fail:

$build = $media && $view_mode
        ? $this->renderMedia($media, $view_mode_id, $langcode)
        : $this->renderMissingMediaIndicator();

Addressing #125
1. ✅ Updated.
2. ✅ Removed own definition, and actually this was wrong: ->getSourceFieldDefinition($type->getSource()); It should be taking the media type
3. ✅ Restored the custom submit handler and added the logic there.
5, 6, 7. ✅ Updated.

Status: Needs review » Needs work

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

oknate’s picture

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

This fixes the test failures locally:

-      
+
+      $view_mode = NULL;

After I changed this line:
$build = $media && ($view_mode || $view_mode_id === 'default')

Can someone smart explain why you can do this:
$build = $media && $view_mode

When $view_mode isn't set, but you can't do $build = $media && ($view_mode || $view_mode_id === 'default')?

The latter throws an undefined variable error while the former does not.

oknate’s picture

Here's a small update to use the EntityDisplayRepositoryInterface::DEFAULT_DISPLAY_MODE constant.

Also posting a fail patch for the bugfix found in #126. I think I might be need to break it out though, because there are some tests changes that might give additional failures without the other changes in the patch.

oknate’s picture

Here's a better set of patches to demonstrate the bug found in #126. To state the bug directly: "MediaEmbed filter does not support the default entity view display". You can easily test this by either:

  • Setting data-view-mode to 'default'
  • Change the Default view mode for media embeds to "Default" on your text format.

Either will result in the missing media indicator displaying when you'd expect to get the embed rendered by the default entity view display.

This is because there isn't a 'default' view mode for media (only a display, not a view mode) and this code in the MediaEmbed filter requires one:

$view_mode = $this->entityRepository->loadEntityByConfigTarget('entity_view_mode', "media.$view_mode_id");

oknate’s picture

Same as #129, except fixing one cut-and-paste error where I had the same test case twice. I meant to copy it and do it once with the default view mode and once with the full view mode. This fixes that.

The last submitted patch, 129: 3066802-129--FAIL.patch, failed testing. View results

oknate’s picture

Here's some manual testing with
1) Demo Umami
2) Media Library
3) DrupalMediaLibrary button enabled
4) An image media where the raw image is a 5 MB image (https://upload.wikimedia.org/wikipedia/commons/2/2d/Snake_River_%285mb%2...)

Test result: Large image style is used for embed out of the box on standard profile. 👍

snake river

Video:
https://www.drupal.org/files/issues/2019-09-13/demo-umami-default-image-...

The last submitted patch, 130: 3066802-130--FAIL.patch, failed testing. View results

oknate’s picture

Manually tested with
1) Standard install of 8.8
2) Media + Media Library
3) DrupalMediaLibrary button
4) same large 5 MB image from #133

Test result: Large image style is used for embed out of the box on standard profile. 👍

five MB image embedded, displays with large image style, preventing raw image from being served.

Video:
https://www.drupal.org/files/issues/2019-09-13/standard-default-image-vi...

phenaproxima’s picture

Status: Needs review » Reviewed & tested by the community
Issue tags: -Needs followup
Related issues: +#3081225: Make embedded media responsive by default, +#3081227: Image media should not use an image style on its canonical route

I...have nothing to complain about in this patch. Well done! We've worked hard, tested like crazy, found some bugs, pivoted, and it's just generally been a roller coaster. I think the dust has settled and we are ready to move on. This is already a big improvement from what's in HEAD.

Also opened follow-ups as per #113 and #121.

phenaproxima’s picture

Title: Ensure that embedded media displays sanely by default » Ensure that embedded image media items do not display at unreasonable sizes by default
Issue summary: View changes

Re-titling for accuracy, and updating the IS.

phenaproxima’s picture

In reviewing the code, I realized one thing: when you create a new Image media type (i.e., with the "Image" source), this patch will ensure you get a nice, clean default display with only the source field visible. And that is very good.

However, that niceness does not extend to any other media source -- new media types that don't use the Image source will display a bunch of other fields as well (in addition to the source field). That's both inconvenient and inconsistent, but in discussion with @oknate, we decided it is not in scope for this issue. This issue really is just about image media, and ensuring that they behave in a sane way, since they have some considerations that other media types don't, like the possibility of inadvertently downloading massive files. (That's why I re-titled this issue in my previous comment and spruced up the issue summary.)

I've opened #3081233: The default display for new media types should only show the source field to fix this for all new media types. It should be quite simple for us to address, and test for; it's just not in scope here.

webchick’s picture

Status: Reviewed & tested by the community » Fixed
Issue tags: +8.8.0 release notes

Agreed with #3081233: The default display for new media types should only show the source field being out of scope for this issue; this one is specifically trying to address the "huge downloads of doom" issue for images.

HUGE thanks to those screenshots/videos. Makes product management review SO much easier! <3

+    // When a new media type with an image source is created we're configuring
+    // the default entity view display using the 'large' image style.
+    // Unfortunately, if a site builder has deleted the 'large' image style,
+    // we need some other image style to use, but at this point, we can't
+    // really know the site builder's intentions. So rather than do something
+    // surprising, we're leaving the embedded media without an image style and
+    // adding a warning that the site builder might want to add an image style.
+    // @see Drupal\media\Plugin\media\Source\Image::prepareViewDisplay

I love the attention to detail with this comment, since it's otherwise pretty confusing why we're doing all of this work. +1.

Great job on the test coverage here, too.

Not much to complain about here; the code is doing what we talked about it doing. And nice that we're fixing a long-standing problem in image styles to begin with as a result.

Soooo... committed and pushed to 8.8.x. Thanks!

I'm tagging this for a release notes mention, since it is a behaviour change from 8.7 (though presumably one that will be very welcome to folks' server bandwidth ;)). @catch's feedback is addressed, in that there isn't anymore an update path; instead folks that have this condition will get a warning message in the admin panel telling them to go address it.

  • webchick committed 97811f7 on 8.8.x
    Issue #3066802 by oknate, anmolgoyal74, phenaproxima, Wim Leers,...
Wim Leers’s picture

  1. To state the bug directly: "MediaEmbed filter does not support the default entity view display".

    🤔 This is a bug in the way Entity View Displays work, not in Media Embed. Apparently it is not, but this is just super weird in Entity API. Not this issue's fault though. The changes look sensible, and the unit tests for the filter were updated, excellent! Ideally, this would've been fixed in a separate issue, but I think it's sufficiently simple to have been committed as part of this patch :)

  2. Thanks for #133 and #135, those make reviews much easier! EDIT: hah, that's what @webchick wrote in #139 :)
  3. #138: I'm … surprised by the descoping of non-image media source media types. IMHO the goal of this issue was to make embedding of arbitrary media look/be formatted as expected. I'm fine with tightening scope, but then the follow-up needs to also be at least major, and also should be a must-have. As an absolute minimum, we need this to work well for YouTube videos out of the box in 8.8. I see #3081233: The default display for new media types should only show the source field is not "major", but "normal", so bumping priority.

Thanks for pushing this across the finish line, @oknate and @phenaproxima!

Status: Fixed » Closed (fixed)

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

pameeela’s picture

Issue summary: View changes

Added release note snippet.

xjm’s picture

Issue summary: View changes
xjm’s picture

Issue summary: View changes

Updating to clarify that this is any Media images attached to content (not anything to do with embedding the media item in CKEditor nor non-Media images).

xjm’s picture

Issue summary: View changes

Oh, actually, it's both in the content and the WYSIWYG. Fixing that again.