diff --git a/core/modules/image/src/Plugin/Filter/FilterImageStyle.php b/core/modules/image/src/Plugin/Filter/FilterImageStyle.php index 1c03de5..dffc408 100644 --- a/core/modules/image/src/Plugin/Filter/FilterImageStyle.php +++ b/core/modules/image/src/Plugin/Filter/FilterImageStyle.php @@ -6,15 +6,15 @@ use Drupal\Core\Annotation\Translation; use Drupal\Core\Entity\EntityRepositoryInterface; use Drupal\Core\Entity\EntityTypeManagerInterface; -use Drupal\file\FileInterface; use Drupal\Core\Image\ImageFactory; use Drupal\Core\Plugin\ContainerFactoryPluginInterface; +use Drupal\Core\Render\RendererInterface; +use Drupal\file\FileInterface; use Drupal\filter\Annotation\Filter; use Drupal\filter\FilterProcessResult; use Drupal\filter\Plugin\FilterBase; use Drupal\image\ImageStyleInterface; use Symfony\Component\DependencyInjection\ContainerInterface; -use Drupal\Core\Render\RendererInterface; /** * Provides a filter to render inline images as image styles. @@ -67,6 +67,7 @@ class FilterImageStyle extends FilterBase implements ContainerFactoryPluginInter * @param \Drupal\Core\Image\ImageFactory $image_factory * The image factory. * @param \Drupal\Core\Render\RendererInterface $renderer + * The renderer. */ public function __construct(array $configuration, $plugin_id, $plugin_definition, EntityTypeManagerInterface $entity_type_manager, EntityRepositoryInterface $entity_repository, ImageFactory $image_factory, RendererInterface $renderer) { parent::__construct($configuration, $plugin_id, $plugin_definition); @@ -97,7 +98,8 @@ public function process($text, $langcode) { $dom = HTML::load($text); $xpath = new \DOMXPath($dom); - // Process each img element DOM element found with the necessary attributes. + // Process each img element DOM element found with the necessary + // attributes. /** @var \DOMElement $dom_element */ foreach ($xpath->query('//*[@data-entity-type="file" and @data-entity-uuid and @data-image-style]') as $dom_element) { // Get the UUID and image style for the file. @@ -154,12 +156,12 @@ protected function loadImageStyles() { */ protected function getImageInfo($file_uuid) { /** - * @var \Drupal\file\FileInterface; + * @var \Drupal\file\FileInterface $file; */ $file = $this->entityRepository->loadEntityByUuid('file', $file_uuid); - // Determine width/height of the source image. - $image_width = $image_height = NULL; + // Determine uri, width and height of the source image. + $image_uri = $image_width = $image_height = NULL; $image = $this->imageFactory->get($file->getFileUri()); if ($image->isValid()) { $image_uri = $file->getFileUri(); @@ -185,11 +187,13 @@ protected function getImageInfo($file_uuid) { */ protected function prepareImageAttributes(\DOMElement $dom_element) { // Make sure all non-regenerated attributes are retained. + $dom_element->removeAttribute('data-entity-type-file'); $dom_element->removeAttribute('data-entity-uuid'); $dom_element->removeAttribute('data-image-style'); $dom_element->removeAttribute('width'); $dom_element->removeAttribute('height'); $dom_element->removeAttribute('src'); + $attributes = array(); for ($i = 0; $i < $dom_element->attributes->length; $i++) { $attr = $dom_element->attributes->item($i); diff --git a/core/modules/responsive_image/src/Plugin/Filter/FilterResponsiveImageStyle.php b/core/modules/responsive_image/src/Plugin/Filter/FilterResponsiveImageStyle.php index 3e7341f..fd47974 100644 --- a/core/modules/responsive_image/src/Plugin/Filter/FilterResponsiveImageStyle.php +++ b/core/modules/responsive_image/src/Plugin/Filter/FilterResponsiveImageStyle.php @@ -8,9 +8,18 @@ namespace Drupal\responsive_image\Plugin\Filter; use Drupal\Component\Utility\Html; +use Drupal\Core\Annotation\Translation; +use Drupal\Core\Entity\EntityRepositoryInterface; +use Drupal\Core\Entity\EntityTypeManagerInterface; +use Drupal\Core\Image\ImageFactory; +use Drupal\Core\Plugin\ContainerFactoryPluginInterface; +use Drupal\Core\Render\RendererInterface; +use Drupal\file\FileInterface; use Drupal\filter\Annotation\Filter; use Drupal\filter\FilterProcessResult; use Drupal\filter\Plugin\FilterBase; +use Drupal\responsive_image\ResponsiveImageStyleInterface; +use Symfony\Component\DependencyInjection\ContainerInterface; /** * Provides a filter to render inline images as responsive images. @@ -23,72 +32,95 @@ * type = Drupal\filter\Plugin\FilterInterface::TYPE_TRANSFORM_REVERSIBLE * ) */ -class FilterResponsiveImageStyle extends FilterBase { +class FilterResponsiveImageStyle extends FilterBase implements ContainerFactoryPluginInterface { + + /** + * The entity type manager. + * + * @var \Drupal\Core\Entity\EntityTypeManagerInterface + */ + protected $entityTypeManager; + + /** + * @var \Drupal\Core\Entity\EntityRepositoryInterface + */ + protected $entityRepository; + + /** + * @var \Drupal\Core\Image\ImageFactory + */ + protected $imageFactory; + + /** + * @var \Drupal\Core\Render\RendererInterface + */ + protected $renderer; + + /** + * Constructs a Drupal\Component\Plugin\PluginBase object. + * + * @param array $configuration + * A configuration array containing information about the plugin instance. + * @param string $plugin_id + * The plugin_id for the plugin instance. + * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager + * The entity type manager. + * @param \Drupal\Core\Entity\EntityRepositoryInterface $entity_repository + * The entity repository. + * @param \Drupal\Core\Image\ImageFactory $image_factory + * The image factory. + * @param \Drupal\Core\Render\RendererInterface $renderer + * The renderer. + */ + public function __construct(array $configuration, $plugin_id, $plugin_definition, EntityTypeManagerInterface $entity_type_manager, EntityRepositoryInterface $entity_repository, ImageFactory $image_factory, RendererInterface $renderer) { + parent::__construct($configuration, $plugin_id, $plugin_definition); + + $this->entityTypeManager = $entity_type_manager; + $this->entityRepository = $entity_repository; + $this->imageFactory = $image_factory; + $this->renderer = $renderer; + } /** * {@inheritdoc} */ - public function process($text, $langcode) { - $search = array(); - $replace = array(); + public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) { + return new static($configuration, $plugin_id, $plugin_definition, $container->get('entity_type.manager'), $container->get('entity.repository'), $container->get('image.factory'), $container->get('renderer')); + } + /** + * {@inheritdoc} + */ + public function process($text, $langcode) { if (stristr($text, 'data-responsive-image-style') !== FALSE) { - $responsive_image_styes = entity_load_multiple('responsive_image_style'); + // Load all the responsive image styles so each img found in the text can + // be checked to ensure it has a valid responsive image style. + $responsive_image_styles = $this->loadResponsiveImageStyles(); + // Load the text that is being processed into XML to find images. $dom = Html::load($text); $xpath = new \DOMXPath($dom); - foreach ($xpath->query('//*[@data-entity-uuid and @data-responsive-image-style]') as $node) { - $file_uuid = $node->getAttribute('data-entity-uuid'); - $node->removeAttribute('data-entity-uuid'); - $responsive_image_style_id = $node->getAttribute('data-responsive-image-style'); - $node->removeAttribute('data-responsive-image-style'); + + // Process each img element DOM element found with the necessary + // attributes. + /** @var \DOMElement $dom_element */ + foreach ($xpath->query('//*[@data-entity-type="file" and @data-entity-uuid and @data-responsive-image-style]') as $dom_element) { + // Get the UUID and responsive image style for the file. + $file_uuid = $dom_element->getAttribute('data-entity-uuid'); + $responsive_image_style_id = $dom_element->getAttribute('data-responsive-image-style'); // If the responsive image style is not a valid one, then don't // transform the HTML. - if (empty($file_uuid) || !in_array($responsive_image_style_id, array_keys($responsive_image_styes))) { + if (empty($file_uuid) || !in_array($responsive_image_style_id, array_keys($responsive_image_styles))) { continue; } - $file = \Drupal::entityManager()->loadEntityByUuid('file', $file_uuid); - - // Determine width/height of images that don't have such attributes set. - $width = $node->getAttribute('width'); - $height = $node->getAttribute('height'); - if (empty($width) || empty($height)) { - $image = \Drupal::service('image.factory')->get($file->getFileUri()); - if ($image->isValid()) { - if (empty($width)) { - $width = $image->getWidth(); - } - if (empty($height)) { - $height = $image->getHeight(); - } - } - } - - // Make sure all non-regenerated attributes are retained. - $node->removeAttribute('width'); - $node->removeAttribute('height'); - $node->removeAttribute('src'); - $attributes = array(); - for ($i = 0; $i < $node->attributes->length; $i++) { - $attr = $node->attributes->item($i); - $attributes[$attr->name] = $attr->value; - } - - // Re-render as a responsive image. - $responsive_image = array( - '#theme' => 'responsive_image', - '#uri' => $file->getFileUri(), - '#width' => $width, - '#height' => $height, - '#attributes' => $attributes, - '#responsive_image_style_id' => $responsive_image_style_id, - ); - $altered_html = drupal_render($responsive_image); + // Transform the HTML for the img element by applying a responsive image + // style. + $altered_img = $this->getResponsiveImageStyleHtml($file_uuid, $responsive_image_style_id, $dom_element); // Load the altered HTML into a new DOMDocument and retrieve the element. - $updated_node = Html::load(trim($altered_html))->getElementsByTagName('body') + $updated_node = Html::load(trim($altered_img))->getElementsByTagName('body') ->item(0) ->childNodes ->item(0); @@ -96,22 +128,132 @@ public function process($text, $langcode) { // Import the updated node from the new DOMDocument into the original // one, importing also the child nodes of the updated node. $updated_node = $dom->importNode($updated_node, TRUE); - // Finally, replace the original image node with the new image node! - $node->parentNode->replaceChild($updated_node, $node); + + // Finally, replace the original image node with the new image node. + $dom_element->parentNode->replaceChild($updated_node, $dom_element); } + // Process the filter with the newly updated DOM. return new FilterProcessResult(Html::serialize($dom)); } + // Process the filter if no image style img elements are found. return new FilterProcessResult($text); } /** + * Loads the responsive image styles. + * + * @return string[] + */ + protected function loadResponsiveImageStyles() { + return array_keys($this->entityTypeManager->getStorage('responsive_image_style')->loadMultiple()); + } + + /** + * Get the the width and height of an image based on the file UUID. + * + * @param string $file_uuid + * The UUID for the file. + * + * @return array + * The image information. + */ + protected function getImageInfo($file_uuid) { + /** + * @var \Drupal\file\FileInterface $file; + */ + $file = $this->entityRepository->loadEntityByUuid('file', $file_uuid); + + // Determine uri, width and height of the source image. + $image_uri = $image_width = $image_height = NULL; + $image = $this->imageFactory->get($file->getFileUri()); + if ($image->isValid()) { + $image_uri = $file->getFileUri(); + $image_width = $image->getWidth(); + $image_height = $image->getHeight(); + } + + return [ + 'uri' => $image_uri, + 'width' => $image_width, + 'height' => $image_height + ]; + } + + /** + * Removes attributes that will be generated from image style theme function. + * + * @param \DOMElement $dom_element + * The DOM element for the img element. + * + * @return array + * The attributes array. + */ + protected function prepareResponsiveImageAttributes(\DOMElement $dom_element) { + // Make sure all non-regenerated attributes are retained. + $dom_element->removeAttribute('data-entity-type-file'); + $dom_element->removeAttribute('data-entity-uuid'); + $dom_element->removeAttribute('data-responsive-image-style'); + $dom_element->removeAttribute('width'); + $dom_element->removeAttribute('height'); + $dom_element->removeAttribute('src'); + + // Responsive image styles should override image styles. + if ($dom_element->hasAttribute('data-image-style')) { + $dom_element->removeAttribute('data-image-style'); + } + + $attributes = array(); + for ($i = 0; $i < $dom_element->attributes->length; $i++) { + $attr = $dom_element->attributes->item($i); + $attributes[$attr->name] = $attr->value; + } + + return $attributes; + } + + /** + * Get the HTML for the img element after image style is applied. + * + * @param string $file_uuid + * The UUID for the file. + * @param string $responsive_image_style_id + * The ID for the responsive image style. + * @param \DOMElement $dom_element + * The DOM element for the image element. + * + * @return string + * The img element with the image style applied. + */ + protected function getResponsiveImageStyleHtml($file_uuid, $responsive_image_style_id, \DOMElement $dom_element) { + $image_info = $this->getImageInfo($file_uuid); + $image_uri = $image_info['uri']; + $image_width = $image_info['width']; + $image_height = $image_info['height']; + + // Remove attributes that will be generated by the image style. + $attributes = $this->prepareResponsiveImageAttributes($dom_element); + + // Re-render as a responsive image. + $responsive_image = array( + '#theme' => 'responsive_image', + '#responsive_image_style_id' => $responsive_image_style_id, + '#uri' => $image_uri, + '#width' => $image_width, + '#height' => $image_height, + '#attributes' => $attributes, + ); + + return $this->renderer->render($responsive_image); + } + + /** * {@inheritdoc} */ public function tips($long = FALSE) { if ($long) { - $responsive_image_styles = entity_load_multiple('responsive_image_style'); + $responsive_image_styles = $this->loadResponsiveImageStyles(); $list = '' . implode(', ', array_keys($responsive_image_styles)) . ''; return t('

You can make images responsive by adding a data-responsive-image-style attribute, whose values is one of the responsive image style machine names: !responsive-image-style-machine-name-list.

', array('!responsive-image-style-machine-name-list' => $list));