Caption filter should use existing <figcaption> and keep comments

Created on 10 January 2018, almost 7 years ago
Updated 31 January 2024, 10 months ago

Problem/Motivation

1) The caption filter currently always wraps the captioned element in a <figure> block to which it adds its own <figcaption>. This creates unnessecarily nested markup in case the element itself is already presented as/contains a <figure> with a <figcaption>.

2) Additionally, the $node->C14N() call strips all comments, which includes Twig debug comments (we can deal with that) and compatibility comments like those used for responsive images: <!--[if IE 9]><video style="display: none;"><![endif]--> (this is bad and will cause problems).

Example setup for 1) using entity_embed:

  1. Media type "Image" with a template like <figure>{{ content.field_image }}<figcaption>{{ name }}</figcaption></figure>
  2. Some content in which the media entity is embedded with a specified custom caption

Expected result:
<figure>{{ content.field_image }}<figcaption>{{ my overridden caption}}</figcaption></figure>

Actual result (using default filter-caption.html.twig):

<figure>
  <figure>
    {{ content.field_image }}
    <figcaption>{{ name }}</figcaption>
  </figure>
  <figcaption>{{ my overridden caption }}</figcaption>
</figure>

Proposed resolution

1) Re-use an already present <figcaption> element.
2) Keep comments when building $altered_html.

Remaining tasks

Discuss what we need/want to do. Review.

πŸ“Œ Task
Status

Active

Version

11.0 πŸ”₯

Component
FilterΒ  β†’

Last updated 3 days ago

No maintainer
Created by

πŸ‡©πŸ‡ͺGermany ckaotik Berlin

Live updates comments and jobs are added and updated live.
Sign in to follow issues

Comments & Activities

Not all content is available!

It's likely this issue predates Contrib.social: some issue and comment data are missing.

  • πŸ‡ΊπŸ‡Έ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 caption

    1. 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.

Production build 0.71.5 2024