Skip to content

Media image and CKEditor5 media embed adding captions from entity fields

Published: at 05:19 PM

On one of the Drupal 10 sites I am working on, we upgraded from CKE4 to CKE5 thus having to switch from entity_embed plugin to media_embed plugin that is offered by core to be used for managing media images within CKEditor. In our previous setup we added 2 fields to the media image entity named: field_photo_caption and field_photo_source which were used to hold information about image and that info would be rendered by default under the image - of course if caption and source info weren’t empty. We altered entity embed UI and hid caption logic from CKE4 UI.

After the mentioned upgrade to CKE5 this functionality stopped working due to CKE plugin change.

To fix this I implemented next steps:

1. Added new template

web/themes/custom/my_theme/templates/media--image.html.twig

{#
/**
 * @file
 * Default theme implementation to present a media item.
 *
 * Available variables:
 * - media: The media item, with limited access to object properties and
 *   methods. Only method names starting with "get", "has", or "is" and
 *   a few common methods such as "id", "label", and "bundle" are available.
 *   For example:
 *   - entity.getEntityTypeId() will return the entity type ID.
 *   - entity.hasField('field_example') returns TRUE if the entity includes
 *     field_example. (This does not indicate the presence of a value in this
 *     field.)
 *   Calling other methods, such as entity.delete(), will result in
 *   an exception.
 *   See \Drupal\Core\Entity\EntityInterface for a full list of methods.
 * - name: Name of the media item.
 * - content: Media content.
 * - title_prefix: Additional output populated by modules, intended to be
 *   displayed in front of the main title tag that appears in the template.
 * - title_suffix: Additional output populated by modules, intended to be
 *   displayed after the main title tag that appears in the template.
 * - view_mode: View mode; for example, "teaser" or "full".
 * - attributes: HTML attributes for the containing element.
 * - title_attributes: Same as attributes, except applied to the main title
 *   tag that appears in the template.
 * Custom added variables @see my_theme_preprocess_media__image
 * - caption: field_photo_caption text value
 * - source: field_photo_source text value
 *
 * @see template_preprocess_media()
 *
 * @ingroup themeable
 */
#}
<div{{ attributes }}>
  {{ title_suffix.contextual_links }}
  {{ content }}

  <figcaption>
    // Ad whatever styling and structure you want for these image
    // caption infos to show up.
    <div class="image-caption">{{ caption }}</div>
    <div class="image-source">{{ source }}</div>
  </figcaption>
</div>

2. Added hook preprocess

web/themes/custom/my_theme/my_theme.theme

/**
 * Implements hook_preprocess_HOOK().
 *
 * Prepares variables for media image template.
 *
 * Default template: media--image.html.twig.
 */
function my_theme_preprocess_media__image(array &$variables) {
  /** @var ?\Drupal\media\MediaInterface $media_image */
  $media_image = $variables['media'];

  if (empty($media_image)) {
    return;
  }

  if ($media_image->bundle() !== 'image') {
    return;
  }

  if ($media_image->hasField('field_photo_caption') && !empty($caption = $media_image->get('field_photo_caption')->getString())) {
    $variables['caption'] = $caption;
  }
  if ($media_image->hasField('field_photo_source') && !empty($source = $media_image->get('field_photo_source')->getString())) {
    $variables['source'] = $source;
  }
}

3. Remove caption logic from CKE5 UI

web/modules/custom/my_module/my_module.module

/**
 * Implements hook_ckeditor5_plugin_info_alter().
 *
 * We are hiding "DrupalMediaCaptionUI" plugin as we're rendering
 * media image captions by default from "field_photo_caption" and
 * "field_photo_source". @see theme_server_preprocess_media__image and
 * @see media--image.html.twig
 */
function my_module_ckeditor5_plugin_info_alter(array &$plugin_definitions) {
  unset($plugin_definitions['ckeditor5_drupalMediaCaption']);
}