Problem/Motivation
This is a follow-up to #2940029: Add an input filter to display embedded Media entities, and the second step of #2801307: [META] Support WYSIWYG embedding of media entities.
The first part is a @Filter plugin, that's what #2940029: Add an input filter to display embedded Media entities provides: Drupal stores media embeds in a structured way using <drupal-media> tags, that are transformed on output. This way, Drupal stays true to its ethos of structured content.
The second part is to make these <drupal-media> tags have a great authoring experience. We don't want content authors to have to look, understand and manipulate that markup: that'd be a usability nightmare.
Fortunately, for this very reason the CKEditor team developed the CKEditor Widget system — which is also what powers Drupal 8's image support in CKEditor today (see #2039163: Update CKEditor library to 4.4).
In a nutshell, the Widget system allows us to have a storage representation (structured content, yay!) and an editing representation (to offer a great editing UX); the corresponding CKEditor plugin is responsible for this mapping.
The scope of this issue is solely the visual representation of an existing <drupal-media> tag; generating <code><drupal-media> tags by selecting from the Media Library is the scope #2994699: Create a CKEditor plugin to select and embed a media item from the Media Library!
Proposed resolution
Just like in #2801307: [META] Support WYSIWYG embedding of media entities, we should stabilize the existing CKEditor integration of the https://www.drupal.org/project/entity_embed module.
It should:
- talk to the server to generate an accurate preview of
Mediaentities using the front end (default) theme's templates - offer exactly the same great in-place caption editing UX as we currently have for images (core's
DrupalImageCaptionCKEditor plugin) - offer exactly the same linking support
- have its code reviewed & approved by the CKEditor team to ensure long-term maintainability and stability
- ensure a good UX even on high-latency connections: either (a) entering and leaving source mode or (b) saving the entity and then editing it again should not cause network roundtrips to fetch media previews. This is also to ensure parity with the existing image support: that is rendered entirely client-side, but that's impossible to achieve for arbitrary media entities. Hence the importance of this and it meriting a bullet here!
Remaining tasks
Stabilize https://www.drupal.org/project/entity_embed, port over all test coverage, to ensure a maximally stable new CKEditor plugin in core.- Reviews!
Convert to✅Done.*.es6.js- Follow-up blocked on a future CKEditor release: undo support is present, but sometimes a single user action requires triggering "undo" multiple times. This is due to bugs in CKEditor itself, which they're working to fix. See #3060245: [upstream] CKEditor's "undo" functionality not working correctly for embeds: offers multiple undo steps where there should be only one and #3063964: [upstream][PP-2] Editing an embedded entity and submitting the `EntityEmbedDialog` without changing anything results in an unwanted "undo' step.
- Follow-up blocked on a future CKEditor release: mask support, to prevent interaction with the embedded media. See #3064572: Add mask to prevent interaction with embedded media.
User interface changes
None.
API changes
None.
Data model changes
None.
Testing steps
Testing steps
- Install latest 8.8.x
- Apply the patch
- Go to Administration => Configuration => Content authoring => Text formats and editors
- Select a text format (e.g. Basic HTML)
- Check " Embed media" under "Enabled filters"
- Add
<drupal-media data-entity-type data-entity-uuid data-align data-caption>in "Limit allowed HTML tags and correct faulty HTML" if the filter enabled and "Save configuration" - Go to node add article (
/node/add/article) - Click the "Source" button in CKEditor
- Add
<drupal-media data-align="center" data-caption="The first core Media embed ever." data-entity-type="media" data-entity-uuid="84911dc4-c086-4781-afc3-eb49b7380ff5"></drupal-media> <p>and</p> <drupal-media data-align="center" data-caption="This media entity is missing" data-entity-type="media" data-entity-uuid="wrong"></drupal-media> - Click the "Source" button in CKEditor again. Now you should see:
- Now just like in #2940029: Add an input filter to display embedded Media entities's testing steps, save the article, and you should see:

Good preview, isn't it? 🙂 - Now go back to edit it, and observe how the preview loads instantly — just like in https://www.drupal.org/files/issues/2019-06-28/media_embed%20cached%20pr...
| Comment | File | Size | Author |
|---|---|---|---|
| #226 | interdiff-225-226.txt | 577 bytes | effulgentsia |
| #226 | 2994696-226.patch | 84.72 KB | effulgentsia |
| #225 | interdiff-222-225.txt | 6.42 KB | effulgentsia |
| #225 | 2994696-225.patch | 84.7 KB | effulgentsia |
| #222 | 2994696-222.patch | 84.44 KB | oknate |
Comments
Comment #2
phenaproximaComment #3
legovaerComment #4
phenaproximaWhat else is this blocked on? Please add a related issue before changing the postponement counter.
Comment #5
phenaproximaNever mind, just saw #2994699-4: Create a CKEditor plugin to select and embed a media item from the Media Library.
Comment #6
wim leersComment #8
wim leersAs of #5, this has been marked as postponed on #2994699: Create a CKEditor plugin to select and embed a media item from the Media Library. I understand the original reasoning, but … we have to have a
@Filterplugin first, that's what #2940029: Add an input filter to display embedded Media entities does. If we want to do the filter first, initially one would have to craft the HTML manually anyway (and note that this won't ship with a Drupal release as-is, we'll want to complete this and #2994699 too in the same release!). So, then it makes much more sense to have the same thing be visible both in a rendered blog posts (/node/1has a visible embedded image media item thanks to the filter) and in edited blog posts (/node/1/edithas a visible embedded image media item thanks to a CKEditor plugin).So, swapping the order.
Ironically, this means that the first sentence in the issue summary is once again accurate: 🥳
Comment #9
wim leersComment #10
wim leersHere we go! 🚀
<drupal-media>HTML tag is upcast by the CKEditor Widget into a visual representation closely matching that on the front end (a preview).\Drupal\ckeditor\CKEditorPluginContextualInterfaceit is automatically enabled whenever@Filter=media_embedis enabled.P.S.: I know that this still needs to be converted to
*.es6.js, butbrew install npmbroke my PHP, because https://github.com/Homebrew/homebrew-core/issues/39567 🤷♂️ 🤯🤬I don't want to waste time on fixing my dev environment again. We can do that in a future patch iteration 🙂Comment #11
wim leersFoundations are green :)
Now let's add the next important feature:
In the previous patch, the caption is visible (thanks to the server-side rendered preview), but it's not yet editable. That's what this interdiff adds.
Again with test coverage of course — note that Drupal core's existing captioning support for images does not have any test coverage!
Comment #12
wim leersStill green! ✅
Time to add the last feature:
Now you can select the embedded media and press
CTRL+K/CMD+Kor click the "Link" button, to make the embedded media link somewhere.Again with test coverage. And again Drupal core doesn't have any test coverage for this. (It predates us having JavaScript tests.)
Comment #13
wim leersAnd still green ✅🥳
With regards to
I can share this, from @kkrzton, the CKEditor 4 Lead Developer:
So we're in a good place. Many thanks to the CKEditor team for their feedback and advice!
Comment #14
wim leersI want to provide a little bit of context:
This is copied verbatim from
drupalimage. It can be confusing to those who don't know this code (it was to me after not having seen this for about five years), but this was written by the CKEditor team themselves.This can presumably be simplified by modifying the
drupalimageCKEditor plugin to make fewer assumptions. Then all this code could go away. Theentity_embedcontrib module of course could not do this, but here we can 🙂Comment #15
phenaproxima'media.filter.preview' seems like a somewhat generic name for the route. How about 'media.embed_filter.preview'?
I think this should be a BadRequestHttpException, since technically the URL is "found", it's just being given garbage parameters.
Nit: Should we specifically check the POST data for 'text', rather than just $request->get()?
WebDriverTestBase already uses ContentTypeCreationTrait. :)
Should be protected.
Nit: This ellipsis is a specialized character; we should probably use three ASCII periods instead.
😲
Comment #16
wim leers#15:
mediamodule add?\Drupal\editor\EditorController::filterXss().Comment #17
wim leersThis uncovered two bugs in CKEditor itself by the way; those will need follow-ups.
Comment #18
phenaproximaProbably only this one, but if we ever add another one, the route name will become ambiguous. Renaming it would make it more clear anyway -- I still think we should change it.
Hmmm...I guess I don't feel too strongly about it, but it still seems kinda like security-by-obscurity to me.
Comment #19
oknateFor #15.4 and #15.5, I added an issue to Entity Embed to fix those in the corresponding test. #3064846: [META] Port good ideas from core media embed to entity embed
Thanks, Phenaproxima.
Comment #20
wim leersI think we can change it at that time. The route name says it's for the
mediamodule'sfilter, namely topreviewit. Hencemedia.filter.previewI don't see what's ambiguous about that?I think the suggested
media.embed_filter.previewroute name is worse, because there is nothing namedembed_filter. If you want to disambiguate between different filters, thenmedia.filter.media_embed.previewperhaps makes sense. But … this route does not actually do anything specific to themedia_embedfilter at all. Its controller docs even say exactly that!But perhaps
media.editor.previewwould be a better name? Because it's about previewing in the context of a text editor.I don't feel strongly about the route name either, but if we're going to change it, I think we should all agree it's clearer. Hopefully this comment helps us get there 🙂
Comment #21
wim leersOther important context:
data-captionattribute yourself right now).data-alignattribute yourself right now).Why?
This is blocked on design and UX team sign-off. See #2994702-12: Allow editors to alter embed-specific metadata, as well as `data-align` and `data-caption` for details. See #2994702-13: Allow editors to alter embed-specific metadata, as well as `data-align` and `data-caption` and #2994702-14: Allow editors to alter embed-specific metadata, as well as `data-align` and `data-caption` for patches that add support for this.
In other words: we're not punting on this, it just doesn't make sense to be included in the scope of this issue.
Comment #22
wim leersA few rounds of feedback have been incorporated into #2940029, rebasing #16 on top of #2940029-91: Add an input filter to display embedded Media entities.
Comment #23
wim leersRerolled #16 to match #2940029-97: Add an input filter to display embedded Media entities's marking of
data-view-modeas optional.Comment #24
wim leers#2940029: Add an input filter to display embedded Media entities landed, unpostponing and rebasing 🥳
Comment #25
wim leersOne last self-review now that I haven't looked at this in a few weeks:
This still needs to be converted to
*.es6.js. Last time I installed the necessary tooling, my dev env stopped working. I'm traveling next week. I am hence very hesitant to do this myself right now. If anybody else would like to do that, that'd be splendid 🙏(This hasn't happened yet in the Entity Embed contrib module because apparently https://www.drupal.org/node/2815083 doesn't work for contrib, only for core. #2957390: Use ES6 for contrib and custom JavaScript development exists to bring this to contrib too.)
Now that this is being brought to Drupal core, it probably makes more sense to instead make the
drupallinkplugin not be as tightly coupled todrupalimage.Done. ✅
This comment needs to be moved one line. ✅
C/P leftover. Fixed. ✅
🤔
This arguably should not be done here, in the
drupalmediaCKEditor plugin, but in\Drupal\ckeditor\Plugin\Editor\CKEditor::buildContentsCssJSSetting()directly. But … this only actually becomes relevant once Drupal-rendered content is embedded in CKEditor. So that arguably means this is correct.I don't feel strongly either way, let's find if reviewers have strong opinions about this :)
Nit: this could useArgh, but becauseFile::setFileUri().File::setFileUri()has a broken implementation, we actually can't. Never mind 🙄Comment #26
wim leersUpdating IS to match changes made in #2940029: Add an input filter to display embedded Media entities. #23 should already have done that.
Comment #27
wim leersHere too I want to make sure all people who ever contributed are credited. People who contributed to Entity Embed's
js/plugins/drupalentity/plugin.js, which is the spiritual predecessor:Comment #29
wim leersThe failures are in
SkipOpTest, which was added yesterday by #2982684: Add a composer scaffolding plugin to core.Looks like a random failure:
Retesting.
Comment #30
oknateFor what it's worth. The test CKEditorIntegrationTest isn't passing on my local.
and
I can fix these errors with these changes:
I think these changes might make the test more robust. I sure would like it to work on my local. I certainly think the first change is harmless enough:
For the second one, with the Byte size, can you think of why that might be different for me? I'm just running it with chromedriver with php PHP 7.1.26 .
Comment #31
wim leers#30:
ElementNotVisibleerror: interesting that it passes both for me and testbot locally and it's not new test coverage, it's coming from Entity Embed! Is Entity Embed's CKEditor integration test coverage passing for you?I guess your computer must be running the test significantly slower, but it's certainly better to change
>elementExists('css', 'drupal-media')->click()towaitForElementVisible('css', 'drupal-media')->click(). Go ahead and make that change! 👍913 is greater than 1024— hah, this 1024 byte thing was fairly arbitrary, but it seemed like a lower bound (since I'm test withd8.testas my domain, no subdirectory, so it can't get much shorter than that). Apparently I underestimated how much shorter it can be. Let's change it to >500 then to err on the safe side.Comment #45
phenaproximaCrediting people Wim listed in #27.
Comment #56
phenaproximaContinuing with that. It's a lot of people!
Comment #60
phenaproximaLast batch.
Comment #61
oknate@wim: re:
No. Some of the entity embed tests don't pass for me. You recently changed it from waitForElementVisible to elementExists. I'm suggesting to change it back in one place I encountered this. See #3064846.5. https://www.drupal.org/files/issues/2019-06-29/3064846-5.patch
If you're on a slower system it takes a bit for the preview to display, I guess.
Comment #62
oknateUpdated patch based on #30 and #31.
Comment #63
wim leers#61: 👍
#62: 🙏 One nice bonus punctuation nitpick you fixed there! 🤓 (FYI: give interdiffs the extension
.txtto ensure DrupalCI doesn't try to test them. Yes this is weird, but it's the convention we have, and it works, once you know it :)Comment #64
phenaproximaVery cursory review -- looks pretty good to me! The JavaScript parts are very complex and will take some serious brain time to puzzle out. Since there's a bunch of CSS and JavaScript changes here, tagging for front-end framework manager review.
Nit: I think this could be a const, since I don't think it will be reassigned. Also, it could probably use a comment to explain what it's for.
No description for the param?
Nit: I think we could use registeredLinkableWidgets.includes() here, since it's an ES6 feature and should be automatically transpiled back to indexOf() for older browsers.
I think all of these could benefit from comments explaining why these display values will work as expected.
This, and the other helper functions, would benefit from explanatory comments.
integrateWithLinkCommands()seems like a more straightforward name for this function.Should we also allow things like B and I tags here? How about span or button, for even more exotic cases? (i.e., visually-hidden bits of text, or a button disguised as a link...)
If we inject the extension.list.module service, we can avoid using drupal_get_path() here.
Comment #65
phenaproximaNow with more ES6!
Comment #68
wim leers#64:
+1, but remember that in #13 I shared that the CKEditor 4 Lead Developer @kkrzton already reviewed this code; his suggestions were incorporated. In fact, I should credit him too!
drupalimage/plugin.js.drupalimage/plugin.jsand in other CKEditor plugins: https://github.com/ckeditor/ckeditor-dev/search?q=linkcommandintegrator&.... Let's stay consistent.drupalimagecaption/plugin.js. Let's stay consistent.So I think I made points 1–5 and 8 actionable, I think points 6 and 7 do not require changes.
Comment #69
phenaproximaAddressed Wim's feedback in #68 and fixed the broken functionality. Turns out I got some
thisscoping wrong. :)Comment #70
effulgentsia commentedI've only started reviewing this, so this is incomplete, but here's what I found so far...
Can we add an additional access requirement that the Media Embed filter is enabled for this format? E.g., if anonymous users only have access to formats that don't allow media embedding, then there's no reason to let them execute this controller.
Can we change this to
$request->query->get('text')? Or is there a reason that we need the flexibility of$request->get()?Comment #72
wim leersComment #73
oknateI don't see a reason an anonymous user should ever be able to access the media.filter.preview route. What about adding
_user_is_logged_in: 'TRUE'?How about a new operation,
_entity_access: 'filter_format.preview'Then update FilterFormatAccessControlHandler to specify for preview access you must be logged in and have the same access as the 'use' access.
I don't think a specific access check for whether the filter_ckeditor_media_embed filter is enabled is necessary, from a security perspective, as long as the route is protected. I think it's fine for it just to not render the embed if the filter isn't enabled, if only users with the appropriate permission can access the route.
If we add another entity operation, site builders won't have to set it every time they create a filter format.
Comment #74
oknateHere are some options for protecting the route with a new access configuration.
I noticed that filter_format.use is normally not given to anonymous users, but it can be. By explicitly preventing anonymous users from hitting this route, we add additional security.
I'm leaving out the es6 changes for now, until those are fixed. Apologies to @phenaproxima. I want to test these access changes, and I don't know es6 yet.
Comment #75
wim leers#73:
The anonymous user may be able to use a text editor (for example for posting comments) and hence be able to use a format that includes media embeds. They may even be allowed to select media from the media library!
Adding a
previewoperation for filters may be possible too, but what does that mean? What would the semantical difference betweenpreviewandusebe?It isn't necessary strictly speaking, but we err on the side of caution and safety in Drupal core. Having one more route/controller be accessible to users that can use a text format (which could be the anonymous user!) means one extra potential attack vector. This is why @effulgentsia's proposal to neutralize this potential attack vector by denying access if the relevant filter isn't in use by the format makes sense.
Comment #76
lauriiiI started reviewing the patch. Unfortunately, I got stuck reviewing the 9 lines of CSS on the patch. Given the complexity around that, I thought I'd post the problems I found here to see if anyone else have time to trial and error more on how to solve this. I'm planning to continue reviewing other parts of the patch next week.
The styles must work in all iterations of data-attribute, as well as without it. Currently, there's some margin set to a child element so that it's rendered inside the border. The margin should be instead outside of the border. Whatever styles we come up with, should presumably rely on the frontend theme styles so that the CKEditor content matches closely what can be seen on the page.
Here's how this looks currently:

Comment #77
bnjmnmThere's one part of #76 that I'd like to get a bit more clarification on:
I know that frontend theme styles can be provided to ckeditor via the theme's
ckeditor_stylesheets:property in its info.yml file, but it seems like this approach would largely benefit Bartik (and Bartik-subtheme) users. Is there perhaps a different approach I'm overlooking that would more easily integrate this with a wider variety of themes?If not, adding it to Bartik will still be beneficial even to non-bartik users, as they'll have something in the codebase to reference if they want to include this in their own themes. I'm interested in knowing if theres a more elegant contrib-friendly way to incorporate some of those FE styles in CKEditor.
Comment #78
wim leersFWIW: I had a call with @lauriii earlier today to walk him through what the challenges are. In a nutshell: #68.4 + we cannot change the DOM structure (which involves two wrappers) + CKEditor Widgets have a
cke_widget_focusedstyle that adds anoutlinethat should work. The challenge is that due to the wrappers, theoutlinelooks slightly weird, like @lauriii demonstrates in these screenshots.#77: That's why this CSS does not live in a theme, but is loaded into CKEditor as part of the CKEditor plugin:
I'm sure @lauriii meant that the CSS should not be coupled to Bartik, just like the current CSS is not. You can find inspiration in
core/modules/media/css/filter.caption.cssfor CSS tweaks that are theme-agnostic yet are able to remove margins and padding that a particular theme is adding.Comment #79
oknateReworking the preview route access. Adding custom access check to check if media_embed filter is enabled on the given filter format.
Adding one unrelated coding style fix. I don't think semicolons belong in the places I changed, and the coder linter was complaining these lines don't end in a period. Perhaps those should be colons, if the point is to indicate that the sentence refers to the example code below.
Comment #80
wim leersNit: s/account/user/
Nit:
The text format for which to check access.Nit: Extraneous
\n.This should instead do:
Because that removes the need for the
cachePerPermissions()and theaddCacheableDependency()calls.Nits:
In both cases, you're modifying the default situation. This requires the reader of the test to know about the default situation created in
::setUp().I think it'd be clearer if this always created a new text format and always created a new user, then there's no need for overrides and understanding things outside this test.
Why is this line necessary?
Comment #81
lauriiiThis comment doesn't take into account that we allow this to be placed inside
aelements at all. Could be great if we could explain why we want this to behave like adiv, with the exception that it is also allowed to be placed insideaelements.Could we explain somewhere why we require drupal-media to include data-entity-type attribute if we only expect it to have a single value?
For better consistency across Drupal, we should convert the current selectors used for targeting the drupal-media element into following BEM and CSS classes.
"to allow for smarter decisions" could use some extra clarity.
Should we handle scenarios where the HTTP request fails as well? Currently, if the request fails for some reason, there's no indication at all for the user that something is missing or that something went wrong.
Comment #82
lauriiiThis is a bit confusing because there's no explanation at all about what went wrong. Was there HTTP error or is there something wrong with the content?
Comment #83
oknateUpdating patch based on Wim's review in #80:
1) ✅
2) ✅
3) ✅
4) ✅Much nicer handing of the access results, and makes total sense.
5) ✅I checked before I added patch 79 and "logged in" and "logged-in" are both used as adjectival form in core. But changing, per your recommendation. Why are they called text formats if the machine name is FilterFormat? I guess, because that's how it's done?
6) ✅I reworked the test to use a new text format.
7) 👍You're right, that line wasn't necessary. It's been refactored away now. But I tested, and because the object is passed by reference, that line wasn't necessary.
Comment #84
oknate@laurii, in regards to #82. The motivation for the placeholder was that it was invisible and therefore you couldn't focus on the broken embed to delete it. It has alt and title text to indicate something has gone wrong. Are you suggesting there should be some visible text as well, indicating what the issue is?
Comment #85
lauriiiIf users are unable to remove broken embeds, how are they supposed to deal with broken embeds? I'm also wondering if this is something that should already happen with the current patch? For some reason, I'm able to focus on broken media and delete it.
Having a title and alt attributes do help deliver the message. If we would make the same text visible also in the UI, it would be even clearer and easier for users to understand why the media isn't rendered as they would expect. 🙂
Comment #86
oknateI guess I wasn't clear:
The point of adding a broken image indicator was, before the current patch (back in the original version of the filter in entity_embed module), the embed just wouldn't render, so there was nothing in the editor to allow focus. The current patch has the image indicating that the embed is broken because the entity can't be loaded.
That's what the placeholder image allows.
Comment #87
wim leers#81
dtd['a']…line.data-entity-typeattribute was raised there. Would you like a comment such as// Match the behavior of the corresponding filter plugin.?#82: this relates to #81.5 — I agree this could be improved. But let's be clear: this is an exceptional case. The
altandtitledo explain what is wrong. The content author must be able to go in and make their one change without having to click through multiple error notifications and without being forced to fix this. But they do need to be able to fix this.#2982322: Mark missing embedded entities in WYSIWYG is the issue that did this. The rationale:
Perhaps we just need a better "missing media" image?
#83.5: because
FilterFormatis the internal code-level name, "text format" is the name of the concept. Just like we say "content type" in the UI and "node type" in the code.Comment #88
lauriii#87.3: we are currently using the element type and data attribute for attaching styles to the element. Instead, we should be using CSS classes.
#87.5 there's no indication that something went from. All the user will be able to see is white.
#87: I agree that we shouldn't have error notification or anything like that. I think it would be enough to just display the current title/alt text on the screen along with the image. Something like this:
Comment #89
phenaproximaI discussed this whole "Ghostbusters" snafu with @lauriii and @Wim Leers on Slack.
Here is a summary of the problem:
imgtag, because that will breakfilter_captionif the media item hasdata-captionset. According to Wim, the resulting HTML would look like this:<figure><img><figcaption>foo</figcaption></figure><p>MESSAGE</p>. Not great.data-captionattribute to say something like "Missing media", because if the text format doesn't havefilter_captionenabled, it won't show up (i.e., we'd be introducing a hidden dependency onfilter_caption).Seems like a pickle. Wim did suggest something like this as a possible solution:
This solution met with no real complaints from @lauriii, but it does present two sticking points:
contentproperty does not support HTML.However, both of those sticking points have solutions too:
contentproperty like so:content: attr(data-missing-media-placeholder).MDN confirms that attr() is extremely well supported by browsers: https://developer.mozilla.org/en-US/docs/Web/CSS/attr
However, this would still need manual testing and screenshots to confirm that it works correctly in all our supported browsers. That said, it's the best idea we have and we should sally forth with it.
Comment #90
wim leers+1 to #89. I didn't know about is true — looking forward to seeing that 😀
TIL this is an idiom and not a typo: https://www.merriam-webster.com/dictionary/sally%20forth
Comment #91
oknateAdding back phenaproxima's es6 changes from #69 (minus changes to drupallink, which was the source of the test failure).
This combines
Comment #92
oknateHere are the changes removed from 69 that will need to be re-examined and reworked.
I'm still trying to remember when I make interdiffs manually not to put .patch extension.
Comment #93
oknateHere's an experiment to add a message to the missing media indicator without having to use a creative solution (see #89 for suggested creative solution). Perhaps I'm missing something, but when testing locally this seemed to work fine.
Comment #94
oknateHere are two screenshots of changes in patch #93:
With caption:
Without caption:
Comment #95
wim leers#91: I am very confused by that "69-91" interdiff. That includes most of the changes I made in #25, which were already in #69?
#92: again, the non-ES6 changes were made by me as early as #25.
#93 + #94: Hm … interesting. I'm not a fan because this generates different markup: it's no longer
<img>but<div><img><p></div>. But perhaps this is what it takes. It looks acceptable to me as a visual user … but I think a screen reader user would not be happy to hear the exact same information twice. I think we could solve that by removing the<p>from the accessibility tree. Evidently I'm concerned we will need a new round of accessibility review, because the current approach already incorporated feedback from the accessibility maintainer in #3063705: Improve `alt` and `title` attributes for missing entity indicator after they gave feedback in the first core patch that went in, for the filter: #2940029: Add an input filter to display embedded Media entities.Looking forward to @phenaproxima's feedback and especially @lauriii's.
Comment #96
oknateThat 69-91 interdiff must be off. I think because I removed the changes to the drupallink plugin.js, they showed up in the interdiff. Ignore that interdiff.
I tracked the failure to this bit of code:
if you change
to
the error goes away.
After some sleuthing I found the issue:
The es6 in #25 doesn't transpile to the working js. Wim said as much in #25, and we have just missed it for several comments.
Comment #97
oknatesigh, ignore this one, this has some changes from 69 on the 25 branch.
Comment #98
oknate98 adds aria-hidden on the p tag. I tested adding on the image too, but decided to go with the p tag.
I also added some of the comments Lauriii recommended. #81.1 and #81.4.
Comment #99
oknatein #25 Wim wrote:
This lead to the drupalink javascript file in #69 to #98 to be either out of sync or wrong (failing). This patch fixes that, it changes the drupallink es6, so that when transpiled, it matches the js.
Comment #100
wim leers#97: Thanks for that separate interdiff, that makes it much easier for me to do a comprehensive review :)
#99 Thanks for figuring that out!
Needs a blank line in between. (I'm surprised
phpcsdoesn't complain?!)Needs blank line to be removed.
I'm not a fan of the fact that we're modifying the filter in this patch. #2940029: Add an input filter to display embedded Media entities just introduced it. But the only thing we're changing (besides punctuation nits in comments) is the missing media indicator.
The importance of that indicator becomes more important in this issue, so refinements being made here is reasonable.
Why would this only be relevant in CKEditor? This is also relevant when it's displayed to the end user. Because the filter is what generates this message, and hence this markup also is displayed to end users.
So this should be moved to
filter.caption.css.Actually … if we keep both
_entity_access: …and_custom_access: …, then we can simplify the callback to just check if$media_embed->status === TRUE. And then the callback can be renamed to::formatUsesMediaEmbedFilter().Because all requirements are
ANDed.Comment #101
wim leersOnce my #100 remarks are addressed, the patch looks ready to me. Next steps:
Comment #102
oknateAddressing feedback from #100:
1 👍
2 👍
3 I'm not a big fan of changing the missing media indicator either. I think the ghostbusters logo is sufficient. We put a lot of work into making sure that was bug free for Entity Embed. To change it here last minute here is less-than-desirable.
4 Moved the css rule. UPDATE: This needs work now. It's no longer center-aligned, neither in the CKEditor, nor when the page is saved.
5 👍 Updated the access on the route to use two requirements.
Comment #103
oknateComment #104
oknateFixing the css for #100.4. I checked and filter.caption.css seems to be loaded even if filter caption is disabled for the format, because the stable theme is loading it.
I feel there's something wrong with the way we're loading the css rule for the missing media here. It should to be tied to the media embed filter, rather than the caption filter. That way, if a theme doesn't have filter.caption.css it still works.
UPDATE
I can confirm, if your theme doesn't override filter.caption.css and filter_caption is not enabled for a text format, the missing media embed's p tag is not text aligned centered. So we need a different way to attach it.
Comment #105
oknateContinuing to work on the css for the revised missing media indicator. I found the center alignment no longer works if you turn off filter caption (and if you're not using a theme that includes it, such as stable). Also, the width of the missing-media div would go full screen without filter caption on and a caption present. So I borrowed some of the css from filter.caption.css and applied it to the .missing-media div. I added a new library for this.
While the filter caption library is tied to the rendered media embed, it is not tied to the missing media indicator. So we'd have to add the caption library to missing embed indicator as well. Which would work, but seems like not the quite right place for it. If you have disabled filter caption to require its css for a completely separate element that seems odd.
Comment #106
oknateThis is the same as the last patch (#105), except that I had forgotten to delete one css rule:
Comment #107
oknateI missed this interdiff on the last comment, accidentally uploaded the same interdiff twice.
Comment #109
oknateI changed my mind about adding a new css library. When I saw the failure from #106 I realized I'd have to add it to the stable theme as well. And then I realized that I'm adding two css files right along media/filter.caption.css. So now I feel the redundancy is worse than the lack of accuracy of the name of the media/filter.caption.css (I was feeling it was inaccurate because it now affects more than just the caption). I do think we could rename media/filter.caption.css to media/filter.media_embed.css, I don't think it's absolutely necessary. And since this css file is already added to 8.8.x, I'd rather not start a discussion about renaming unnecessarily. It could turn into a red herring / bikeshed from the goal of getting this RTBC. So going back to Wim's original suggestion that I add it to filter.caption.css. It wasn't working, because I needed to add it to stable as well. Now that I am adding it to both the media/filter.caption.css in the media module and in the stable theme, the changes take affect properly. But we do need to add it to the wysiwyg.
as well as the render array for the missing media:
This takes care of this:
This allows it work both in the wysiwyg and when viewing the page after saving.
Comment #110
oknateThis still needs addressing:
I started working on it. I think Lauriii wants these rules changed to use classes:
This is a bit of a heavy lift, I believe, as the drupalmedia plugin.js has allowed attributes, the text format / editor form has validation of the allowed attributes on the drupal-media tag and CKEditor is manipulating the class tag to add "cke_widget_element".
I'm thinking we could change them to classes:
.drupal-media--align-left
.drupal-media--align-right
.drupal-media--align-center
but allowing those classes to persist on the drupal-media tag might require changing a lot of code.
After looking into this a bit more, I don't think we'll need to change the validation on the text format / editor form. Also, they don't need to persist as they are only in the wysiwyg and can be removed on downcasting.
So the plugin.js could add these classes to the widget, if the data properties are there. These rules don't persist out of the wysiwyg.
Comment #111
oknateAddressing #87.3 (and #110). It would be good as a follow-up to this patch to add some simple test coverage that the css styles in this patch are applied.
Comment #112
oknateThis patch adds:
Comment #113
phenaproximaDidn't find much to complain about. Virtually all of these are minor points.
I think this can be a
const, since it will not be reassigned. (In ES6,constvariables are mutable, but not re-assignable.)widgetName needs a description.
Why does this need
display: table? That seems like an unusual choice :) So if this was the only way to get it to work, maybe this could benefit from a comment explaining the rationale.We can combine these lines into one: "Makes embedded media items linkable by integrating with the drupallink plugin."
dtdshould be aconst, since it won't be reassigned.This line could use a comment.
Can we say "...corresponding server-side text filter plugin", to make it clear we're talking about something running within Drupal?
This line is longer than 80 characters.
I'm not sure we need
const widget = this, since arrow functions bind to the lexicalthisautomatically. (Maybe try changing it locally and see if tests pass.)captionEditableMutationObserveris a bit of a long-winded name. How about justcaptionObserver?I'm not sure we need to do the
hasOwnProperty()check. According to MDN, it's basically just a harmless no-op if wedeletea key that doesn't exist; see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operat...Same here. This can just be
if (dataToHash.link) {...}.I think @lauriii was on to something -- we should do some sort of error handling, perhaps, if the HTTP request fails. Even if it's something as simple as setting the element's HTML to
Drupal.t('An error occurred while trying to preview the media.');Let's rephrase this a bit, as it's a little confusing. How about "This is an internal part of the media system in Drupal core and may be subject to change in minor releases. This class should not be instantiated or extended by external code."
I think we should mark this class
@internalwith the same wording as the preview controller. Additionally, both CKEditorPluginContextualInterface and CKEditorPluginCssInterface extend CKEditorPluginInterface, so there's no need to implement that one explicitly.Ideally, we should inject the
extension.list.moduleservice and use it to get extension paths.Why aren't we using
::has()in MediaFilterController::formatUsersMediaEmbedFilter()?Looks like we're using
drupal_get_path()in the DrupalMedia CKEditor plugin, but hard-coding the path to the Media module here. Can we make it consistent (ideally by injecting theextension.list.moduleservice)?Nit: It's a little more readable to set
$assert_session = $this->assertSession();at the top of the test method, then call methods on$assert_sessionthroughout.Comment #114
phenaproximaSent this to @lauriii and I don't think we have addressed his feedback in #76 yet.
Comment #115
phenaproximaI demoed this to the UX team yesterday as well. @andrewmacpherson was present and voiced no objections to the markup generated inside the editor for a missing media item. Therefore, I'm removing the "needs accessibility review" tag.
Comment #117
phenaproximaCrediting @andrewmacpherson for his input.
Comment #118
wim leerssetStyle()calls in the CKEditor Widget JS and deleting the CSS file altogether than adding additional classes for nicer selectors. That'd make it more explicit that this is an implementation detail.\Drupal\media\Controller\OEmbedIframeController. If we're changing it here, then let's also update that one.Comment #119
oknateThis patch addresses feedback from #113. I'll address #118 separately.
#113.3:
In regards to this rule:
I'm not a FE dev. I pulled this from /core/modules/filter/css/filter.caption.css.
I tried changing it to other common display options and none allowed the element's width to shrink to fit its contents. I'll leave it until a FE dev comes up with something better.
This is how I was testing if you want to give it a look: Put a "sleep(500)" statement into CKEditorIntegrationTest::testLinkability() after the embed is rendered in the CKEditor, then press the "source" button and edit the uuid so it's invalid.
Comment #120
oknateHere's a screenshot of #85.5. (#113.3)
There's some disagreement about whether we should do this. If for some reason the embed (or missing entity indicator) fails to render, such as a fatal error in a preprocess function or a server becoming unresponsive, this will give feedback to the user. I see it as a minor change and a net positive as it will handle an edge case we haven't handled.
There's no test coverage for this. This is how I was testing if you want to give it a look:
throw new NotFoundHttpException();at the beginning of MediaFilterController::preview()Comment #121
oknateAdding back separate css file for the .missing-media css per #118.2.
Comment #122
oknateFor #81.3, which was implemented in #110 and disagreed with in #118.
In #118 Wim writes:
I can see your point. They are single use rules, not to be re-used, so there's no point adding reusable classes, and it's somewhat clearer to have it in a css file, but it does give the impression that these are rules. I think more comments could allow us to remove the classes and go back to the original. I think before I change or revert this, Wim and Lauriii should come to agreement as to what to do here.
I like it in a css file. It's easier to see the rules laid out and separated from the data manipulation. So I'd rather not use setStyle calls(). I'd rather either keep it as is now, or revert it over that.
Comment #123
lauriii#118 related to #113.13: The problem I was trying to point out is that without explicitly handling the error scenario, there's no visual hint what so ever that something has gone wrong if the preview fails to load. Without explicitly handling errors, the UI would have a little bit of empty space instead of a preview whenever an error occurs. It's hard to predict how users would react to that, but it's hard to imagine that they would at least at first associate that to a system-level error. Instead, they might expect it to be something wrong with their content, and they would try to take action to solve that. This issue is related to this problem: #2987444: Ajax errors are not communicated through the UI.
Comment #124
ckrinaI totally agree with @lauriii with the need of giving more information/feedback to the user to improve the usability of the content authoring experience.
So for #82, this icon is not enough feedback. First, because if it will show up when the referenced media has been deleted, it means the source is missing. And actually this icon usually stands for “forbidden”. Maybe we could try to use a “broken link” or similar representation. Apart from the icon itself, some more info should be given, specially because we have it and we can point out the user to solve the issue.
So I’d add an explanatory text similar to “The referenced media source has been deleted.”
#81.5 (and answering Wim’s #87.5)
That is exactly one of the most frustrating experiences when editing content: ghost errors that you don’t know how to solve. So we should try to clearly communicate:
a. That something went wrong.
b. How to solve the current situation.
c. Warnings about what can go wrong so they have a chance to decide before doing anything.
d. Ideally, what has gone wrong. Probably difficult to know here.
So I’d suggest, as mentioned in earlier comments, that we show a message as implemented in #120 adding some instructions:
“An error occurred while trying to preview(load?) the media source. Save you content to avoid data loss and reload this page.”
Comment #125
oknateAddressing #118.3, I present two options, the third being no change.
My concern with setStyle is that, if someone needs to override these settings for some reason, it is much harder to do that. My concern with reverting the changes is, Lauriii feedback that we should use classes instead of data attributes to attach css will not have been addressed.
Therefore my opinion is the best option here is option 3, followed by option 2.
The patch numbers are off due to another comment coming in as I was adding mine.
Comment #126
oknateUpdated patch for reverting BEM classes change, see #125. I had forgotten to remove the functional js test.
Comment #127
oknateIn regards to this in #124:
If the media to be embedded cannot be loaded based on the uuid, we can't tell if it's been deleted, or has never been added. Therefore, we have to use a more generic message "Missing media". I think the reason it's missing, while valuable, cannot be ascertained with confidence.
Comment #128
oknateAddressing #118.5, updating @internal comment in OEmbedIframeController.
We still need to address #76.
Comment #129
lauriiiI agree with this. However, the reason I would prefer using CSS selectors for styling is that if the default solution doesn't work well with someone's custom code, they would be still able to override the styles with a reasonable amount of effort needed. #125.1 and 3 both seem like valid solutions to me. Do we have a prediction on the likely hood of someone having to override these styles?
Maybe we should then say something like "The referenced media source is missing. To solve this, the media has to be re-embedded from the media library."
Comment #130
oknateHere's an attempt to address #76.

It looks good in the wysiwyg with Bartik. But I noticed after posting this, it is off after saving when using align-center.
This change was a personal thing. It doesn't make a difference for the test:
I was getting annoyed by how small the image was in the test.
Update, I think it did make a difference for the test. The image is now "image-1.png" instead of "image-test.png"! I'll revert that change.
Comment #131
oknateContinuing to address #76:
Comment #133
oknateGoing back to original image in the test. It did make a difference.
Comment #135
oknateFixing assertions around asset libraries in the Kernel test MediaEmbedFilterTest. Including an interdiff to before I started changing css (#128).
Comment #136
phenaproximaBoy, there really was not much to complain about in this patch. I think I only have two overriding concerns which block RTBC.
Nit: This should have a trailing comma (which is allowed by ES6).
I'm not sure we can rely on .cke_widget_* classes, since AFAIK they're generated by CKEditor and are internal to it.
Overall, the organization of the CSS in this patch feels confusing to me. I think we might want to refactor some of it and move it into fewer style sheets. I will try to secure a dedicated front-end developer's time to handle that.
Nit: This comment should end with a period. :)
Isn't this logic basically identical to getFocusedLinkableWidget() in the drupallink plugin? If so, can't we remove this function, call drupallink's registerLinkableWidget('drupalmedia'), and leave it at that?
I think we should add comments explaining what both of these event handlers are for.
I don't think this should invoke the callback. The callback is probably expecting the preview to have loaded successfully (per the documentation of _loadPreview()), which means that it will be thrown for a loop if, in fact, an error occurred.
Nit: For readability, let's change all calls of
$this->assertSession()to a single$assert_session = $this->assertSession()at the top of each test method, then call methods on$assert_sessionin the rest of the test.This, and all similar waitFor*() calls throughout the entire test, need to be wrapped by $this->assertNotEmpty(). (The reason being that they return NULL if they fail, but don't actually cause any sort of assertion error make the test fail.)
Comment #137
wim leers#123: agreed that it's related to #2987444!
#124: Thanks for chiming in here! 🙏😊
#129:
#130: This seems to be making many more changes to CSS than what's necessary to address the margin-related concerns in #76?
#136:
drupallink's function because it's not exposed as a public API. But … you're totally on to something! Instead of havingdrupalimageanddrupalmediareact toexecandrefreshevents on thedrupalunlinkcommand, we can just have thedrupallinkplugin do that generically now, thanks toregisterLinkableWidget()! Well-spotted sir 🦅👁Comment #138
oknateUpdating based on feedback in #136:
I need a little more guidance on this. 🙏
If implement Wim suggestion in 4, we won't need to add comments here, as they'll be refactored away.
Also, @phenaproxima and I talked in the #media channel today, about the scope of the css:
During this conversation I realized that some of the styles that were in filter.media_embed.css could go into ckeditor.drupalmedia.css, since the later only affects the ckeditor, and the styles in question only needed to affect the ckeditor.
Comment #139
oknateI added a @todo in #138:
I haven't removed the line because it seems harmless, and it seems better to leave the attributes in sync. Maybe @wim could remember if this is only needed for the css, if so, we can drop this line.
Comment #140
oknateWim writes:
You're probably talking about this:
The problem is that when the item was centered, it was using left-margin: auto and right-margin: auto to center the item. This meant that the margin and the alignment were coupled. In order to fix the margin showing inside the outline, we needed to switch to a new way of aligning the element, in order to get this effect:
instead of the blue outline here having a lot of white margin around the centered item:
Comment #141
oknateSmall coding style fix.
Comment #142
oknateHere I'm trying to implement #138.4. It seems to work fine for drupalmedia for 'exec' function. I'm not sure how to test the 'refresh' function. I also need to test drupalimage changes.
Comment #143
wim leers#138.2: -1 on this
.drupal-media-wrapper, see my explanation in #137.#138.4 + #142: That's exactly what I meant! 🥳 The
refreshevent is invoked whenever the cursor position changes, because that requires button states to be refreshed. You will notice that if you move the cursor onto a linkeddrupalimageordrupalmediawidget that the "unlink" button becomes triggerable instead of disabled. That's what this does :)#139: Yes, that was added only for the styling: that's what the comment says. We did that in #3064287: [regression] Follow-up for #3037316: alignment doesn't display until source button pushed.
#140: Indeed, that's why. I'm okay with this if @lauriii signs off on it. I've never seen
justify-contentbefore and https://developer.mozilla.org/en-US/docs/Web/CSS/justify-content indicates that browser support is not complete. But perhaps this subset does have sufficiently wide support. Thanks for the super clear explanation by the way! 👏Great work here, @oknate! Just since last night, I think most feedback has been addressed. My primary remaining concern is #138.2 and the "AJAX error" UX as it currently works (see the screenshot in #120).
Next steps AFAICT:
.cke_*classes instead of adding our own, because this functionality is CKEditor-powered, so it's silly and even misleading to add our own classes. If the functionality is CKEditor-coupled, the CSS should not make it seem as if it's not.Comment #144
oknateAddressing #137:
This looks pretty good, but it seems weird to include a theme file in the filter's css. Any suggestions on how to get this styling in the ckeditor without including the classy theme file in the DrupalMedia CKEditor's ::getCssFiles() method?
Also, restoring function getFocusedWidget to drupalimage plugin, due to error message triggered by this code:
This also made me realize that it could break BC to remove that function. We don't offer the same public api for drupalmedia. Perhaps we should? I'm not sure what the rationale for drupalimage was.
Comment #145
oknateHere's an effort to improve the visual display of the missing media indicator. I took inspiration from how the icons look in the media library. I also tried out a new icon (using photoshop and editing one of the existing media icons). But decided not to add it to the patch, because there's a separate issue for that: #2944631: Improve media icon that represents "No thumbnail available", and I don't want to derail the focus here.
With current icon:

With new icon (not included in patch):

See #2944631: Improve media icon that represents "No thumbnail available"
Also, I was looking into it a bit and I think "justify-content: flex-end" is unsupported in ie11, and Drupal 8 officially supports ie11. So we probably need to replace that way of doing right-align, or add an ie11 work-around. I will leave that to those who make things work in ie11. I don't have access to ie11 currently.
Comment #146
oknateAdding test coverage for drupalunlink button integration.
Comment #147
oknateSince we're refactoring the drupalimage plugin's js, I felt it was worthwhile to add some test coverage for the unlink functionality on that widget too. Also, I don't see any test coverage for it in core.
As I was adding this, I noticed that our test function testLinkabilityWhenDrupalImageIsAbsent was trying to remove the DrupalImage button from the editor, but we didn't have a DrupalImage button there! This means running testLinkability() running twice wasn't testing two different scenarios, and that the code block to remove the DrupalImage button was on a snipe hunt. I refactored this to use a data provider and I added the DrupalImage button by default to the test. It now runs the testLinkability test twice, once with DrupalImage button enabled and once without. It also, tests the enabling of the unlink when focused on the DrupalImage widget, and in that context clicking on the DrupalUnlink button will remove the link from the DrupalImage element.
I also removed the wait within assertEditorButtonDisabled() and assertEditorButtonEnabled(). The random failures I thought I was experiencing there are not reproducible today.
Comment #148
lauriii#143.2 I'm not a CKEditor expert so I'm likely missing something. I'm having a hard time understanding why you think that it's silly to provide our own CSS to the element. To me, it seems reasonable to customize the CSS of elements added by us based on our requirements 🤷♂️ I'm not outright against using inline styles but I don't understand why it would be worthwhile taking a risk that someone would be having a hard time overriding that CSS.
#143.3 This looks much better already. I haven't manually tested all variations but I noticed that the styles are broken when data-align is provided:

#143.4
That seems like a rare edge case (feel free to correct me if I'm wrong). Isn't that something that would only occur while the content sync is still in progress? Maybe we should get confirmation from UX or product but I think it might be acceptable for this text to not support that rare edge case. The reason I think this way is that we can help a big group of users by using a more specific message. If we used the generic message, it means that everyone suffers.
Comment #149
effulgentsia commentedYep, it has, thanks!
#70.2 has not been yet. The response to that was:
However, that is incorrect.
EditorController::filterXss()calls$request->request->get(), not$request->get(). So EditorController is opinionated on the data coming in via the POST body. For MediaFilterController, we need to decide whether we're expecting a GET or a POST, and change to either$request->query->get()or$request->request->get()accordingly. Or if there's a reason we want to support both and let the JS choose which one to use, then we need to document that reason.I think it's ok for MediaFilterController to choose GET even if EditorController is choosing POST, because for EditorController, the input can be of arbitrary length, so can exceed the length limit of GET parameters. For MediaFilterController, the JS is currently choosing GET, which I think is the semantically correct choice, and since the expected string is just the
<drupal-media>element, I think we can assume that we're safely within GET length limits. Unless we're concerned about attributes likealtanddata-captionpushing us beyond GET length limits?Comment #150
phenaproximaI've become very confused by the last chunk of comments on this patch. It feels like the scope is wiggling a bit out of control and it's no longer clear to me where the path to committing this issue lies.
So I got on the horn with @oknate and we had a quick "state of the union" call to itemize everything we need. This is where we landed:
@todocomment in the code) to add test coverage and unify the linkability stuff later.Agreed. Let's use GET for now. It is trivial for us to allow POST as well later on if that proves to be necessary.
Comment #151
oknate#150.3: Adding test for AJAX error handling.
One small thing after I posted this:
should be
Comment #152
oknate#150.6: Reverting refactor suggested in #136.4. I have opened a follow-up issue for the refactor: #3069525: Refactor to remove drupalunlink event listeners for registered linked widgets.
Comment #153
ckrinaWaw, it’s almost impossible to follow you! You’ve been busy! 👏
1. #143.4 What about just saying something actionable like: "The referenced media source is missing and needs to be re-embedded from the media library." or "The referenced media source is missing and needs to be re-embedded.” I still think "Missing media" is not enough, sorry.
2.
#150. It would be great to have feedback that looks different from the regular text, so using messages styling would be ideal… but right now they are not prepared to work with icons (see message 4).
3.
#150. What happens if the admin theme has different styles from the Classy’s ones? Can we rely on admin themes following the needed pattern?
4. #150 Also about this message, as I said it'd be great to have a different styling for error messages that makes them stand out from the content. But it should be the same for all of them. As an editor I won't know if one is loaded via AJAX or not. They are both messages, so they should look the same.
Comment #154
oknateAddressing #148.2: Fixing
It turns out there is a bug where attributes from drupal-media element are carried over to the embed. This process was removing the 'missing-media' class. I put an exception for 'class', because it seems destructive to overwrite the class of the embed. Another option would be to merge the classes. Since filter_align adds a class to the drupal-media element, it was getting carried over by overwriting the attribute. I put an exception for the 'class' attribute, and added test coverage that the missing media indicator has the 'missing-media' class.
Comment #157
oknateFixing the test failure, I noticed that there's test coverage for the align classes being added to the embedded media, which lead me to look into it further and sure enough. If you don't pass classes such as 'align-right' on to the embedded media, they won't align right outside the WYSIWYG after saving. So this patch restores those, and merges the classes on the embedded element with those from drupal-entity element.
I also noticed that the flex-box model doesn't work for displaying text to the right of an image (See screenshot). We're only using this in the CKEditor, but I think we'll need to go back to closer to what we had in #25 for the alignment in the CKEditor, in order to get the text on the left. I changed the styles to use flexbox to try to fix the outlines showing whitespace (margin) inside. So now we'll need to try to revert the flexbox stuff and fix the outlines another way.
Here's a quick test you can use to manually check the css styles, just throw this into CKEditorIntegrationTest.
An unrelated change I threw in: I copied classes that were missing to the the stable theme:
Comment #159
oknateThis patch fixes the bug found in #157 with the css:
Also, it removes the flexbox css:
justify-content: flex-end;and
justify-content: flex-start;This should help make the styles more ie11 compatible.
Comment #160
oknateAddressing #153.1:
This required some additional styling too. I had to put a width constraint on missing media as it was shrinking to fit the image before.
Comment #161
oknateComment #162
oknateComment #163
oknateComment #164
oknateComment #165
oknateRemoving the label from the missing media, per request from @phenaproxima:

I don't know why drupal.org just said I commented three times or four times. Weird.
Comment #166
phenaproximaI just had a very pleasant call with @lauriii and @ckrina to figure out how we want to present the "I/O error" -- i.e., a generic AJAX error when the media preview fails to render for whatever reason.
The conclusion we reached is that, although it would be nice to be able to use the admin theme's error styling, there is no real way for us to do this reliably with current core mechanisms. So we will open a follow-up for that.
In the meantime, though, we need three things:
Comment #167
oknateAddressing #166: This patch simplifies the missing media indicator by removing the image and replacing it with a background image. This means the message doesn't need to be hidden from screen readers (as it did when it was duplicating the alt and title text).
Then, we're updating the i.o. error to use the missing media indicator's markup, which removes the issue where we were using the classy theme and adding it with the plugin.
Comment #168
phenaproximaOh man, this is looking so very, very good. First-class work from everyone all around!
Both of these should be
const, since neither is reassigned. :) Also, shouldn'terrorinclude the phrasing "Please save your work and reload this page"?The filter is called
media_embed, notentity_embed. :)As per @effulgentsia's feedback in #149 and mine in #150, let's use
$request->query->get(), and update the doc comment to match ("Throws an exception if 'text' parameter is not found in the query string").This is out of scope for this patch, but I don't especially object to it. So, whatevs :)
This is no longer needed, so we can remove it :)
Similarly, we no longer need this either :)
I think, for flexibility, we should alter the CSS classes we use. Both types of errors (missing media and I/O) should get a
drupal-media-errorclass, or something similarly named. Then we should also have something likedrupal-media-error--missing-sourcefor missing media, anddrupal-media-error--preview-errorfor I/O errors.The contents of this if statement could use a comment explaining why we are merging all the classes together.
This should happen outside the foreach() loop.
It doesn't look like $this->state is used for anything. Also, since this is test code, we don't need to bother with proper dependency injection in this class. :) So let's just remove all this.
Pro-tip: it's preferable to use
$route->setDefault('_controller', ...)here, so that we don't override any other relevant defaults that may be present.We should not do this in tests, lest the container change during the course of it (this happens a lot). Instead, just call
$this->container->get('state')in the test as needed, rather than keeping a persistent property around.Generally we should use assertSame() rather than assertEquals().
assertWaitOnAjaxRequest() is dangerously unreliable. Can we instead wait for a concrete condition, like some particular text appearing, or an element being visible or invisible?
Nit: Extra blank line here that shouldn't be! :)
Nit: All of these calls can be chained. Editor::load()->setSettings()->save().
If findField() returns NULL, we'll call setValue() on NULL and this test will die with a fatal error. This should be $assert_session->fieldExists()->setValue() instead.
As I mentioned before, it's best for us to NOT use this method if we can avoid it. It's more reliable to simply assert the expected condition.
$additional_attributes should be type hinted as an array, and default to [].
Why did this change?
I think this could probably be $this->assertSession()->elementsCount().
We should always pass TRUE as the third argument to in_array().
This can be greatly simplified, I think:
$assert_session->elementAttributeContains('css', '.missing-media', 'class', 'align-' . $additional_attributes['data-align']);Comment #169
andrewmacpherson commentedRe: #115 -
Removing the tag confused me, so I lost this issue. I can find issues which have the "needs accessibility review" tag, but I won't find issues where that tag has been removed by someone else.
There is indeed an accessibility issue with the markup -
aria-hiddenandtitleattributes are being used inappropriately here:I think the
aria-hidden="true"is being used to avoid repeating the phrase "Missing media" to screen reader users. That's fine, but this markup is over-optimizing for a blind person controlling their screen reader with a keyboard, at the expense of other scenarios.The
aria-hidden="true"attribute should be used very rarely. In general it should never be applied to text which is actually visible on the screen. Screen readers can be configured to read whatever is currently underneath the mouse pointer, so partially-sighted users can read the page by pointing at the text they are interested in. People with dyslexia sometimes resort to using a screen reader this way too. Screen readers can also be configured to read text by selecting it. If visible text has thearia-hidden="true"attribute, it can cause both of these methods to break.The image
titleattribute is also getting in the way here. It duplicates thealttext, and some browser/screenreader combinations will read both of them. It also duplicates nearby visible text, so it's really not doing anything useful.We should change it to this markup:
Here, the image has no text alternative, but that's OK. This pattern is known as "image with adjacent text alternative".
Comment #170
andrewmacpherson commentedOh wow, I don't think my concern in #169 applies anymore, after the changes in #167. That patch appeared while I was writing my a11y feedback.
Comment #171
andrewmacpherson commentedPatch #167 has CSS table properties:
It's not a good idea to use CSS table display properties, if it isn't intended to be understood as a table. These have been found to have an affect on the semantics conveyed to assistive tech via the accessibility tree, in some browsers. See Using display:table has semantic effects in some screen readers.
Update: There are some conflicting reports too, in WCBuf: CSS Display Properties versus HTML Semantics and Tables, CSS Display Properties, and ARIA. So I'm not sure what the current situation is exactly.
It would be good if we can avoid using CSS table properties for this.
Comment #172
oknateAddressing feedback in #168.
1. ✅changed let to const
2. ✅fixed two entity_embed references.
3. ✅
4. ✅See #118, this was a request by Wim:
5. ✅ removed $themeExtensionList from DrupalMedia.
6. ✅ removed $moduleExtensionList from MediaEmbed.
7. ✅ Changed css names, although I modified the names to "media-embed-error". The reason why is that drupal-media is specific to CKEditor only, it's the name of the CKEditor plugin, while MediaEmbed is the name of the filter and it applies both in the CKEditor and after saving. We have a css class filter.media_embed.css that holds the css for the missing embed indicator's css. So for these reasons I think this name makes more sense.
8. ✅ added a comment about why we're merging the class attribute.
9. ✅ updated to move setting of attributes to empty array if not set outside for loop although, I don't think it matters in this context whether it's in the for loop. But I agree it's better stylistically.
10. ✅removed state.
I call \Drupal::state() lower on the page. I forgot to update that to $this->state, I guess. But I went ahead and removed it. If dependency injection isn't important here, it will make it less likely to break when the base class's dependencies are changed in the future.
11. ✅👍Good point.
12. ✅ Replaced $this->state with $this->container->get('state') in the test.
13. ✅Changed to assertSame() instead of assertEquals().
14. ✅ Replaced assertWaitOnAjaxRequest() calls.
15. ✅removed whitespace
16. ✅updated to use chaining.
17. ✅ Addressed in the next comment (#173).
18. ✅ Replaced assertWaitOnAjaxRequest() calls.
19. ✅fixed type hint for array.
20. In regards to this change:
$this->applyFilter() only applies the media_embed filter. The reason this changed is that processText allows you to choose which filters you are applying. I added a few tests in ::providerMissingEntityIndicator() including one where I don't use all of the filters. I removed filter_align for one test when I was working on fixing the issue where it was stripping the 'missing-media' class when filter_align was enabled. The test demonstrates how the attributes are added to the embed. In this patch I changed the assertion to this. the "no filter_align" test was a baseline for comparison when I was fixing the issue. The following assertion is where I test the class attribute is being merged correctly, preserving the missing media indicator's class and adding the align class from data-align property added by filter_align:
Update: I improve this code below in #174.
21. 🤔 How do you use assertSession() in a Kernel test? It doesn't seem to be a method out of the box?
22. ✅passing TRUE as the third argument to in_array()
23. ✅fixed below in #174.
Comment #173
oknateAddressing #168.17:
If findField() returns NULL, we'll call setValue() on NULL and this test will die with a fatal error. This should be $assert_session->fieldExists()->setValue() instead.Comment #174
oknateAddressing #168.23, simplifying assertion of alignment class in missing media indicator.
Comment #175
oknateAddressing #171: Removing "display: table" css that has led to some very interesting concerns about accessibility I didn't know about. This was something I added when I was trying to fix the outlines, which is no longer necessary now that we've simplified the markup of the missing embed indicator.
I also was missing a change for #168.7, where I'm updating the css style name for the missing media indicator.
Comment #176
oknateThis is a minor change, but something I've been wanting to do for a while. I've found in this thread it's more useful to follow the pattern Wim established with MediaEmbedFilterTest::testMissingEntityIndicator() and call the missing media method in the MediaEmbed filter an indicator.
I think the new version is more accurate and more useful. You can't actually render the missing media. You are rendering something to indicate that the media is missing. We have been using this terminology for a while to refer to it, so this just brings it in line.
Comment #177
phenaproximaExcept for one CSS-related thing, we are down to supernits. This is really close to being ready. Amazing job with this, @oknate!
Question, looking at this CSS: I'm not clear on why we wouldn't simply have the embedded media always carry the
drupal-mediaclass, both inside and outside of the editor. If there is an error, it could also carry a class likedrupal-media--error. I think this might simplify the CSS substantially.core/drupal.ajaxalready has explicit dependencies oncore/drupalandcore/jquery. Therefore, we only need to mentioncore/drupal.ajaxhere.To elucidate my point about simplifying the CSS: if we make that change, I think the classes would look like this:
['drupal-media', 'drupal-media--error', 'drupal-media--missing-source'].Minor but important phrasing thing: can we put parentheses around "or the missing media indicator"? This will make it clear that we're either dealing with an embed, or we're dealing with a missing media indicator.
Nit: We don't really need $merged_classes as its own variable here.
"handing" should be "handling" :)
Supernit: 'ajax' should be 'AJAX', since it is technically an acronym :)
Nit: the callback function should not be on a new line after the wait time. So this should look like
$page->waitFor(10, function () use ($figcaption) { ... });This can actually be simplified, because $assert_session->elementExists() can take a "container" element. So, you could do this instead:
$assert_session->elementExists('css', 'a', $figcaption)->click();This is odd syntax; optional arguments, like $additional_attributes, all need to come after required arguments, like $verification_selector.
Comment #178
oknateAddressing feedback in #177. After I posted this, I got additional feedback on the classes. See below. I cancelled the test since I posted #179 a few minutes later.
Comment #179
oknateAddressing feedback in #177, take two.
1. ✅ Updated the classes, see 3 for more info.
2. ✅ Removed:
3. ✅ Updated the classes, but with this additional feedback from you in the #media slack channel:
Similarly, the I/O error should have `media-embed media-embed--error media-embed--preview-error`
The missing error should have `media-embed media-embed--error media-embed--missing-error`
4. ✅Added parentheses.
5. ✅Removed variable, although I hate really long lines. Either way it's functionally the same. I'll take your suggestion.
6. ✅fixed typo, s/handing/handling, found two other places in core where there occurs, but left them be as they are out of scope.
7. ✅ fixed /s/ajax/AJAX
8. ✅fixed indentation
9. ✅fixed nit
10. ✅Updated the parameters to move $additional_attributes after required attributes.
Comment #180
oknateI noticed that the drupalimage integration with drupallink adds a context menu, but the same integration for drupalmedia wasn't adding a context menu for the "Edit Link" and "Unlink" context menu items. So here's a fix for that:

Also, when I re-transpiled the drupalmedia plugin.js, I saw that I had missed one of the css changes from the last patch (#179) (see attached screenshot to see the bug)
Comment #181
oknateThis patch adds test coverage for the context menu items provided by drupallink plugin added in #180.
Comment #182
oknateFixing minor cut and paste issue: irrelevantly named variables.
Comment #183
oknateHere's a minor @todo. There's a coding standard error found by the testbot:
Comment #184
oknateFixing coding style issue in #183.
Comment #185
oknateIgnore this patch, I forgot to pull the latest changes to my working branch, so it has a lot of extra garbage in it undoing commits.
Comment #186
oknateWe have added a lot of useful CKEditor testing helper methods, and now that I have started to work on test coverage for the next issue for media embedding, I'm realizing that allowing these methods to be reused in other tests would be valuable.
Therefore in this patch, I'm moving the CKEditor testing helper methods into a trait.
/core/modules/ckeditor/tests/src/FunctionalJavascript/CKEditorTestTrait.php
Also, since the CKEditor might have a different instance name, I'm parameterizing it in the new methods added in #181, so it's not hardcoded like this:
- $open_context_menu = "var editor = CKEDITOR.instances['edit-body-0-value'];Wim had already established that pattern in 25, I had just forgotten about this when adding the context menu test coverage in #181:
protected function waitForEditor($instance_id = 'edit-body-0-value', $timeout = 10000)One minor @todo: "Constructs a new DrupalMedia filter plugin object." should be something like "Constructs a new DrupalMedia CKEditor plugin object."
Comment #187
oknateAddressing minor @todo in #186:
(This isn't a filter.)
Comment #188
oknateComment #189
starshaped@phenaproxima and I paired to refactor the CSS for embedded media inside CKEditor. The end result is a little more verbose than what was there previously, but the decisions in there are documented and we were able to remove the !important, which should generally be avoided if possible. New patch attached, plus screenshots of how it looks with all combinations of alignment and captioning. This was tested on a Mac, in the latest version of Opera.
Comment #190
oknateThanks for the updated css.
One thing, line 47:
* display and center it as if it were textshould end in a period.Comment #191
phenaproximaYeah, this is pretty much ready for RTBC in my opinion. I have a few nitpicks, and the only major change I think we need is to walk back what I now think is a premature abstraction introduced in #186. I think that having a CKEditor test trait is a good idea in general, but it may carry implications that are well outside the scope of this patch. So let's revert that for the time being, and move your good work into a new patch filed in a follow-up issue.
Although copy-and-pasting code sucks, and I understand and fully agree with the rationale here, I think that abstracting all this common functionality into a trait may be premature and will raise committer eyebrows. It might be a smarter idea for us to keep the methods isolated to the relevant test class(es) for now, and then open a follow-up to move them into a trait. What say you?
Is there any particular reason for choosing
inline-block, especially considering that filter_align adds floats in the.align-*classes? If so, we should probably document that rationale in a comment.I'm surprised that filter_align doesn't have margins already!
Nit: There's an extra blank line here that shouldn't be.
Rather than use
let menu = {}and overwriting it, let's just return stuff as needed:Not a big deal, but why did we change these? I thought the semicolons were perfectly clear...
Nit: This seems like it could be streamlined a bit. How about:
RTBC once all these things are addressed.
Comment #192
oknateAddressing #191:
1. ✅Removed the added trait.
2. The rationale here is to mimic image2 plugin's behavior. Perhaps this: https://github.com/ckeditor/ckeditor-dev/blob/master/plugins/image2/dev/... The last style? When you remove the style on the drupal-media element, the outline breaks, either stretching to the full-width or not enclosing the contents.
3. ¯\_(ツ)_/¯ I guess enforcing a default could be seen as a negative. Personally, I'd rather it look ok out of the box.
4. ✅Removed extra space.
5. ✅Dropped variable.
6. I have a good reason for changing the semicolons to colons here. I've been running the code through coder module's sniffer:
phpcbf '--standard=vendor/drupal/coder/coder_sniffer/Drupal/'The change to periods makes the sniffer happy and I don't think it changes the meaning.
7. The order of css classes matter. The 'media-embed' class should go first, I think. In the case of the ::renderMissingMediaIndicator() build array, it would lead to this order 'media-embed--error media-embed--missing-source media-embed' rather than 'media-embed media-embed--error media-embed--missing-source'. I could rework the code to add the class here:
And then change the code adding the 'media-embed' class to:
That would put the 'media-embed' class at the beginning of the missing media indicator's classes, but after any existing classes on the media embed. What do you think?
Also, I'm adding credit for ckrina for her feedback.
Another small change:
This failed for me on a slower computer.
Comment #193
oknateI'm not sure how these unrelated changes crept in, removing:
Comment #194
oknateThis is also a coding style change that is unrelated to the focus of the issue, so removing.
Comment #195
phenaproximaI think we are pretty much done here. The patch has been thoroughly put through its paces; bugs have been found, fixed, and tested; the test coverage is very extensive; the UX team has reviewed the work, and their changes have been implemented; the front-end framework manager has reviewed this (although it might need a re-review, we'll see); and a backend framework manager has reviewed as well.
I don't think we need to do more iterating here; the resulting functionality is stable, attractive, and usable. Further tweaking can and should happen in follow-ups. On to the next stage. RTBC!
Comment #196
lauriiiWhy are we setting margin-bottom when aligned to left and right, but not when aligned to the center?
This file should be run prettier and eslint.
Markup should be wrapped in a theme function.
I don't think we should be adding this class globally. This class is not essential to the functionality. We could create a new template for this, and add the classes in Classy.
Comment #197
phenaproximaI discussed #196 with @lauriii and @oknate to clarify what's being requested in the fourth point. I was pretty unclear on the purpose of Classy versus Stable, so this really helped sort out my thinking:
media-embedclass. At all. It doesn't truly add anything, so let's remove all mention of it from core.media-embed--errorshould becomemedia-embed-error(note the removal of a hyphen); it should be, from a CSS perspective, its own distinct entity. The related "state" classes should be renamed accordingly.media-embed-errorclass(es) would not be applied at all (i.e., they wouldn't even be mentioned in the markup). However, making that happen would require us to do some weird backend acrobatics which are not worth the time or effort, so @lauriii said he would not block commit on this point.Comment #198
effulgentsia commented#194 is looking really good to me. I don't have any framework management concerns with it. Just the following nits:
Why do we need to do this? Isn't the JS code already implementing a cache via
_previewNeedsServerSideUpdate()? If this additional caching is needed on top of that, let's add code comments explaining it. If we retain this, then I'm also curious if we want to make _previewNeedsServerSideUpdate() respect this value, since currently it looks like it can return FALSE even if more than 5 minutes has passed.If a module or theme is overriding these, I'm assuming we want those overrides in the editor too? In which case, we should get these values from the library info.
This warrants a theme hook, so that themes can override the markup and classes.
Comment #199
phenaproxima@effulgentsia agreed that this can be done in a follow-up: #3071713: Make error messages for embedded media themeable.
Comment #200
effulgentsia commentedTo clarify, here's what I mean...
Inject the
'library.discovery'service into DrupalMedia. Then in getCssFiles(), instead of$this->moduleExtensionList->getPath('system') . '/css/components/hidden.module.css', do this...That's for 'hidden.module.css', because we don't want the rest of system/base.
But for
filter.media_embed.css, I think we should return all of the items that get returned in$this->libraryDiscovery->getLibraryByName('media', 'filter.media_embed')['css'].Comment #201
oknateSee #206, this patch was missing a change.
Comment #202
oknateTriggering tests.
Comment #203
oknateSee #206, this patch was missing a change.
Comment #206
oknateAddressing #196.1, adding margin bottom.
#200, #198.2 👍✅- Added $this->libraryDiscovery->getLibraryByName. Note, since we're dropping the 'media-embed' class (#196.4), I'm dropping the 'media/filter.media_embed' library. The only thing left was the .media-embed-error styles, and since those aren't going to applied in stable, only in classy, I created a library in classy for that, and left it unstyled in stable (per #197.1).
#196.2 I ran eslint and prettier. I'm more of a BE dev. So there were some es lint errors I wasn't sure what do with. I can research them, but it might be better if someone tells me which of these need fixing. Many I can figure out myself, such as "Missing JSDoc". I'll work on that tomorrow.
#196.3 - addressed in #199 and follow-up issue to add theme template for missing media indicator.
#197.1 ✅ - .media-embed-error replaces .media-embed--error, and it's only styled in classy, not stable.
#197.2 ✅ - dropped .media-embed class
#197.3 ✅ - .media-embed-error replaces .media-embed--error, .media-embed-error--preview-error replaces .media-embed--error--preview-error
#197.4 ¯\_(ツ)_/¯ - I'm not sure how we'd strip these classes in stable. There's no .theme file, and I'm not sure what hook we could use anyway.
#198.1 Here's the work Wim did to set this up, it may help answer the rationale behind it. #3064340: Make preview responses cacheable to accelerate previews. One example is when the "source" button is clicked. The previews go away, someone edits a small change and clicks the "source" button again, without the caching of the preview route, it would have to regenerate the preview.
Comment #207
phenaproximaCode looks pretty good...a few minor things. Also, given the changes we've made here, I'm marking this for front-end framework manager review again.
Superdupernit: We can remove this extra blank line, I think :)
Not a big deal, but I think having this method is a bit too "generic". I would propose we move all of this logic into getCssFiles(), and simply hard-code the name of the file we're trying to extract (hidden.module.css), and the extension (system) and name of the library (base) from which we're trying to extract it. We could potentially also take advantage of preg_grep() to locate the file, in the library's CSS list, rather than doing interesting substr() and strlen() acrobatics. :)
We should add a @todo to the doc comment:
I thought this library no longer exists...??
I think we should call this 'media_embed', just in case we later decide to add more stuff to it.
Comment #208
lauriiiIs there any downside if we apply this rule always to .media-embed when inside .media-embed-widget?
Can .media-embed be rendered outside .media-embed-widget? Could we apply this rule more globally so that this is applied even when .media-embed isn't necessarily rendered inside .media-embed-widget?
Nit: you can remove the last 20px. CSS will shorthand it so that the value given for right side is also used for the left side if it doesn't have its own value.
I don't think this library is loaded at the moment because these styles aren't showing up when testing with Umami.
What are these empty
elements?
This is really strange. Could we just duplicate the rule needed for now and open a follow-up to come up with a better solution?
Comment #209
phenaproximaThanks, @lauriii! Let the kung-fu fighting begin :)
Whoops! Looks like we forgot to remove all mentions of .media-embed. :)
@oknate discovered this while testing. It's a weird quirk of CKEditor and can be hacked around, but the hack itself would probably be kinda controversial and would drag this issue out even longer. I propose we open a follow-up to fix this there.
Comment #210
oknateAddressing feedback in #207. I'll update this comment with notes in a bit.
Comment #211
lauriiiRelated to #208.5, I was wondering if this is actually highlighting a bigger problem we might be having; what happens if there are libraries attached to the entity? Why do we have to manually add these CSS files? Shouldn't they be already a dependency to the markup depending on those assets?
I haven't done a detailed review on this yet, but overall I feel fine with the patch so I don't think this should be blocked by a review from me.
Comment #212
phenaproximaLet's take a quick pulse check at this point.
media-embed-widget drupal-media > .align-centerand its ilk to justdrupal-media > .align-centerin ckeditor.drupalmedia.css.In other words, there aren't very many code changes needed here; we mostly just need to justify some decisions and (probably) open some follow-ups.
Comment #213
phenaproximaJust discussed things with @effulgentsia and @oknate. Here's what we need to do:
data-align), change frequently, which they may do under certain uses. This calls for a comment in the JavaScript code of the drupalmedia plugin.So, let's get these follow-ups filed, changes made, decisions documented, and things fixed. Then we are, by agreement of virtually everyone still involved, at RTBC.
Comment #214
wim leersI was on vacation, catching up since #143! While I was working on this, more comments kept appearing 🙈
Overall observation while I was reading and responding to all the comments: the scope of this issue is becoming too big. I think it'd be helpful to first make filter changes in a separate issue.
#144:
CKEDITOR.plugins.drupalimage.getFocusedWidget. That's unfortunate, but I agree it's the right thing to do.#145:
justify-content. That's exactly what I was concerned about in #137 + #143. In #143 I wrote I defer to @lauriii on that.#146: Looks good! Hilarious text once again :D Nit: please use
assertSame()instead ofassertEquals().#147:
DrupalImage. Indeed no test coverage exists for most existing CKEditor integration things. I think it's a nice win to add test coverage here even though it is out of scope. If core committers are fine with it being part of this patch, I'm fine with it too. But while you're waiting for this to be committed, you might as well extract this to a separate issue and patch so it can land independently :)testLinkabilityWhenDrupalImageIsAbsent. Great catch! 🦅👁 The changes look good, except for the// Remove the `drupalimage` plugin's `DrupalImage` button.logic should be using the same logic as the now-deletedtestLinkabilityWhenDrupalImageIsAbsent()used to execute. On second thought … this is probably simpler and easier to understand. It's just more brittle, because you have to keep this in sync with what's in::setUp(). And brittleness is important.#148:
I think it might be best to do a quick call about the first and last bullet.
#149:
GET, since that's what thedrupalmediaplugin uses (and yes, it's okay for us to assumeGETsuffices). I'm fine with changingMediaFilterControllerto matchEditorControllerlike you pointed out, but I think we should also restrict it at the routing level, by addingmethods: [GET]to the route definition.#150:
#153
#154 + #157: RE:
class="missing-media". Nice find. Note that this is not a problem in HEAD because in HEAD, the missing media render array does not contain aclass. Perhaps we can move the entire set of "improve missing media handling" changes to a separate issue? How do @lauriii and @ckrina feel about that?#159: I'm super suspicious of CSS specifically for left alignment. What about right alignment? How does this hold up in RTL contexts?
#166: Thanks for organizing that call to expedite progress! I am fine with that. But I wonder what you think about my suggestion (see my response to #153).
#167: Oh, using a background image, very interesting! Could we do the same for the filter-side generated missing media message? Also, I object to using the same
class="missing-media"here, because this is an AJAX error (either due to network failure or server failure), no media is actually missing.#168:
assertWaitOnAjaxRequest()is not unreliable if there aren't multiple AJAX requests happening.#169: The
titleattribute was specifically added so that sighted users could see a helpful message when they're hovering over the image when looking at the filtered text (so not in the text editor, but when looking at/node/42for example).#177:
2. I don't think we should not deduplicate dependencies ourselves. Drupal handles that for us. We should have a dependency on every asset library whose symbol we import into the global closure of a JS file. Here, that's
jQuery(hencecore/jquery) andDrupal(hencecore/drupal). And we also need the AJAX system to be available (hence alsocore/drupal.ajax).#179: This is now changing the parameter order of
\Drupal\Tests\media\Kernel\MediaEmbedFilterTest::testFilterIntegration()only to make$additional_attributesoptional. But all test cases provide this anyway. It also causes significant changes in the data provider. Please revert this.#180 + #181: RE: link context menu. Nice catch! 🥳
#186:
👎 This patch is plenty large as it is. We also generally avoid creating premature abstractions. Please create that trait in the next issue instead of this one. EDIT: Hah, @phenaproxima said the same in #191.EDIT: #192 addressed this!#189: I don't understand why after lots of back-and-forth in the past we concluded that
inline-blockwas not possible yet now it suddenly is. Will investigate.#191:
4. Because the alignment filter does not and cannot make assumptions; it does only alignment. It's up to the UI/content component itself to have appropriate margins. Anyway, this dates back to >5 years ago, discussing this more is out of scope here for sure.
#196++
#197++
#198:
testPreviewUsesDefaultThemeAndIsClientCacheable(). See the screencast demonstrating its impact: https://www.drupal.org/files/issues/2019-06-27/3064340%20before.mov versus https://www.drupal.org/files/issues/2019-06-27/3064340%20after.mov. This work was done in #3064340: Make preview responses cacheable to accelerate previewsInteresting point. But …EDIT: #200 is a very helpful clarification! I still believe we should fix this one abstraction level higher. But … perhaps that's just not possible in the abstraction we have: if each plugin is supposed to return CSS files, then the caller (one abstraction level up) can't magically determine which asset libraries they originate from. So … I think that means I agree with you. 😁👍 I think we then do need a follow-up to fix other CKEditor plugins to do this too, as well as update the API docs to recommend this pattern. For CKEditor 5, we should not port this API design flaw in #2966864: Add optional support for CKEditor 5 in D9 so we can remove CKE 4 from Drupal 10.\Drupal\ckeditor\Plugin\CKEditorPlugin\DrupalImageCaption::getCssFiles()and\Drupal\ckeditor\Plugin\CKEditorPlugin\Language::getCssFiles()are also not yet doing that. How about we create a follow-up issue to fix all of those at the same time? Right now, this is just following the existing pattern in Drupal core. Besides, this arguably should be handled in the caller of a plugin'sgetCssFiles():\Drupal\ckeditor\CKEditorPluginManager::getCssFiles(). Then we don't burden each CKEditor plugin with this. Would you be okay with that?#206:
#208.4: CKEditor must be generating those automatically. As long as those empty tags don't disrupt anything, I'd like to not deal with that in this issue, since this has already become extremely long.
#211:
hook_ckeditor_css_alter(). If a theme wants to do this, they can useckeditor_stylesheets: […]in their*.info.ymlfile. This is not new. Look atbartik.info.ymlfor an example 🙂hidden.module.cssin particular was introduced in #3057578: .visually-hidden not respected in CKEditor preview to ensure that a field label with.visually-hiddendoes not get displayed in the CKEditor preview. There are clear before vs after screenshots in that issue summary.#212:
RE: last bullet. Media entities rendered by Drupal don't have asset libraries attached by default. I explained above why we don't want this. I can expand on it more if you like. Loading attached CSS and JS — problematic as it is for the reasons I've outlined already — would require us to use the AJAX system. This would mean that requests would use the back-end theme instead of the front-end theme, thus loading both the wrong template (that of the admin theme) and the wrong asset libraries (those of the admin theme). It would also make the responses uncacheable (AJAX responses are uncacheable). If you want the nitty gritty background, see #2844822: The preview in CKEditor does not use the same Twig template as the one on the front end (default theme). Specifically, see #2844822-41: The preview in CKEditor does not use the same Twig template as the one on the front end (default theme), where I anticipated this would come up 🙉. Entity Embed prior to 1.0 was using the AJAX system. One early user with a super advanced use case needed it. They acknowledged it would not work for everyone always. Many people ran into the problem of the wrong Twig template being used. By now 23% of all Drupal 8 Entity Embed installs is using
8.x-1.0where no AJAX request is used, and hence no CSS or JS is loaded. There have been no complaints. (I just triaged the Entity Embed issue queue again to make sure.)Patch review
👎 I still think this is wrong. We shouldn't add
class="media-embed-widget"just to allow us to write.media-embed-widget. Our CSS should just use. cke_widget_drupalmedia.The CSS is coupled to CKEditor. The file is even named
ckeditor.drupalmedia.css. Adding an extra level of indirection is just confusing.I apparently already explained this before in #143 and in #137.
This was triggered by @phenaproxima in #136.2, where he wrote:
This is a valid concern! :) I'm glad it was raised. Fortunately we can rely on these classes.
✅ Fixed.
🤔 In #25, this used to be more abstract:
It was more abstract for a reason: custom and contrib modules may layer additional behavior, styling or semantics on top of existing HTML elements by adding more
data-attributes. For example,data-highlight,data-editor-comments,data-invert-colors,data-mirror,data-geolocation, and so on. That's exactly whatFilterAligndoes viadata-alignandFilterCaptionviadata-caption. ThisThe current code hardcodes only behavior for
data-align. Yes, I know this plugin has special behavior fordata-caption. There's no other way for that one. Butdata-alignwas implemented generically, and at least some otherdata-somethings could achieve the same.In my opinion, this is a regression compared to earlier patch iterations. But I won't block the patch over this.
✅ Added
methods: [GET], as I recommended in response to #149.✅ Nit: This can be static, since it doesn't rely on any service data. Less coupling. Fixed.
🤔 See my response to #177.
🤔✅ Created an issue for generalizing this, or for at least establishing a pattern: #3072063: CKEditorPluginManager::getCssFiles() and CKEditorPluginCssInterface::getCssFiles() should be library-aware and override-aware. I'd personally prefer to not do this pretty complex thing in this issue, but I won't block this patch over it.
🤓✅ Language nit.
🔎 Nit: this entire test is about the
drupalmediaplugin.🔎 Nit: the route does not throw an error. The controller throws it. And only the test controller does that. That just simulates a failed response, due to either server error or network error.
🔎 Übernit: s/javascript/JavaScript/.
🔎 Übernit: 80 cols.
🔎 … more nits.
✅Fixed.
👎 See my response to #147.2. Reverted this back to the old pattern to remove the brittleness. ✅
The comment is still the same as in #25, but the test logic is very different.
This was a problem elsewhere in this test too.
Improved comments in several places, and also made them consistent with the other comments in this test class.
This is inaccurate, they're not added to
<drupal-media>by the CKEditor Widget's JS, but to thedrupalmediaCKEditor Widget wrapper.⚠️ These don't actually return anything. Fixed. ✅
👏 Nicely done! 🥳
👎 Out-of-scope change. (Plus two more, not quoted.) Reverted. ✅ (I think this was a leftover from a previous patch iteration where more libraries were being attached.)
👎 Data providers should avoid making arguments optional unless there are very strong reasons not to. Tests should be as specific and predictable as possible. Removed defaults. ✅
👎⚠️ This is causing a huge pile of change just because the arguments have been re-ordered. I also remarked on this in my review of #179. Reverted those changes. And apparently that means 100% of changes are reverted, there were zero functional changes. ✅
HAH! Great catch :D
🤔 Shouldn't this be
media-embed-error.css? AndStyles for media embed errors.?👎 Out-of-scope change. Reverted. ✅
WHEW 🤯😴
Comment #215
wim leers#213:
media-embed-widgetin favor of.cke_widget_drupalmedia.#208.3: Why does this even live in
classyand not inmedia?_loadPreview()to the effect ofNote the absence of caching, that's because this uses a GET request (which is cacheable) and the server takes special care to make the responses privately cacheable (i.e. per session) in the browser.?Comment #216
wim leers@phenaproxima and I pair-programmed what we hope to be the last iteration in this impressively long issue 😁
.align-*code that exists in the current patch because it has gotten sign-off from the front-end maintainer @lauriii. But we also agreed that we should not make additional use cases impossible. So we settled on restoring the original code (which exposesdata-attributes and makes them targetable from CSS) but keeping the.align-*. We created an issue to consider removing.align-*: #3072279: Stop generating align-* classes for preview in DrupalMedia CKEditor plugin.FilterCaption's use of thefilter/captionasset library, which ensures that a sensible default styling is available in all themes, not just in Classy subthemes. So in this reroll we are adding amedia/embed_errorasset library.Everything except for the last bullet is captured by
interdiff-details.txt. The last bullet is captured byinterdiff-css.txt.interdiff.txtcontains everything.Comment #218
wim leersOh, we forgot to remove
DrupalMediaTestin #216 (because we descoped the library-related things in #213 in favor of #3072063: CKEditorPluginManager::getCssFiles() and CKEditorPluginCssInterface::getCssFiles() should be library-aware and override-aware).Comment #219
oknate1. There is an extra space before bracket in two places:
2: There's an extra line in this file:
Comment #220
wim leers@phenaproxima pinged me about one thing he noticed while doing a final patch review. He was wondering why we were specifying margins in one CSS file but not another. And then he was wondering why we're even specifying them at all. I have been concerned about that too, but since it'd have been easy to fix in a follow-up, I didn't care too much. But I'm glad he brought it up :)
@phenaproxima and I suspect that this was added to improve the usability of the authoring experience for content with left- or right-aligned embedded media. And that's a noble cause, too! However,
align.module.cssdoes not specify this (which is whatFilterAlignrelies upon). Consequently, embedded aligned images (<img data-align="…">) are displayed without any margins in CKEditor by default. Unless the front end theme adds some (usingckeditor_stylesheetsin*.info.yml).It'd be a big WTF if aligned embedded media items have this margin but aligned images do not. See for yourself:
So let's keep this consistent, then add margins in a follow-up (and then target all CKEditor widgets (by using the selector
.cke_widget_wrapper). Then things stay consistent :)Comment #221
phenaproximaFiled three related follow-ups.
Comment #222
oknateFixing the path on the missing media indicator image in core/themes/stable/css/media/filter.media_embed.css.
Comment #223
phenaproximaNice catch, @oknate -- thanks!
At @effulgentsia's request, I did some light testing to figure out if I could get the
_previewNeedsServerSideUpdate()function (in the CKEditor plugin's JavaScript) to ever return false. The answer is no, I couldn't, but I think that is because the only way to make changes right now is to go into source mode (which downcasts the embedded media widget), make the changes, and then back into visual mode (which upcasts the widget by re-instantiating it, causing it to fetch the preview again). This seems correct to me; I'm guessing that once we implement #2994699: Create a CKEditor plugin to select and embed a media item from the Media Library, and changes can be made in the UI, the preview will become substantially smarter.Beyond that, I think we're done here. Wim and I have thoroughly reviewed the patch, scaled back the scope a bit, removed some things that were questionable, and clarified many decisions. All requested follow-ups have been filed. I think we can land it.
Comment #224
effulgentsia commentedSomehow that didn't stick, so setting that checkbox again.
Comment #225
effulgentsia commentedI had to make the following coding standards fixes in order for the patch to pass the pre-commit hook. Leaving at RTBC, but waiting for tests to come back green.
Comment #226
effulgentsia commentedThat was an unintended consequence of copy/paste. This patch reverts it.
Comment #228
effulgentsia commentedPushed to 8.8.x. Thank you for everyone's great work on this. See you in #2994699: Create a CKEditor plugin to select and embed a media item from the Media Library for the next step!
Comment #229
wim leers#226: hah, I was gonna point out exactly that :)
Also: 🥳🥳🥳🥳
Comment #230
wim leers#2994699: Create a CKEditor plugin to select and embed a media item from the Media Library has been rerolled — that next step should be much easier to land 😀🤞
Comment #231
oknateredacted
Comment #233
wim leersComment #234
pameeela commentedRemoved release notes tag, this is already tagged for highlights, which I think is correct.
Please comment if I'm wrong and there is any potential for disruption from this that should go in release notes.
Comment #235
wim leersYou're correct, I didn't realize required something to be disruptive. Thanks!