- πΊπΈUnited States jonraedeke
Unfortunately, this is still an issue with CKEditor 5 implementation in core.
I'm just digging into CKEditor 5 plugin coding, but it appears that CKEditor 5 really isn't aware of the media markup.
- First commit to issue fork.
- πΊπΈUnited States jayhuskins
For anyone interested, here's how I solved this for my website:
- Do not display a figure around the media in the media display
- Create a custom text filter that goes between "Caption images" and "Embed media" which adds a figure if there isn't already one
Here is the code for the process function in my custom text filter:
public function process($text, $langcode) { $result = new FilterProcessResult($text); if (stristr($text, '<drupal-media') === FALSE) { return $result; } $dom = Html::load($text); $xpath = new \DOMXPath($dom); $query = '//drupal-media[@data-entity-type="media" and normalize-space(@data-entity-uuid)!=""]'; foreach ($xpath->query($query) as $node) { $parent = $node->parentNode; if ($parent->tagName === 'a') { $parent = $parent->parentNode(); } if ($parent->tagName === 'figure') { continue; } // Move classes from node to figure. $classes = $node->getAttribute('class'); $node->removeAttribute('class'); $figure = $dom->createElement('figure'); $figure->setAttribute('class', $classes); // Place figure between parent and node. $parent->replaceChild($figure, $node); $figure->appendChild($node); } $result->setProcessedText(Html::serialize($dom)); return $result; }
- π΅π±Poland pawel.traczynski Warsaw
The Drupal core caption filter did not work for me either for the resons mentioned above.
Here is how I achived captions for embedded media images while being in full control of the HTML markup.
I needed to give the user an option to:
- embed image while being able to pick image style: I have created one style for tall/portrait image and one for wide/full content width image
- allow them to provide caption
- use my own markup for the image printed image style and the caption1. I have setup media bundle 'image' and added 'media_caption' formatted text field in it. The caption format allowed paragraphs and links to that instead of plain text captions, a caption could be multiple paragraphs or could contain a link. For this purpose I have used a text format that I already had on my site that allows exactly and only this.
2. Then configured two different view modes for media, where each view mode used different image style for the image. One view mode machine name was 'image_full' and the other was 'image_tall'.
3. The custom templates for embedded image media I needed to be global, that is I neeeded them to be picked up independently of theme being in use. For this reason I have registered my own media templates in my module:
/** * Implements hook_theme(). */ function MODULE_theme($existing, $type, $theme, $path) { return [ // Use custom templates for 'media' with 'image' bundle and // with 'image-...' view modes. 'media__image__image_full' => [ 'template' => 'media-image', 'render element' => 'elements', 'base hook' => 'media', ], 'media__image__image_tall' => [ 'template' => 'media-image', 'render element' => 'elements', 'base hook' => 'media', ], ]; }
As you can see I used single template for both view modes. Because of using my templates only for my view modes, other view modes, like those used in the Media administration page are not impacted and will continue to display normally without breaking the admin UI styling.
4. The custom media-image.html.twig template looked like this:
<figure{{ attributes }}> {{ title_suffix.contextual_links }} {{ content|without('media_caption') }} {% if media_caption %} <figcaption class="fix-margins">{{ media_caption }}</figcaption> {% endif %} </figure>
5. Then to support my own template and my markup requirements I added this template preprocess:
/** * Preprocess variables for media template. * * Providing custom classes and values to media-image templates. */ function MODULE_preprocess_media(&$variables) { /** @var \Drupal\media\MediaInterface $media */ $media = $variables['elements']['#media']; /** @var \Drupal\MODULE\Common $common */ $common = \Drupal::service('MODULE.common'); $bundle = $media->bundle(); $view_mode = $variables['elements']['#view_mode']; if ($bundle == 'image' && in_array($view_mode, ['image_full', 'image_tall'])) { // Converts view mode like 'image_full' to 'full'. $image_mode = str_replace('image_', '', $view_mode); $variables['attributes']['class'][] = 'img'; $variables['attributes']['class'][] = 'img-' . $image_mode; // This just assigns caption value from media entity to a variable. // If value is missing this will equal FALSE. $variables['media_caption'] = $common->fieldValue('media_caption', $media, FALSE); } }
Thats it. Now when Embed Media button it pressed, when user uploads a new image, they will see a custom caption field. In addition when the insert the image into CKEditor5, they will see a dropdown to pick the image style to use.
I have implemented this as an "Insert" module replacement for CK5.
So far the only downsides of this solution are:
- once the image has been inserted into ckeditor5 content, then if user wants to edit the caption again, they have to edit it in the media, for example by editing the media on the media administration page
- the caption is global for a single media item. You cannot insert the media in different nodes while having different captions for each.