.../modules/media/src/Plugin/Filter/MediaEmbed.php | 61 +++++++++++++++++++++- .../tests/src/Kernel/MediaEmbedFilterTest.php | 51 ++++++++++++++++++ 2 files changed, 111 insertions(+), 1 deletion(-) diff --git a/core/modules/media/src/Plugin/Filter/MediaEmbed.php b/core/modules/media/src/Plugin/Filter/MediaEmbed.php index 5d967a02da..ee2f9eb8c7 100644 --- a/core/modules/media/src/Plugin/Filter/MediaEmbed.php +++ b/core/modules/media/src/Plugin/Filter/MediaEmbed.php @@ -11,6 +11,7 @@ use Drupal\Core\Render\RendererInterface; use Drupal\filter\FilterProcessResult; use Drupal\filter\Plugin\FilterBase; +use Drupal\image\Plugin\Field\FieldType\ImageItem; use Drupal\media\MediaInterface; use Symfony\Component\DependencyInjection\ContainerInterface; @@ -156,10 +157,11 @@ public function process($text, $langcode) { $xpath = new \DOMXPath($dom); foreach ($xpath->query('//drupal-media[@data-entity-type="media" and normalize-space(@data-view-mode)!="" and normalize-space(@data-entity-uuid)!=""]') as $node) { + /** @var \DOMElement $node */ $uuid = $node->getAttribute('data-entity-uuid'); $view_mode_id = $node->getAttribute('data-view-mode'); - // Delete the attributes this filter reacts to. + // Delete the consumed attributes. $node->removeAttribute('data-entity-type'); $node->removeAttribute('data-entity-uuid'); $node->removeAttribute('data-view-mode'); @@ -169,6 +171,9 @@ public function process($text, $langcode) { if (!$media) { \Drupal::logger('media')->error('The media item with UUID "@uuid" does not exist.', ['@uuid' => $uuid]); } + else { + $this->applyPerEmbedMediaOverrides($node, $media); + } $view_mode = $this->entityRepository->loadEntityByConfigTarget('entity_view_mode', "media.$view_mode_id"); if (!$view_mode) { @@ -304,4 +309,58 @@ public static function disableQuickEdit(array $build) { return $build; } + /** + * Applies attribute-based per-media embed overrides of media information. + * + * Currently, this only supports overriding an image media source's `alt` and + * `title`. Support for more overrides may be added in the future. + * + * @param \DOMElement $node + * The HTML tag whose attributes may contain overrides, and if such + * attributes are applied, they will be considered consumed and will + * therefore be removed from the HTML. + * @param \Drupal\media\MediaInterface $media + * The media entity to apply attribute-based overrides to, if any. + * + * @see \Drupal\media\Plugin\media\Source\Image + */ + protected function applyPerEmbedMediaOverrides(\DOMElement $node, MediaInterface $media) { + if ($image_field = $this->getMediaImageSourceField($media)) { + $settings = $media->{$image_field}->getItemDefinition()->getSettings(); + + if (!empty($settings['alt_field']) && $node->hasAttribute('alt')) { + $media->{$image_field}->alt = $node->getAttribute('alt'); + $media->thumbnail->alt = $node->getAttribute('alt'); + // Delete the consumed attribute. + $node->removeAttribute('alt'); + } + + if (!empty($settings['title_field']) && $node->hasAttribute('title')) { + $media->{$image_field}->title = $node->getAttribute('title'); + $media->thumbnail->title = $node->getAttribute('title'); + // Delete the consumed attribute. + $node->removeAttribute('title'); + } + } + } + + /** + * Get image field from source config. + * + * @param \Drupal\media\MediaInterface $media + * A media entity. + * + * @return string|null + * String of image field name. + */ + protected function getMediaImageSourceField(MediaInterface $media) { + $field_definition = $media->getSource() + ->getSourceFieldDefinition($media->bundle->entity); + $item_class = $field_definition->getItemDefinition()->getClass(); + if ($item_class == ImageItem::class || is_subclass_of($item_class, ImageItem::class)) { + return $field_definition->getName(); + } + return NULL; + } + } diff --git a/core/modules/media/tests/src/Kernel/MediaEmbedFilterTest.php b/core/modules/media/tests/src/Kernel/MediaEmbedFilterTest.php index 3293ec9a6b..a48188b5fd 100644 --- a/core/modules/media/tests/src/Kernel/MediaEmbedFilterTest.php +++ b/core/modules/media/tests/src/Kernel/MediaEmbedFilterTest.php @@ -4,6 +4,7 @@ use Drupal\Core\Cache\Cache; use Drupal\Core\Cache\CacheableMetadata; +use Drupal\field\Entity\FieldConfig; /** * @coversDefaultClass \Drupal\media\Plugin\Filter\MediaEmbed @@ -178,6 +179,56 @@ public function providerAccessUnpublished() { ]; } + /** + * Tests overriding of `alt` and `title` for image media items. + */ + public function testOverridesAltAndTitle() { + // The `alt` field property is enabled by default, the `title` one is not. + // Since we want to test it, enable it. + $source_field = FieldConfig::load('media.image.field_media_image'); + $source_field->setSetting('title_field', TRUE); + $source_field->save(); + + $base = [ + 'data-entity-type' => 'media', + 'data-entity-uuid' => static::EMBEDDED_ENTITY_UUID, + 'data-view-mode' => 'full', + ]; + $input = $this->createEmbedCode($base); + $input .= $this->createEmbedCode([ + 'alt' => 'alt 1', + 'title' => 'title 1', + ] + $base); + $input .= $this->createEmbedCode([ + 'alt' => 'alt 2', + 'title' => 'title 2', + ] + $base); + $input .= $this->createEmbedCode([ + 'alt' => 'alt 3', + 'title' => 'title 3', + ] + $base); + + $this->applyFilter($input); + + $img_nodes = $this->cssSelect('img'); + $this->assertCount(4, $img_nodes); + $this->assertHasAttributes($img_nodes[0], [ + 'alt' => 'default alt', + ]); + $this->assertHasAttributes($img_nodes[1], [ + 'alt' => 'alt 1', + 'title' => 'title 1', + ]); + $this->assertHasAttributes($img_nodes[2], [ + 'alt' => 'alt 2', + 'title' => 'title 2', + ]); + $this->assertHasAttributes($img_nodes[3], [ + 'alt' => 'alt 3', + 'title' => 'title 3', + ]); + } + /** * Tests the indicator for missing entities. *