diff --git a/core/modules/image/image.admin.inc b/core/modules/image/image.admin.inc index e43bd487..b4df6bce 100644 --- a/core/modules/image/image.admin.inc +++ b/core/modules/image/image.admin.inc @@ -47,13 +47,32 @@ function template_preprocess_image_style_preview(&$variables) { $variables['preview']['original']['width'] = round($variables['preview']['original']['height'] / $variables['original']['height'] * $variables['original']['width']); } - // Set up derivative file information. - $preview_file = $style->buildUri($original_path); + // Set up derivative file pipeline. + $pipeline = \Drupal::service('image.processor')->createInstance('derivative') + ->setImageStyle($style) + ->setSourceImageUri($original_path); + $preview_file = $pipeline->getDerivativeImageUri(); + // Create derivative if necessary. if (!file_exists($preview_file)) { - $style->createDerivative($original_path, $preview_file); + $pipeline->buildDerivativeImage(); + } + + // The resulting image dimensions may be available from the Image object in + // the pipeline. If not (the image was already processed, or the state of + // dimensions from Image object is not determined), then load the Image + // object form the file. + $load_from_file = TRUE; + if ($pipeline->hasImage()) { + $preview_image = $pipeline->getImage(); + if ($preview_image->getWidth() && $preview_image->getHeight()) { + $load_from_file = FALSE; + } + } + if ($load_from_file) { + $preview_image = $image_factory->get($preview_file); } - $preview_image = $image_factory->get($preview_file); + $variables['derivative'] = [ 'url' => file_url_transform_relative(file_create_url($preview_file)), 'width' => $preview_image->getWidth(), diff --git a/core/modules/image/image.module b/core/modules/image/image.module index 73bb4630..d18ab71b 100644 --- a/core/modules/image/image.module +++ b/core/modules/image/image.module @@ -255,18 +255,15 @@ function image_style_options($include_empty = TRUE) { function template_preprocess_image_style(&$variables) { $style = ImageStyle::load($variables['style_name']); - // Determine the dimensions of the styled image. - $dimensions = [ - 'width' => $variables['width'], - 'height' => $variables['height'], - ]; - - $style->transformDimensions($dimensions, $variables['uri']); + $pipeline = \Drupal::service('image.processor')->createInstance('derivative') + ->setImageStyle($style) + ->setSourceImageUri($variables['uri']) + ->setSourceImageDimensions($variables['width'], $variables['height']); $variables['image'] = [ '#theme' => 'image', - '#width' => $dimensions['width'], - '#height' => $dimensions['height'], + '#width' => $pipeline->getDerivativeImageWidth(), + '#height' => $pipeline->getDerivativeImageHeight(), '#attributes' => $variables['attributes'], '#style_name' => $variables['style_name'], ]; @@ -274,8 +271,8 @@ function template_preprocess_image_style(&$variables) { // If the current image toolkit supports this file type, prepare the URI for // the derivative image. If not, just use the original image resized to the // dimensions specified by the style. - if ($style->supportsUri($variables['uri'])) { - $variables['image']['#uri'] = $style->buildUrl($variables['uri']); + if ($pipeline->isSourceImageProcessable()) { + $variables['image']['#uri'] = $pipeline->getDerivativeImageUrl()->toString(); } else { $variables['image']['#uri'] = $variables['uri']; diff --git a/core/modules/image/src/Controller/ImageStyleDownloadController.php b/core/modules/image/src/Controller/ImageStyleDownloadController.php index 2913b9fe..e540911f 100644 --- a/core/modules/image/src/Controller/ImageStyleDownloadController.php +++ b/core/modules/image/src/Controller/ImageStyleDownloadController.php @@ -6,6 +6,7 @@ use Drupal\Core\Image\ImageFactory; use Drupal\Core\Lock\LockBackendInterface; use Drupal\Core\StreamWrapper\StreamWrapperManagerInterface; +use Drupal\image\ImageProcessor; use Drupal\image\ImageStyleInterface; use Drupal\system\FileDownloadController; use Symfony\Component\DependencyInjection\ContainerInterface; @@ -35,6 +36,13 @@ class ImageStyleDownloadController extends FileDownloadController { */ protected $imageFactory; + /** + * The image processor service. + * + * @var \Drupal\image\ImageProcessor + */ + protected $imageProcessor; + /** * A logger instance. * @@ -51,11 +59,14 @@ class ImageStyleDownloadController extends FileDownloadController { * The image factory. * @param \Drupal\Core\StreamWrapper\StreamWrapperManagerInterface $stream_wrapper_manager * The stream wrapper manager. + * @param \Drupal\image\ImageProcessor $image_processor + * The image processor service. */ - public function __construct(LockBackendInterface $lock, ImageFactory $image_factory, StreamWrapperManagerInterface $stream_wrapper_manager = NULL) { + public function __construct(LockBackendInterface $lock, ImageFactory $image_factory, StreamWrapperManagerInterface $stream_wrapper_manager, ImageProcessor $image_processor) { parent::__construct($stream_wrapper_manager); $this->lock = $lock; $this->imageFactory = $image_factory; + $this->imageProcessor = $image_processor; $this->logger = $this->getLogger('image'); } @@ -66,7 +77,8 @@ public static function create(ContainerInterface $container) { return new static( $container->get('lock'), $container->get('image.factory'), - $container->get('stream_wrapper_manager') + $container->get('stream_wrapper_manager'), + $container->get('image.processor') ); } @@ -96,20 +108,31 @@ public function deliver(Request $request, $scheme, ImageStyleInterface $image_st $target = $request->query->get('file'); $image_uri = $scheme . '://' . $target; - // Check that the style is defined, the scheme is valid, and the image - // derivative token is valid. Sites which require image derivatives to be - // generated without a token can set the - // 'image.settings:allow_insecure_derivatives' configuration to TRUE to - // bypass the latter check, but this will increase the site's vulnerability - // to denial-of-service attacks. To prevent this variable from leaving the - // site vulnerable to the most serious attacks, a token is always required - // when a derivative of a style is requested. + // Check that the style is defined, return a 404 (Page Not Found) if + // missing. + if (empty($image_style)) { + throw new NotFoundHttpException(); + } + + // Create an image process pipeline. + $pipeline = $this->imageProcessor->createInstance('derivative'); + $pipeline + ->setImageStyle($image_style) + ->setSourceImageUri($image_uri); + + // Check that the scheme is valid, and the image derivative token is valid. + // Sites which require image derivatives to be generated without a token + // can set the 'image.settings:allow_insecure_derivatives' configuration to + // TRUE to bypass the latter check, but this will increase the site's + // vulnerability to denial-of-service attacks. To prevent this variable + // from leaving the site vulnerable to the most serious attacks, a token is + // always required when a derivative of a style is requested. // The $target variable for a derivative of a style has // styles//... as structure, so we check if the $target variable // starts with styles/. - $valid = !empty($image_style) && $this->streamWrapperManager->isValidScheme($scheme); + $valid = $this->streamWrapperManager->isValidScheme($scheme); if (!$this->config('image.settings')->get('allow_insecure_derivatives') || strpos(ltrim($target, '\/'), 'styles/') === 0) { - $valid &= hash_equals($image_style->getPathToken($image_uri), $request->query->get(IMAGE_DERIVATIVE_TOKEN, '')); + $valid &= hash_equals($pipeline->getDerivativeImageUrlSecurityToken(), $request->query->get(IMAGE_DERIVATIVE_TOKEN, '')); } if (!$valid) { // Return a 404 (Page Not Found) rather than a 403 (Access Denied) as the @@ -119,7 +142,7 @@ public function deliver(Request $request, $scheme, ImageStyleInterface $image_st throw new NotFoundHttpException(); } - $derivative_uri = $image_style->buildUri($image_uri); + $derivative_uri = $pipeline->getDerivativeImageUri(); $headers = []; // If using the private scheme, let other modules provide headers and @@ -163,7 +186,7 @@ public function deliver(Request $request, $scheme, ImageStyleInterface $image_st // Try to generate the image, unless another thread just did it while we // were acquiring the lock. - $success = file_exists($derivative_uri) || $image_style->createDerivative($image_uri, $derivative_uri); + $success = file_exists($derivative_uri) || $pipeline->buildDerivativeImage(); if (!empty($lock_acquired)) { $this->lock->release($lock_name); diff --git a/core/modules/image/src/EventSubscriber/ImageDerivativeSubscriber.php b/core/modules/image/src/EventSubscriber/ImageDerivativeSubscriber.php index 59b75903..7f86f1c0 100644 --- a/core/modules/image/src/EventSubscriber/ImageDerivativeSubscriber.php +++ b/core/modules/image/src/EventSubscriber/ImageDerivativeSubscriber.php @@ -3,7 +3,6 @@ namespace Drupal\image\EventSubscriber; use Drupal\Component\Utility\Crypt; -use Drupal\Component\Utility\UrlHelper; use Drupal\Core\Cache\Cache; use Drupal\Core\Config\ConfigFactoryInterface; use Drupal\Core\Extension\ModuleHandlerInterface; @@ -403,11 +402,11 @@ public function resolveDerivativeImageUrl(ImageProcessEvent $event): void { public function loadSourceImage(ImageProcessEvent $event): void { $pipeline = $event->getPipeline(); - if ($pipeline->hasVariable('sourceImageUri') && !$pipeline->hasVariable('image')) { - // If the source file doesn't exist, return FALSE without creating folders. + if ($pipeline->hasVariable('sourceImageUri') && !$pipeline->hasImage()) { + // If the source file doesn't exist or is invalid, throw an exception. $image = $this->imageFactory->get($pipeline->getVariable('sourceImageUri'), $pipeline->getVariable('imageToolkitId')); if (!$image->isValid()) { - return; + throw new ImageProcessException('Missing or invalid source image file ' . $pipeline->getVariable('sourceImageUri')); } $pipeline->setImage($image); } @@ -433,7 +432,7 @@ public function saveDerivativeImage(ImageProcessEvent $event): void { throw new ImageProcessException('Failed to create style directory'); } - if (!$pipeline->getVariable('image')->save($pipeline->getVariable('derivativeImageUri'))) { + if (!$pipeline->getImage()->save($pipeline->getVariable('derivativeImageUri'))) { if (file_exists($pipeline->getVariable('derivativeImageUri'))) { $this->logger->error('Cached image file %destination already exists. There may be an issue with your rewrite configuration.', ['%destination' => $pipeline->getVariable('derivativeImageUri')]); } @@ -482,7 +481,7 @@ public function applyImageStyle(ImageProcessEvent $event): void { public function applyImageEffect(ImageProcessEvent $event): void { $pipeline = $event->getPipeline(); $effect = $event->getArgument('imageEffect'); - $effect->applyEffect($pipeline->getVariable('image')); + $effect->applyEffect($pipeline->getImage()); } /** diff --git a/core/modules/image/src/ImageProcessPipelineInterface.php b/core/modules/image/src/ImageProcessPipelineInterface.php index 44073042..1cbcca0b 100644 --- a/core/modules/image/src/ImageProcessPipelineInterface.php +++ b/core/modules/image/src/ImageProcessPipelineInterface.php @@ -3,6 +3,7 @@ namespace Drupal\image; use Drupal\Component\Plugin\PluginInspectionInterface; +use Drupal\Core\Image\ImageInterface; use Drupal\Core\Plugin\ContainerFactoryPluginInterface; /** @@ -57,6 +58,32 @@ public function hasVariable(string $variable): bool; */ public function deleteVariable(string $variable): ImageProcessPipelineInterface; + /** + * Sets the Image object to be manipulated. + * + * @param \Drupal\Core\Image\ImageInterface $image + * The ImageInterface object to be derived. + * + * @return self + */ + public function setImage(ImageInterface $image): ImageProcessPipelineInterface; + + /** + * Returns the current Image object. + * + * @return \Drupal\Core\Image\ImageInterface + * The ImageInterface object to be derived. + */ + public function getImage(): ImageInterface; + + /** + * Returns whether the Image object is set. + * + * @return bool + * TRUE if the Image is set, FALSE otherwise. + */ + public function hasImage(): bool; + /** * Dispatches an event to be executed on the pipeline. * diff --git a/core/modules/image/src/Plugin/Field/FieldFormatter/ImageFormatter.php b/core/modules/image/src/Plugin/Field/FieldFormatter/ImageFormatter.php index ee99b0f8..70009707 100644 --- a/core/modules/image/src/Plugin/Field/FieldFormatter/ImageFormatter.php +++ b/core/modules/image/src/Plugin/Field/FieldFormatter/ImageFormatter.php @@ -10,6 +10,7 @@ use Drupal\Core\Session\AccountInterface; use Drupal\Core\Url; use Drupal\image\Entity\ImageStyle; +use Drupal\image\ImageProcessor; use Symfony\Component\DependencyInjection\ContainerInterface; use Drupal\Core\Form\FormStateInterface; use Drupal\Core\Cache\Cache; @@ -44,6 +45,13 @@ class ImageFormatter extends ImageFormatterBase implements ContainerFactoryPlugi */ protected $imageStyleStorage; + /** + * The image processor service. + * + * @var \Drupal\image\ImageProcessor + */ + protected $imageProcessor; + /** * Constructs an ImageFormatter object. * @@ -65,11 +73,14 @@ class ImageFormatter extends ImageFormatterBase implements ContainerFactoryPlugi * The current user. * @param \Drupal\Core\Entity\EntityStorageInterface $image_style_storage * The image style storage. + * @param \Drupal\image\ImageProcessor $image_processor + * The image processor service. */ - public function __construct($plugin_id, $plugin_definition, FieldDefinitionInterface $field_definition, array $settings, $label, $view_mode, array $third_party_settings, AccountInterface $current_user, EntityStorageInterface $image_style_storage) { + public function __construct($plugin_id, $plugin_definition, FieldDefinitionInterface $field_definition, array $settings, $label, $view_mode, array $third_party_settings, AccountInterface $current_user, EntityStorageInterface $image_style_storage, ImageProcessor $image_processor) { parent::__construct($plugin_id, $plugin_definition, $field_definition, $settings, $label, $view_mode, $third_party_settings); $this->currentUser = $current_user; $this->imageStyleStorage = $image_style_storage; + $this->imageProcessor = $image_processor; } /** @@ -85,7 +96,8 @@ public static function create(ContainerInterface $container, array $configuratio $configuration['view_mode'], $configuration['third_party_settings'], $container->get('current_user'), - $container->get('entity_type.manager')->getStorage('image_style') + $container->get('entity_type.manager')->getStorage('image_style'), + $container->get('image.processor') ); } diff --git a/core/modules/image/src/Plugin/Field/FieldFormatter/ImageUrlFormatter.php b/core/modules/image/src/Plugin/Field/FieldFormatter/ImageUrlFormatter.php index 7109627a..f6522d19 100644 --- a/core/modules/image/src/Plugin/Field/FieldFormatter/ImageUrlFormatter.php +++ b/core/modules/image/src/Plugin/Field/FieldFormatter/ImageUrlFormatter.php @@ -64,7 +64,16 @@ public function viewElements(FieldItemListInterface $items, $langcode) { /** @var \Drupal\file\FileInterface[] $images */ foreach ($images as $delta => $image) { $image_uri = $image->getFileUri(); - $url = $image_style ? $image_style->buildUrl($image_uri) : file_create_url($image_uri); + if ($image_style) { + $url = $this->imageProcessor->createInstance('derivative') + ->setImageStyle($image_style) + ->setSourceImageUri($image_uri) + ->getDerivativeImageUrl() + ->toString(); + } + else { + $url = file_create_url($image_uri); + } $url = file_url_transform_relative($url); // Add cacheability metadata from the image and image style. diff --git a/core/modules/image/src/Plugin/ImageProcessPipeline/Derivative.php b/core/modules/image/src/Plugin/ImageProcessPipeline/Derivative.php index 4c0439cb..8081428e 100644 --- a/core/modules/image/src/Plugin/ImageProcessPipeline/Derivative.php +++ b/core/modules/image/src/Plugin/ImageProcessPipeline/Derivative.php @@ -6,6 +6,7 @@ use Drupal\Core\Url; use Drupal\image\Event\ImageDerivativePipelineEvents; use Drupal\image\ImageProcessException; +use Drupal\image\ImageProcessPipelineInterface; use Drupal\image\ImageStyleInterface; /** @@ -75,19 +76,13 @@ public function setImageStyle(ImageStyleInterface $image_style): self { } /** - * Sets the 'image' variable. - * - * @param \Drupal\Core\Image\ImageInterface $image - * The ImageInterface object to be derived. - * - * @return $this + * {@inheritdoc} */ - public function setImage(ImageInterface $image): self { + public function setImage(ImageInterface $image): ImageProcessPipelineInterface { if (!$this->hasVariable('sourceImageUri')) { $this->setVariable('sourceImageUri', NULL); } - $this->setVariable('image', $image); - return $this; + return parent::setImage($image); } /** diff --git a/core/modules/image/src/Plugin/ImageProcessPipeline/ImageProcessPipelineBase.php b/core/modules/image/src/Plugin/ImageProcessPipeline/ImageProcessPipelineBase.php index 286bd4c8..3a3ddd7f 100644 --- a/core/modules/image/src/Plugin/ImageProcessPipeline/ImageProcessPipelineBase.php +++ b/core/modules/image/src/Plugin/ImageProcessPipeline/ImageProcessPipelineBase.php @@ -2,6 +2,7 @@ namespace Drupal\image\Plugin\ImageProcessPipeline; +use Drupal\Core\Image\ImageInterface; use Drupal\Core\KeyValueStore\MemoryStorage; use Drupal\Core\Plugin\PluginBase; use Drupal\image\Event\ImageProcessEvent; @@ -29,6 +30,13 @@ class ImageProcessPipelineBase extends PluginBase implements ImageProcessPipelin */ protected $variables; + /** + * The Image object being processed. + * + * @var \Drupal\Core\Image\ImageInterface + */ + protected $image; + /** * Constructs a ImageProcessPipeline plugin. * @@ -92,6 +100,28 @@ public function deleteVariable(string $variable): ImageProcessPipelineInterface return $this; } + /** + * {@inheritdoc} + */ + public function setImage(ImageInterface $image): ImageProcessPipelineInterface { + $this->image = $image; + return $this; + } + + /** + * {@inheritdoc} + */ + public function getImage(): ImageInterface { + return $this->image; + } + + /** + * {@inheritdoc} + */ + public function hasImage(): bool { + return (bool) $this->image; + } + /** * {@inheritdoc} */ diff --git a/core/modules/image/tests/src/Functional/FileMoveTest.php b/core/modules/image/tests/src/Functional/FileMoveTest.php index c2a3a1bb..f460d755 100644 --- a/core/modules/image/tests/src/Functional/FileMoveTest.php +++ b/core/modules/image/tests/src/Functional/FileMoveTest.php @@ -34,8 +34,13 @@ class FileMoveTest extends BrowserTestBase { /** * Tests moving a randomly generated image. + * + * @group legacy + * + * @expectedDeprecation The Drupal\image\Entity\ImageStyle::buildUri method is deprecated since version 9.x.x and will be removed in y.y.y. + * @expectedDeprecation The Drupal\image\Entity\ImageStyle::createDerivative method is deprecated since version 9.x.x and will be removed in y.y.y. */ - public function testNormal() { + public function testNormalLegacy() { // Pick a file for testing. $file = File::create((array) current($this->drupalGetTestFiles('image'))); @@ -61,4 +66,36 @@ public function testNormal() { $this->assertFalse(file_exists($derivative_uri), 'Make sure derivative image has been flushed.'); } + /** + * Tests moving a randomly generated image. + */ + public function testNormal() { + // Pick a file for testing. + $file = File::create((array) current($this->drupalGetTestFiles('image'))); + + // Create derivative image. + $styles = ImageStyle::loadMultiple(); + $style = reset($styles); + $original_uri = $file->getFileUri(); + $pipeline = \Drupal::service('image.processor')->createInstance('derivative') + ->setImageStyle($style) + ->setSourceImageUri($original_uri); + $derivative_uri = $pipeline->getDerivativeImageUri(); + $pipeline->buildDerivativeImage(); + + // Check if derivative image exists. + $this->assertFileExists($derivative_uri, 'Make sure derivative image is generated successfully.'); + + // Clone the object so we don't have to worry about the function changing + // our reference copy. + $desired_filepath = 'public://' . $this->randomMachineName(); + $result = file_move(clone $file, $desired_filepath, FileSystemInterface::EXISTS_ERROR); + + // Check if image has been moved. + $this->assertFileExists($result->getFileUri(), 'Make sure image is moved successfully.'); + + // Check if derivative image has been flushed. + $this->assertFileNotExists($derivative_uri, 'Make sure derivative image has been flushed.'); + } + } diff --git a/core/modules/image/tests/src/Functional/ImageAdminStylesTest.php b/core/modules/image/tests/src/Functional/ImageAdminStylesTest.php index 46c96ca2..0325226f 100644 --- a/core/modules/image/tests/src/Functional/ImageAdminStylesTest.php +++ b/core/modules/image/tests/src/Functional/ImageAdminStylesTest.php @@ -42,7 +42,10 @@ public function createSampleImage(ImageStyleInterface $style) { $file_path = \Drupal::service('file_system')->copy($file->uri, 'public://'); } - return $style->buildUrl($file_path) ? $file_path : FALSE; + $pipeline = \Drupal::service('image.processor')->createInstance('derivative') + ->setImageStyle($style) + ->setSourceImageUri($file_path); + return $pipeline->getDerivativeImageUrl() ? $file_path : FALSE; } /** @@ -339,7 +342,10 @@ public function testStyleReplacement() { // Test that image is displayed using newly created style. $this->drupalGet('node/' . $nid); - $this->assertRaw(file_url_transform_relative($style->buildUrl($original_uri)), new FormattableMarkup('Image displayed using style @style.', ['@style' => $style_name])); + $pipeline = \Drupal::service('image.processor')->createInstance('derivative') + ->setImageStyle($style) + ->setSourceImageUri($original_uri); + $this->assertRaw(file_url_transform_relative($pipeline->getDerivativeImageUrl()->toString()), new FormattableMarkup('Image displayed using style @style.', ['@style' => $style_name])); // Rename the style and make sure the image field is updated. $new_style_name = strtolower($this->randomMachineName(10)); @@ -354,7 +360,10 @@ public function testStyleReplacement() { // Reload the image style using the new name. $style = ImageStyle::load($new_style_name); - $this->assertRaw(file_url_transform_relative($style->buildUrl($original_uri)), 'Image displayed using style replacement style.'); + $pipeline = \Drupal::service('image.processor')->createInstance('derivative') + ->setImageStyle($style) + ->setSourceImageUri($original_uri); + $this->assertRaw(file_url_transform_relative($pipeline->getDerivativeImageUrl()->toString()), 'Image displayed using style replacement style.'); // Delete the style and choose a replacement style. $edit = [ @@ -366,7 +375,10 @@ public function testStyleReplacement() { $replacement_style = ImageStyle::load('thumbnail'); $this->drupalGet('node/' . $nid); - $this->assertRaw(file_url_transform_relative($replacement_style->buildUrl($original_uri)), 'Image displayed using style replacement style.'); + $pipeline = \Drupal::service('image.processor')->createInstance('derivative') + ->setImageStyle($replacement_style) + ->setSourceImageUri($original_uri); + $this->assertRaw(file_url_transform_relative($pipeline->getDerivativeImageUrl()->toString()), 'Image displayed using style replacement style.'); } /** @@ -436,8 +448,10 @@ public function testFlushUserInterface() { // Create an image to make sure it gets flushed. $files = $this->drupalGetTestFiles('image'); $image_uri = $files[0]->uri; - $derivative_uri = $style->buildUri($image_uri); - $this->assertTrue($style->createDerivative($image_uri, $derivative_uri)); + $pipeline = \Drupal::service('image.processor')->createInstance('derivative') + ->setImageStyle($style) + ->setSourceImageUri($image_uri); + $this->assertTrue($pipeline->buildDerivativeImage()); $this->assertEqual($this->getImageCount($style), 1); // Go to image styles list page and check if the flush operation link @@ -485,7 +499,10 @@ public function testConfigImport() { // Test that image is displayed using newly created style. $this->drupalGet('node/' . $nid); - $this->assertRaw(file_url_transform_relative($style->buildUrl($original_uri)), new FormattableMarkup('Image displayed using style @style.', ['@style' => $style_name])); + $pipeline = \Drupal::service('image.processor')->createInstance('derivative') + ->setImageStyle($style) + ->setSourceImageUri($original_uri); + $this->assertRaw(file_url_transform_relative($pipeline->getDerivativeImageUrl()->toString()), new FormattableMarkup('Image displayed using style @style.', ['@style' => $style_name])); // Copy config to sync, and delete the image style. $sync = $this->container->get('config.storage.sync'); diff --git a/core/modules/image/tests/src/Functional/ImageDimensionsTest.php b/core/modules/image/tests/src/Functional/ImageDimensionsTest.php index 6a969e57..c1471b75 100644 --- a/core/modules/image/tests/src/Functional/ImageDimensionsTest.php +++ b/core/modules/image/tests/src/Functional/ImageDimensionsTest.php @@ -35,8 +35,12 @@ class ImageDimensionsTest extends BrowserTestBase { /** * Test styled image dimensions cumulatively. + * + * @group legacy + * + * @expectedDeprecation The Drupal\image\Entity\ImageStyle::buildUrl method is deprecated since version 9.x.x and will be removed in y.y.y. */ - public function testImageDimensions() { + public function testLegacyImageDimensions() { $image_factory = $this->container->get('image.factory'); // Create a working copy of the file. $files = $this->drupalGetTestFiles('image'); @@ -291,6 +295,276 @@ public function testImageDimensions() { $this->assertEqual($image_file->getHeight(), 50); } + /** + * Test styled image dimensions cumulatively. + */ + public function testImageDimensions() { + $image_factory = $this->container->get('image.factory'); + // Create a working copy of the file. + $files = $this->drupalGetTestFiles('image'); + $file = reset($files); + /** @var \Drupal\Core\File\FileSystemInterface $file_system */ + $file_system = \Drupal::service('file_system'); + $original_uri = $file_system->copy($file->uri, 'public://', FileSystemInterface::EXISTS_RENAME); + + // Create a style. + /** @var $style \Drupal\image\ImageStyleInterface */ + $style = ImageStyle::create(['name' => 'test', 'label' => 'Test']); + $style->save(); + + $pipeline = \Drupal::service('image.processor')->createInstance('derivative') + ->setImageStyle($style) + ->setSourceImageUri($original_uri); + + $generated_uri = 'public://styles/test/public/' . $file_system->basename($original_uri); + $url = file_url_transform_relative($pipeline->getDerivativeImageUrl()->toString()); + + $variables = [ + '#theme' => 'image_style', + '#style_name' => 'test', + '#uri' => $original_uri, + '#width' => 40, + '#height' => 20, + ]; + // Verify that the original image matches the hard-coded values. + $image_file = $image_factory->get($original_uri); + $this->assertSame($variables['#width'], $image_file->getWidth()); + $this->assertSame($variables['#height'], $image_file->getHeight()); + + // Scale an image that is wider than it is high. + $effect = [ + 'id' => 'image_scale', + 'data' => [ + 'width' => 120, + 'height' => 90, + 'upscale' => TRUE, + ], + 'weight' => 0, + ]; + + $style->addImageEffect($effect); + $style->save(); + $this->assertSame('', $this->getImageTag($variables)); + $this->assertFileNotExists($generated_uri, 'Generated file does not exist.'); + $this->drupalGet($this->getAbsoluteUrl($url)); + $this->assertResponse(200, 'Image was generated at the URL.'); + $this->assertFileExists($generated_uri, 'Generated file does exist after we accessed it.'); + $image_file = $image_factory->get($generated_uri); + $this->assertSame(120, $image_file->getWidth()); + $this->assertSame(60, $image_file->getHeight()); + + // Rotate 90 degrees anticlockwise. + $effect = [ + 'id' => 'image_rotate', + 'data' => [ + 'degrees' => -90, + 'random' => FALSE, + ], + 'weight' => 1, + ]; + + $style->addImageEffect($effect); + $style->save(); + $this->assertSame('', $this->getImageTag($variables)); + $this->assertFileNotExists($generated_uri, 'Generated file does not exist.'); + $this->drupalGet($this->getAbsoluteUrl($url)); + $this->assertResponse(200, 'Image was generated at the URL.'); + $this->assertFileExists($generated_uri, 'Generated file does exist after we accessed it.'); + $image_file = $image_factory->get($generated_uri); + $this->assertSame(60, $image_file->getWidth()); + $this->assertSame(120, $image_file->getHeight()); + + // Scale an image that is higher than it is wide (rotated by previous effect). + $effect = [ + 'id' => 'image_scale', + 'data' => [ + 'width' => 120, + 'height' => 90, + 'upscale' => TRUE, + ], + 'weight' => 2, + ]; + + $style->addImageEffect($effect); + $style->save(); + $this->assertSame('', $this->getImageTag($variables)); + $this->assertFileNotExists($generated_uri, 'Generated file does not exist.'); + $this->drupalGet($this->getAbsoluteUrl($url)); + $this->assertResponse(200, 'Image was generated at the URL.'); + $this->assertFileExists($generated_uri, 'Generated file does exist after we accessed it.'); + $image_file = $image_factory->get($generated_uri); + $this->assertSame(45, $image_file->getWidth()); + $this->assertSame(90, $image_file->getHeight()); + + // Test upscale disabled. + $effect = [ + 'id' => 'image_scale', + 'data' => [ + 'width' => 400, + 'height' => 200, + 'upscale' => FALSE, + ], + 'weight' => 3, + ]; + + $style->addImageEffect($effect); + $style->save(); + $this->assertSame('', $this->getImageTag($variables)); + $this->assertFileNotExists($generated_uri, 'Generated file does not exist.'); + $this->drupalGet($this->getAbsoluteUrl($url)); + $this->assertResponse(200, 'Image was generated at the URL.'); + $this->assertFileExists($generated_uri, 'Generated file does exist after we accessed it.'); + $image_file = $image_factory->get($generated_uri); + $this->assertSame(45, $image_file->getWidth()); + $this->assertSame(90, $image_file->getHeight()); + + // Add a desaturate effect. + $effect = [ + 'id' => 'image_desaturate', + 'data' => [], + 'weight' => 4, + ]; + + $style->addImageEffect($effect); + $style->save(); + $this->assertSame('', $this->getImageTag($variables)); + $this->assertFileNotExists($generated_uri, 'Generated file does not exist.'); + $this->drupalGet($this->getAbsoluteUrl($url)); + $this->assertResponse(200, 'Image was generated at the URL.'); + $this->assertFileExists($generated_uri, 'Generated file does exist after we accessed it.'); + $image_file = $image_factory->get($generated_uri); + $this->assertSame(45, $image_file->getWidth()); + $this->assertSame(90, $image_file->getHeight()); + + // Add a random rotate effect. + $effect = [ + 'id' => 'image_rotate', + 'data' => [ + 'degrees' => 180, + 'random' => TRUE, + ], + 'weight' => 5, + ]; + + $style->addImageEffect($effect); + $style->save(); + $this->assertSame('', $this->getImageTag($variables)); + $this->assertFileNotExists($generated_uri, 'Generated file does not exist.'); + $this->drupalGet($this->getAbsoluteUrl($url)); + $this->assertResponse(200, 'Image was generated at the URL.'); + $this->assertFileExists($generated_uri, 'Generated file does exist after we accessed it.'); + + // Add a crop effect. + $effect = [ + 'id' => 'image_crop', + 'data' => [ + 'width' => 30, + 'height' => 30, + 'anchor' => 'center-center', + ], + 'weight' => 6, + ]; + + $style->addImageEffect($effect); + $style->save(); + $this->assertEqual('', $this->getImageTag($variables)); + $this->assertFileNotExists($generated_uri, 'Generated file does not exist.'); + $this->drupalGet($this->getAbsoluteUrl($url)); + $this->assertResponse(200, 'Image was generated at the URL.'); + $this->assertFileExists($generated_uri, 'Generated file does exist after we accessed it.'); + $image_file = $image_factory->get($generated_uri); + $this->assertSame(30, $image_file->getWidth()); + $this->assertSame(30, $image_file->getHeight()); + + // Rotate to a non-multiple of 90 degrees. + $effect = [ + 'id' => 'image_rotate', + 'data' => [ + 'degrees' => 57, + 'random' => FALSE, + ], + 'weight' => 7, + ]; + + $effect_id = $style->addImageEffect($effect); + $style->save(); + // @todo Uncomment this once + // https://www.drupal.org/project/drupal/issues/2670966 is resolved. + // $this->assertSame('', $this->getImageTag($variables)); + $this->assertFileNotExists($generated_uri, 'Generated file does not exist.'); + $this->drupalGet($this->getAbsoluteUrl($url)); + $this->assertResponse(200, 'Image was generated at the URL.'); + $this->assertFileExists($generated_uri, 'Generated file does exist after we accessed it.'); + $image_file = $image_factory->get($generated_uri); + // @todo Uncomment this once + // https://www.drupal.org/project/drupal/issues/2670966 is resolved. + // $this->assertSame(41, $image_file->getWidth()); + // $this->assertSame(41, $image_file->getHeight()); + + $effect_plugin = $style->getEffect($effect_id); + $style->deleteImageEffect($effect_plugin); + + // Ensure that an effect can unset dimensions. + $effect = [ + 'id' => 'image_module_test_null', + 'data' => [], + 'weight' => 8, + ]; + + $style->addImageEffect($effect); + $style->save(); + $this->assertSame('', $this->getImageTag($variables)); + + // Test URI dependent image effect. + $style = ImageStyle::create(['name' => 'test_uri', 'label' => 'Test URI']); + $effect = [ + 'id' => 'image_module_test_uri_dependent', + 'data' => [], + 'weight' => 0, + ]; + $style->addImageEffect($effect); + $style->save(); + $variables = [ + '#theme' => 'image_style', + '#style_name' => 'test_uri', + '#uri' => $original_uri, + '#width' => 40, + '#height' => 20, + ]; + // PNG original image. Should be resized to 100x100. + $pipeline = \Drupal::service('image.processor')->createInstance('derivative') + ->setImageStyle($style) + ->setSourceImageUri($original_uri); + $generated_uri = 'public://styles/test_uri/public/' . $file_system->basename($original_uri); + $url = file_url_transform_relative($pipeline->getDerivativeImageUrl()->toString()); + + $this->assertSame('', $this->getImageTag($variables)); + $this->assertFileNotExists($generated_uri, 'Generated file does not exist.'); + $this->drupalGet($this->getAbsoluteUrl($url)); + $this->assertResponse(200, 'Image was generated at the URL.'); + $this->assertFileExists($generated_uri, 'Generated file does exist after we accessed it.'); + $image_file = $image_factory->get($generated_uri); + $this->assertSame(100, $image_file->getWidth()); + $this->assertSame(100, $image_file->getHeight()); + // GIF original image. Should be resized to 50x50. + $file = $files[1]; + $original_uri = $file_system->copy($file->uri, 'public://', FileSystemInterface::EXISTS_RENAME); + $pipeline = \Drupal::service('image.processor')->createInstance('derivative') + ->setImageStyle($style) + ->setSourceImageUri($original_uri); + $generated_uri = 'public://styles/test_uri/public/' . $file_system->basename($original_uri); + $url = file_url_transform_relative($pipeline->getDerivativeImageUrl()->toString()); + $variables['#uri'] = $original_uri; + $this->assertSame('', $this->getImageTag($variables)); + $this->assertFileNotExists($generated_uri, 'Generated file does not exist.'); + $this->drupalGet($this->getAbsoluteUrl($url)); + $this->assertResponse(200, 'Image was generated at the URL.'); + $this->assertFileExists($generated_uri, 'Generated file does exist after we accessed it.'); + $image_file = $image_factory->get($generated_uri); + $this->assertSame(50, $image_file->getWidth()); + $this->assertSame(50, $image_file->getHeight()); + } + /** * Render an image style element. * diff --git a/core/modules/image/tests/src/Functional/ImageFieldDisplayTest.php b/core/modules/image/tests/src/Functional/ImageFieldDisplayTest.php index 96baad54..ef8f8a10 100644 --- a/core/modules/image/tests/src/Functional/ImageFieldDisplayTest.php +++ b/core/modules/image/tests/src/Functional/ImageFieldDisplayTest.php @@ -195,7 +195,10 @@ public function _testImageFieldFormatters($scheme) { // Ensure the derivative image is generated so we do not have to deal with // image style callback paths. - $this->drupalGet(ImageStyle::load('thumbnail')->buildUrl($image_uri)); + $pipeline = \Drupal::service('image.processor')->createInstance('derivative') + ->setImageStyle(ImageStyle::load('thumbnail')) + ->setSourceImageUri($image_uri); + $this->drupalGet($pipeline->getDerivativeImageUrl()->toString()); $image_style = [ '#theme' => 'image_style', '#uri' => $image_uri, @@ -213,7 +216,7 @@ public function _testImageFieldFormatters($scheme) { if ($scheme == 'private') { // Log out and try to access the file. $this->drupalLogout(); - $this->drupalGet(ImageStyle::load('thumbnail')->buildUrl($image_uri)); + $this->drupalGet($pipeline->getDerivativeImageUrl()->toString()); $this->assertResponse('403', 'Access denied to image style thumbnail as anonymous user.'); } @@ -227,7 +230,7 @@ public function _testImageFieldFormatters($scheme) { // Test the image URL formatter with an image style. $display_options['settings']['image_style'] = 'thumbnail'; - $expected_url = file_url_transform_relative(ImageStyle::load('thumbnail')->buildUrl($image_uri)); + $expected_url = file_url_transform_relative($pipeline->getDerivativeImageUrl()->toString()); $this->assertEqual($expected_url, $node->{$field_name}->view($display_options)[0]['#markup']); } @@ -286,7 +289,10 @@ public function testImageFieldSettings() { $node = $node_storage->load($nid); $file = $node->{$field_name}->entity; - $url = file_url_transform_relative(ImageStyle::load('medium')->buildUrl($file->getFileUri())); + $pipeline = \Drupal::service('image.processor')->createInstance('derivative') + ->setImageStyle(ImageStyle::load('medium')) + ->setSourceImageUri($file->getFileUri()); + $url = file_url_transform_relative($pipeline->getDerivativeImageUrl()->toString()); $this->assertSession()->elementExists('css', 'img[width=40][height=20][class=image-style-medium][src="' . $url . '"]'); // Add alt/title fields to the image and verify that they are displayed. diff --git a/core/modules/image/tests/src/Functional/ImageStyleFlushTest.php b/core/modules/image/tests/src/Functional/ImageStyleFlushTest.php index a5036eec..28aab49a 100644 --- a/core/modules/image/tests/src/Functional/ImageStyleFlushTest.php +++ b/core/modules/image/tests/src/Functional/ImageStyleFlushTest.php @@ -37,8 +37,11 @@ public function createSampleImage($style, $wrapper) { // Make sure we have an image in our wrapper testing file directory. $source_uri = \Drupal::service('file_system')->copy($file->uri, $wrapper . '://'); // Build the derivative image. - $derivative_uri = $style->buildUri($source_uri); - $derivative = $style->createDerivative($source_uri, $derivative_uri); + $pipeline = \Drupal::service('image.processor')->createInstance('derivative') + ->setImageStyle($style) + ->setSourceImageUri($source_uri); + $derivative_uri = $pipeline->getDerivativeImageUri(); + $derivative = $pipeline->buildDerivativeImage(); return $derivative ? $derivative_uri : FALSE; } diff --git a/core/modules/image/tests/src/Functional/ImageStylesPathAndUrlTest.php b/core/modules/image/tests/src/Functional/ImageStylesPathAndUrlTest.php index 194f6e57..417f5128 100644 --- a/core/modules/image/tests/src/Functional/ImageStylesPathAndUrlTest.php +++ b/core/modules/image/tests/src/Functional/ImageStylesPathAndUrlTest.php @@ -58,8 +58,12 @@ protected function setUp() { /** * Tests \Drupal\image\ImageStyleInterface::buildUri(). + * + * @group legacy + * + * @expectedDeprecation The Drupal\image\Entity\ImageStyle::buildUri method is deprecated since version 9.x.x and will be removed in y.y.y. */ - public function testImageStylePath() { + public function testLegacyImageStylePath() { $scheme = 'public'; $actual = $this->style->buildUri("$scheme://foo/bar.gif"); $expected = "$scheme://styles/" . $this->style->id() . "/$scheme/foo/bar.gif"; @@ -72,57 +76,96 @@ public function testImageStylePath() { /** * Tests an image style URL using the "public://" scheme. + * + * @group legacy + * + * @expectedDeprecation The Drupal\image\Entity\ImageStyle::buildUri method is deprecated since version 9.x.x and will be removed in y.y.y. + * @expectedDeprecation The Drupal\image\Entity\ImageStyle::buildUrl method is deprecated since version 9.x.x and will be removed in y.y.y. */ - public function testImageStyleUrlAndPathPublic() { - $this->doImageStyleUrlAndPathTests('public'); + public function testLegacyImageStyleUrlAndPathPublic() { + $this->doLegacyImageStyleUrlAndPathTests('public'); } /** * Tests an image style URL using the "private://" scheme. + * + * @group legacy + * + * @expectedDeprecation The Drupal\image\Entity\ImageStyle::buildUri method is deprecated since version 9.x.x and will be removed in y.y.y. + * @expectedDeprecation The Drupal\image\Entity\ImageStyle::buildUrl method is deprecated since version 9.x.x and will be removed in y.y.y. */ - public function testImageStyleUrlAndPathPrivate() { - $this->doImageStyleUrlAndPathTests('private'); + public function testLegacyImageStyleUrlAndPathPrivate() { + $this->doLegacyImageStyleUrlAndPathTests('private'); } /** * Tests an image style URL with the "public://" scheme and unclean URLs. + * + * @group legacy + * + * @expectedDeprecation The Drupal\image\Entity\ImageStyle::buildUri method is deprecated since version 9.x.x and will be removed in y.y.y. + * @expectedDeprecation The Drupal\image\Entity\ImageStyle::buildUrl method is deprecated since version 9.x.x and will be removed in y.y.y. */ - public function testImageStyleUrlAndPathPublicUnclean() { - $this->doImageStyleUrlAndPathTests('public', FALSE); + public function testLegacyImageStyleUrlAndPathPublicUnclean() { + $this->doLegacyImageStyleUrlAndPathTests('public', FALSE); } /** * Tests an image style URL with the "private://" schema and unclean URLs. + * + * @group legacy + * + * @expectedDeprecation The Drupal\image\Entity\ImageStyle::buildUri method is deprecated since version 9.x.x and will be removed in y.y.y. + * @expectedDeprecation The Drupal\image\Entity\ImageStyle::buildUrl method is deprecated since version 9.x.x and will be removed in y.y.y. */ - public function testImageStyleUrlAndPathPrivateUnclean() { - $this->doImageStyleUrlAndPathTests('private', FALSE); + public function testLegacyImageStyleUrlAndPathPrivateUnclean() { + $this->doLegacyImageStyleUrlAndPathTests('private', FALSE); } /** * Tests an image style URL with the "public://" schema and language prefix. + * + * @group legacy + * + * @expectedDeprecation The Drupal\image\Entity\ImageStyle::buildUri method is deprecated since version 9.x.x and will be removed in y.y.y. + * @expectedDeprecation The Drupal\image\Entity\ImageStyle::buildUrl method is deprecated since version 9.x.x and will be removed in y.y.y. */ - public function testImageStyleUrlAndPathPublicLanguage() { - $this->doImageStyleUrlAndPathTests('public', TRUE, TRUE, 'fr'); + public function testLegacyImageStyleUrlAndPathPublicLanguage() { + $this->doLegacyImageStyleUrlAndPathTests('public', TRUE, TRUE, 'fr'); } /** * Tests an image style URL with the "private://" schema and language prefix. + * + * @group legacy + * + * @expectedDeprecation The Drupal\image\Entity\ImageStyle::buildUri method is deprecated since version 9.x.x and will be removed in y.y.y. + * @expectedDeprecation The Drupal\image\Entity\ImageStyle::buildUrl method is deprecated since version 9.x.x and will be removed in y.y.y. */ - public function testImageStyleUrlAndPathPrivateLanguage() { - $this->doImageStyleUrlAndPathTests('private', TRUE, TRUE, 'fr'); + public function testLegacyImageStyleUrlAndPathPrivateLanguage() { + $this->doLegacyImageStyleUrlAndPathTests('private', TRUE, TRUE, 'fr'); } /** * Tests an image style URL with a file URL that has an extra slash in it. + * + * @group legacy + * + * @expectedDeprecation The Drupal\image\Entity\ImageStyle::buildUri method is deprecated since version 9.x.x and will be removed in y.y.y. + * @expectedDeprecation The Drupal\image\Entity\ImageStyle::buildUrl method is deprecated since version 9.x.x and will be removed in y.y.y. */ - public function testImageStyleUrlExtraSlash() { - $this->doImageStyleUrlAndPathTests('public', TRUE, TRUE); + public function testLegacyImageStyleUrlExtraSlash() { + $this->doLegacyImageStyleUrlAndPathTests('public', TRUE, TRUE); } /** * Tests that an invalid source image returns a 404. + * + * @group legacy + * + * @expectedDeprecation The Drupal\image\Entity\ImageStyle::buildUrl method is deprecated since version 9.x.x and will be removed in y.y.y. */ - public function testImageStyleUrlForMissingSourceImage() { + public function testLegacyImageStyleUrlForMissingSourceImage() { $non_existent_uri = 'public://foo.png'; $generated_url = $this->style->buildUrl($non_existent_uri); $this->drupalGet($generated_url); @@ -131,8 +174,10 @@ public function testImageStyleUrlForMissingSourceImage() { /** * Tests building an image style URL. + * + * @todo This is a legacy method, remove in y.y.y. */ - public function doImageStyleUrlAndPathTests($scheme, $clean_url = TRUE, $extra_slash = FALSE, $langcode = FALSE) { + public function doLegacyImageStyleUrlAndPathTests($scheme, $clean_url = TRUE, $extra_slash = FALSE, $langcode = FALSE) { $this->prepareRequestForGenerator($clean_url); // Make the default scheme neither "public" nor "private" to verify the @@ -164,7 +209,7 @@ public function doImageStyleUrlAndPathTests($scheme, $clean_url = TRUE, $extra_s // Get the URL of a file that has not been generated and try to create it. $generated_uri = $this->style->buildUri($original_uri); - $this->assertFalse(file_exists($generated_uri), 'Generated file does not exist.'); + $this->assertFileNotExists($generated_uri); $generate_url = $this->style->buildUrl($original_uri, $clean_url); // Make sure that language prefix is never added to the image style URL. @@ -326,4 +371,273 @@ public function doImageStyleUrlAndPathTests($scheme, $clean_url = TRUE, $extra_s $this->assertFalse(file_exists($directory), 'New directory was not created in the filesystem when requesting an unauthorized image.'); } + /** + * @covers \Drupal\image\Plugin\ImageProcessPipeline\Derivative::getDerivativeImageUri + */ + public function testImageStylePath() { + $scheme = 'public'; + + $pipeline = \Drupal::service('image.processor')->createInstance('derivative') + ->setImageStyle($this->style) + ->setSourceImageUri("$scheme://foo/bar.gif"); + // Got the path for a file URI. + $this->assertSame("$scheme://styles/" . $this->style->id() . "/$scheme/foo/bar.gif", $pipeline->getDerivativeImageUri()); + + $pipeline = \Drupal::service('image.processor')->createInstance('derivative') + ->setImageStyle($this->style) + ->setSourceImageUri('foo/bar.gif'); + // Got the path for a relative file path. + $this->assertSame("$scheme://styles/" . $this->style->id() . "/$scheme/foo/bar.gif", $pipeline->getDerivativeImageUri()); + } + + /** + * Tests that an invalid source image returns a 404. + */ + public function testImageStyleUrlForMissingSourceImage() { + $non_existent_uri = 'public://foo.png'; + $generated_url = \Drupal::service('image.processor')->createInstance('derivative') + ->setImageStyle($this->style) + ->setSourceImageUri($non_existent_uri) + ->getDerivativeImageUrl() + ->toString(); + $this->drupalGet($generated_url); + $this->assertResponse(404, 'Accessing an image style URL with a source image that does not exist provides a 404 error response.'); + } + + /** + * Provides setup data for the test method. + */ + public function providerImageStyleUrlAndPath() { + return [ + 'public scheme' => ['public', TRUE, FALSE, NULL], + 'private scheme' => ['private', TRUE, FALSE, NULL], + 'public scheme, unclean URLs' => ['public', FALSE, FALSE, NULL], + 'private scheme, unclean URLs' => ['private', FALSE, FALSE, NULL], + 'public scheme, language prefix' => ['public', TRUE, TRUE, 'fr'], + 'private scheme, language prefix' => ['private', TRUE, TRUE, 'fr'], + 'public scheme, extra slash' => ['public', TRUE, TRUE, NULL], + ]; + } + + /** + * Tests building image style URLs. + * + * @dataProvider providerImageStyleUrlAndPath + */ + public function testImageStyleUrlAndPath(string $scheme, bool $clean_url, bool $extra_slash, ?string $langcode) { + $this->prepareRequestForGenerator($clean_url); + + // Make the default scheme neither "public" nor "private" to verify the + // functions work for other than the default scheme. + $this->config('system.file')->set('default_scheme', 'temporary')->save(); + + // Create the directories for the styles. + $directory = $scheme . '://styles/' . $this->style->id(); + $this->assertNotFalse(\Drupal::service('file_system')->prepareDirectory($directory, FileSystemInterface::CREATE_DIRECTORY)); + + // Override the language to build the URL for the correct language. + if ($langcode) { + $language_manager = \Drupal::service('language_manager'); + $language = $language_manager->getLanguage($langcode); + $language_manager->setConfigOverrideLanguage($language); + } + + // Create a working copy of the file. + $files = $this->drupalGetTestFiles('image'); + $file = array_shift($files); + /** @var \Drupal\Core\File\FileSystemInterface $file_system */ + $file_system = \Drupal::service('file_system'); + $original_uri = $file_system->copy($file->uri, $scheme . '://', FileSystemInterface::EXISTS_RENAME); + // Let the image_module_test module know about this file, so it can claim + // ownership in hook_file_download(). + \Drupal::state()->set('image.test_file_download', $original_uri); + $this->assertNotFalse($original_uri, 'Created the generated image file.'); + + // Get the URL of a file that has not been generated and try to create it. + $pipeline = \Drupal::service('image.processor')->createInstance('derivative') + ->setImageStyle($this->style) + ->setSourceImageUri($original_uri) + ->setCleanUrl($clean_url); + $generated_uri = $pipeline->getDerivativeImageUri(); + $this->assertFileNotExists($generated_uri); + $generate_url = $pipeline->getDerivativeImageUrl()->toString(); + $this->assertFileNotExists($generated_uri); + + // Make sure that language prefix is never added to the image style URL. + if ($langcode) { + $this->assertNotContains("/$langcode/", $generate_url); + } + + // Ensure that the tests still pass when the file is generated by accessing + // a poorly constructed (but still valid) file URL that has an extra slash + // in it. + if ($extra_slash) { + $modified_uri = str_replace('://', ':///', $original_uri); + // Check that an extra slash was added to the generated file URI. + $this->assertNotSame($original_uri, $modified_uri); + $pipeline = \Drupal::service('image.processor')->createInstance('derivative') + ->setImageStyle($this->style) + ->setSourceImageUri($modified_uri) + ->setCleanUrl($clean_url); + $generate_url = $pipeline->getDerivativeImageUrl()->toString(); + } + + // When using non-clean URLS, the system path contains the script name. + if (!$clean_url) { + $this->assertContains('index.php/', $generate_url); + } + + // Add some extra chars to the token. + $this->drupalGet(str_replace(IMAGE_DERIVATIVE_TOKEN . '=', IMAGE_DERIVATIVE_TOKEN . '=Zo', $generate_url)); + $this->assertResponse(404, 'Image was inaccessible at the URL with an invalid token.'); + // Change the parameter name so the token is missing. + $this->drupalGet(str_replace(IMAGE_DERIVATIVE_TOKEN . '=', 'wrongparam=', $generate_url)); + $this->assertResponse(404, 'Image was inaccessible at the URL with a missing token.'); + + // Check that the generated URL is the same when we pass in a relative path + // rather than a URI. We need to temporarily switch the default scheme to + // match the desired scheme before testing this, then switch it back to the + // "temporary" scheme used throughout this test afterwards. + $this->config('system.file')->set('default_scheme', $scheme)->save(); + $relative_path = StreamWrapperManager::getTarget($original_uri); + $pipeline = \Drupal::service('image.processor')->createInstance('derivative') + ->setImageStyle($this->style) + ->setSourceImageUri($relative_path) + ->setCleanUrl($clean_url); + $generate_url_from_relative_path = $pipeline->getDerivativeImageUrl()->toString(); + $this->assertSame($generate_url, $generate_url_from_relative_path); + $this->config('system.file')->set('default_scheme', 'temporary')->save(); + + // Fetch the URL that generates the file. + $this->drupalGet($generate_url); + $this->assertResponse(200, 'Image was generated at the URL.'); + $this->assertFileExists($generated_uri); + // assertRaw can't be used with string containing non UTF-8 chars. + $this->assertNotEmpty(file_get_contents($generated_uri), 'URL returns expected file.'); + $image = $this->container->get('image.factory')->get($generated_uri); + $this->assertSame($image->getMimeType(), $this->drupalGetHeader('Content-Type'), 'Expected Content-Type was reported.'); + $this->assertSame($image->getFileSize(), (int) $this->drupalGetHeader('Content-Length'), 'Expected Content-Length was reported.'); + + // Check that we did not download the original file. + $original_image = $this->container->get('image.factory') + ->get($original_uri); + $this->assertNotSame($original_image->getFileSize(), (int) $this->drupalGetHeader('Content-Length')); + + if ($scheme == 'private') { + $this->assertSame('Sun, 19 Nov 1978 05:00:00 GMT', $this->drupalGetHeader('Expires')); + $this->assertContains('no-cache', $this->drupalGetHeader('Cache-Control')); + $this->assertSame('image_module_test', $this->drupalGetHeader('X-Image-Owned-By')); + + // Make sure that a second request to the already existing derivative + // works too. + $this->drupalGet($generate_url); + $this->assertResponse(200, 'Image was generated at the URL.'); + + // Check that the second request also returned the generated image. + $this->assertSame($image->getFileSize(), (int) $this->drupalGetHeader('Content-Length')); + + // Check that we did not download the original file. + $this->assertNotSame($original_image->getFileSize(), (int) $this->drupalGetHeader('Content-Length')); + + // Make sure that access is denied for existing style files if we do not + // have access. + \Drupal::state()->delete('image.test_file_download'); + $this->drupalGet($generate_url); + $this->assertResponse(403, 'Confirmed that access is denied for the private image style.'); + + // Repeat this with a different file that we do not have access to and + // make sure that access is denied. + $file_noaccess = array_shift($files); + $original_uri_noaccess = $file_system->copy($file_noaccess->uri, $scheme . '://', FileSystemInterface::EXISTS_RENAME); + $generated_uri_noaccess = $scheme . '://styles/' . $this->style->id() . '/' . $scheme . '/' . $file_system->basename($original_uri_noaccess); + $this->assertFileNotExists($generated_uri_noaccess); + $pipeline = \Drupal::service('image.processor')->createInstance('derivative') + ->setImageStyle($this->style) + ->setSourceImageUri($original_uri_noaccess); + $generate_url_noaccess = $pipeline->getDerivativeImageUrl()->toString(); + + $this->drupalGet($generate_url_noaccess); + $this->assertResponse(403, 'Confirmed that access is denied for the private image style.'); + // Verify that images are not appended to the response. + // Currently this test only uses PNG images. + $this->assertContains('.png', $generate_url, 'Confirming that private image styles are not appended require PNG file.'); + // Check for PNG-Signature + // (cf. http://www.libpng.org/pub/png/book/chapter08.html#png.ch08.div.2) + // in the response body. + $raw = $this->getSession()->getPage()->getContent(); + $this->assertNotContains(chr(137) . chr(80) . chr(78) . chr(71) . chr(13) . chr(10) . chr(26) . chr(10), $raw); + } + else { + $this->assertSame('Sun, 19 Nov 1978 05:00:00 GMT', $this->drupalGetHeader('Expires'), 'Expires header was sent.'); + $this->assertNotContains('no-cache', $this->drupalGetHeader('Cache-Control')); + + if ($clean_url) { + // Add some extra chars to the token. + $this->drupalGet(str_replace(IMAGE_DERIVATIVE_TOKEN . '=', IMAGE_DERIVATIVE_TOKEN . '=Zo', $generate_url)); + $this->assertResponse(200, 'Existing image was accessible at the URL with an invalid token.'); + } + } + + // Allow insecure image derivatives to be created for the remainder of this + // test. + $this->config('image.settings') + ->set('allow_insecure_derivatives', TRUE) + ->save(); + + // Create another working copy of the file. + $files = $this->drupalGetTestFiles('image'); + $file = array_shift($files); + $original_uri = $file_system->copy($file->uri, $scheme . '://', FileSystemInterface::EXISTS_RENAME); + // Let the image_module_test module know about this file, so it can claim + // ownership in hook_file_download(). + \Drupal::state()->set('image.test_file_download', $original_uri); + + // Suppress the security token in the URL, then get the URL of a file that + // has not been created and try to create it. Check that the security token + // is not present in the URL but that the image is still accessible. + $this->config('image.settings')->set('suppress_itok_output', TRUE)->save(); + $pipeline = \Drupal::service('image.processor')->createInstance('derivative') + ->setImageStyle($this->style) + ->setSourceImageUri($original_uri) + ->setCleanUrl($clean_url); + $generated_uri = $pipeline->getDerivativeImageUri(); + $this->assertFileNotExists($generated_uri, 'Generated file does not exist.'); + $generate_url = $pipeline->getDerivativeImageUrl()->toString(); + $this->assertNotContains(IMAGE_DERIVATIVE_TOKEN . '=', $generate_url, 'The security token does not appear in the image style URL.'); + $this->drupalGet($generate_url); + $this->assertResponse(200, 'Image was accessible at the URL with a missing token.'); + + // Stop suppressing the security token in the URL. + $this->config('image.settings')->set('suppress_itok_output', FALSE)->save(); + // Ensure allow_insecure_derivatives is enabled. + $this->assertTrue($this->config('image.settings')->get('allow_insecure_derivatives')); + // Check that a security token is still required when generating a second + // image derivative using the first one as a source. + $pipeline = \Drupal::service('image.processor')->createInstance('derivative') + ->setImageStyle($this->style) + ->setSourceImageUri($generated_uri) + ->setCleanUrl($clean_url); + $nested_url = $pipeline->getDerivativeImageUrl()->toString(); + $matches_expected_url_format = (boolean) preg_match('/styles\/' . $this->style->id() . '\/' . $scheme . '\/styles\/' . $this->style->id() . '\/' . $scheme . '/', $nested_url); + $this->assertTrue($matches_expected_url_format, "URL for a derivative of an image style matches expected format."); + $nested_url_with_wrong_token = str_replace(IMAGE_DERIVATIVE_TOKEN . '=', 'wrongparam=', $nested_url); + $this->drupalGet($nested_url_with_wrong_token); + $this->assertResponse(404, 'Image generated from an earlier derivative was inaccessible at the URL with a missing token.'); + // Check that this restriction cannot be bypassed by adding extra slashes + // to the URL. + $this->drupalGet(substr_replace($nested_url_with_wrong_token, '//styles/', strrpos($nested_url_with_wrong_token, '/styles/'), strlen('/styles/'))); + $this->assertResponse(404, 'Image generated from an earlier derivative was inaccessible at the URL with a missing token, even with an extra forward slash in the URL.'); + $this->drupalGet(substr_replace($nested_url_with_wrong_token, '////styles/', strrpos($nested_url_with_wrong_token, '/styles/'), strlen('/styles/'))); + $this->assertResponse(404, 'Image generated from an earlier derivative was inaccessible at the URL with a missing token, even with multiple forward slashes in the URL.'); + // Make sure the image can still be generated if a correct token is used. + $this->drupalGet($nested_url); + $this->assertResponse(200, 'Image was accessible when a correct token was provided in the URL.'); + + // Check that requesting a nonexistent image does not create any new + // directories in the file system. + $directory = $scheme . '://styles/' . $this->style->id() . '/' . $scheme . '/' . $this->randomMachineName(); + $this->drupalGet(file_create_url($directory . '/' . $this->randomString())); + $this->assertFileNotExists($directory, 'New directory was not created in the filesystem when requesting an unauthorized image.'); + } + } diff --git a/core/modules/image/tests/src/Kernel/ImageStyleCustomStreamWrappersTest.php b/core/modules/image/tests/src/Kernel/ImageStyleCustomStreamWrappersTest.php index 3e0b345a..8b16c728 100644 --- a/core/modules/image/tests/src/Kernel/ImageStyleCustomStreamWrappersTest.php +++ b/core/modules/image/tests/src/Kernel/ImageStyleCustomStreamWrappersTest.php @@ -75,7 +75,11 @@ public function register(ContainerBuilder $container) { * The derivative expected stream wrapper scheme. */ public function testCustomStreamWrappers($source_scheme, $expected_scheme) { - $derivative_uri = $this->imageStyle->buildUri("$source_scheme://some/path/image.png"); + // Create an image process pipeline. + $pipeline = \Drupal::service('image.processor')->createInstance('derivative') + ->setImageStyle($this->imageStyle) + ->setSourceImageUri("$source_scheme://some/path/image.png"); + $derivative_uri = $pipeline->getDerivativeImageUri(); $derivative_scheme = StreamWrapperManager::getScheme($derivative_uri); // Check that the derivative scheme is the expected scheme. diff --git a/core/modules/image/tests/src/Kernel/ImageThemeFunctionTest.php b/core/modules/image/tests/src/Kernel/ImageThemeFunctionTest.php index 0d219694..adaa5e46 100644 --- a/core/modules/image/tests/src/Kernel/ImageThemeFunctionTest.php +++ b/core/modules/image/tests/src/Kernel/ImageThemeFunctionTest.php @@ -86,7 +86,12 @@ public function testImageFormatterTheme() { // Create a style. $style = ImageStyle::create(['name' => 'test', 'label' => 'Test']); $style->save(); - $url = file_url_transform_relative($style->buildUrl($original_uri)); + + // Create an image process pipeline. + $pipeline = \Drupal::service('image.processor')->createInstance('derivative') + ->setImageStyle($style) + ->setSourceImageUri($original_uri); + $url = file_url_transform_relative($pipeline->getDerivativeImageUrl()->toString()); // Create a test entity with the image field set. $entity = EntityTest::create(); @@ -148,7 +153,12 @@ public function testImageStyleTheme() { // Create a style. $style = ImageStyle::create(['name' => 'image_test', 'label' => 'Test']); $style->save(); - $url = file_url_transform_relative($style->buildUrl($original_uri)); + + // Create an image process pipeline. + $pipeline = \Drupal::service('image.processor')->createInstance('derivative') + ->setImageStyle($style) + ->setSourceImageUri($original_uri); + $url = file_url_transform_relative($pipeline->getDerivativeImageUrl()->toString()); // Create the base element that we'll use in the tests below. $base_element = [ diff --git a/core/modules/rdf/tests/src/Functional/ImageFieldAttributesTest.php b/core/modules/rdf/tests/src/Functional/ImageFieldAttributesTest.php index bd7eab7d..91994b53 100644 --- a/core/modules/rdf/tests/src/Functional/ImageFieldAttributesTest.php +++ b/core/modules/rdf/tests/src/Functional/ImageFieldAttributesTest.php @@ -107,7 +107,11 @@ public function testNodeTeaser() { // Construct the node and image URIs for testing. $node_uri = $this->node->toUrl('canonical', ['absolute' => TRUE])->toString(); - $image_uri = ImageStyle::load('medium')->buildUrl($this->file->getFileUri()); + $image_uri = \Drupal::service('image.processor')->createInstance('derivative') + ->setImageStyle(ImageStyle::load('medium')) + ->setSourceImageUri($this->file->getFileUri()) + ->getDerivativeImageUrl() + ->toString(); // Test relations from node to image. $expected_value = [ diff --git a/core/modules/rdf/tests/src/Functional/StandardProfileTest.php b/core/modules/rdf/tests/src/Functional/StandardProfileTest.php index 93d78a93..305618a5 100644 --- a/core/modules/rdf/tests/src/Functional/StandardProfileTest.php +++ b/core/modules/rdf/tests/src/Functional/StandardProfileTest.php @@ -169,7 +169,11 @@ protected function setUp() { // Set URIs. // Image. $image_file = $this->article->get('field_image')->entity; - $this->imageUri = ImageStyle::load('large')->buildUrl($image_file->getFileUri()); + $this->imageUri = \Drupal::service('image.processor')->createInstance('derivative') + ->setImageStyle(ImageStyle::load('large')) + ->setSourceImageUri($image_file->getFileUri()) + ->getDerivativeImageUrl() + ->toString(); // Term. $this->termUri = $this->term->toUrl('canonical', ['absolute' => TRUE])->toString(); // Article. @@ -228,7 +232,11 @@ protected function doFrontPageRdfaTests() { // @todo Once the image points to the original instead of the processed // image, move this to testArticleProperties(). $image_file = $this->article->get('field_image')->entity; - $image_uri = ImageStyle::load('medium')->buildUrl($image_file->getFileUri()); + $image_uri = \Drupal::service('image.processor')->createInstance('derivative') + ->setImageStyle(ImageStyle::load('medium')) + ->setSourceImageUri($image_file->getFileUri()) + ->getDerivativeImageUrl() + ->toString(); $expected_value = [ 'type' => 'uri', 'value' => $image_uri, diff --git a/core/modules/responsive_image/responsive_image.module b/core/modules/responsive_image/responsive_image.module index 36ea84d2..47ae6e9a 100644 --- a/core/modules/responsive_image/responsive_image.module +++ b/core/modules/responsive_image/responsive_image.module @@ -448,7 +448,13 @@ function responsive_image_get_image_dimensions($image_style_name, array $dimensi ]; } elseif ($entity = ImageStyle::load($image_style_name)) { - $entity->transformDimensions($dimensions, $uri); + $pipeline = \Drupal::service('image.processor')->createInstance('derivative'); + $pipeline + ->setImageStyle($entity) + ->setSourceImageUri($uri) + ->setSourceImageDimensions($dimensions['width'] ?? NULL, $dimensions['height'] ?? NULL); + $dimensions['width'] = $pipeline->getDerivativeImageWidth(); + $dimensions['height'] = $pipeline->getDerivativeImageHeight(); } return $dimensions; @@ -475,7 +481,10 @@ function responsive_image_get_mime_type($image_style_name, $extension) { $fake_path = 'responsive_image.' . $extension; } else { - $fake_path = 'responsive_image.' . ImageStyle::load($image_style_name)->getDerivativeExtension($extension); + $pipeline = \Drupal::service('image.processor')->createInstance('derivative') + ->setImageStyle(ImageStyle::load($image_style_name)) + ->setSourceImageFileExtension($extension); + $fake_path = 'responsive_image.' . $pipeline->getDerivativeImageFileExtension(); } return Drupal::service('file.mime_type.guesser.extension')->guess($fake_path); } @@ -491,7 +500,11 @@ function _responsive_image_image_style_url($style_name, $path) { } $entity = ImageStyle::load($style_name); if ($entity instanceof ImageStyle) { - return file_url_transform_relative($entity->buildUrl($path)); + $pipeline = \Drupal::service('image.processor')->createInstance('derivative'); + $pipeline + ->setImageStyle($entity) + ->setSourceImageUri($path); + return file_url_transform_relative($pipeline->getDerivativeImageUrl()->toString()); } return file_url_transform_relative(file_create_url($path)); } diff --git a/core/modules/responsive_image/tests/src/Functional/ResponsiveImageFieldDisplayTest.php b/core/modules/responsive_image/tests/src/Functional/ResponsiveImageFieldDisplayTest.php index 9b805d6e..7ea88926 100644 --- a/core/modules/responsive_image/tests/src/Functional/ResponsiveImageFieldDisplayTest.php +++ b/core/modules/responsive_image/tests/src/Functional/ResponsiveImageFieldDisplayTest.php @@ -280,8 +280,10 @@ protected function doTestResponsiveImageFieldFormatters($scheme, $empty_styles = ->save(); // Create a derivative so at least one MIME type will be known. - $large_style = ImageStyle::load('large'); - $large_style->createDerivative($image_uri, $large_style->buildUri($image_uri)); + $large_style_pipeline = \Drupal::service('image.processor')->createInstance('derivative') + ->setImageStyle(ImageStyle::load('large')) + ->setSourceImageUri($image_uri); + $large_style_pipeline->buildDerivativeImage(); // Output should contain all image styles and all breakpoints. $this->drupalGet('node/' . $nid); @@ -292,9 +294,11 @@ protected function doTestResponsiveImageFieldFormatters($scheme, $empty_styles = $this->assertRaw(''); // Assert the empty image is present. $this->assertRaw('data:image/gif;base64,R0lGODlhAQABAIABAP///wAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw=='); - $thumbnail_style = ImageStyle::load('thumbnail'); + $thumbnail_style_pipeline = \Drupal::service('image.processor')->createInstance('derivative') + ->setImageStyle(ImageStyle::load('thumbnail')) + ->setSourceImageUri($image_uri); // Assert the output of the 'srcset' attribute (small multipliers first). - $this->assertRaw('data:image/gif;base64,R0lGODlhAQABAIABAP///wAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw== 1x, ' . file_url_transform_relative($thumbnail_style->buildUrl($image_uri)) . ' 1.5x'); + $this->assertRaw('data:image/gif;base64,R0lGODlhAQABAIABAP///wAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw== 1x, ' . file_url_transform_relative($thumbnail_style_pipeline->getDerivativeImageUrl()->toString()) . ' 1.5x'); $this->assertRaw('/styles/medium/'); // Assert the output of the original image. $this->assertRaw(file_url_transform_relative(file_create_url($image_uri)) . ' 3x'); @@ -305,8 +309,10 @@ protected function doTestResponsiveImageFieldFormatters($scheme, $empty_styles = $this->assertRaw('sizes="(min-width: 700px) 700px, 100vw"'); $this->assertPattern('/media="\(min-width: 560px\)".+?sizes="\(min-width: 700px\) 700px, 100vw"/'); // Assert the output of the 'srcset' attribute (small images first). - $medium_style = ImageStyle::load('medium'); - $this->assertRaw(file_url_transform_relative($medium_style->buildUrl($image_uri)) . ' 220w, ' . file_url_transform_relative($large_style->buildUrl($image_uri)) . ' 360w'); + $medium_style_pipeline = \Drupal::service('image.processor')->createInstance('derivative') + ->setImageStyle(ImageStyle::load('medium')) + ->setSourceImageUri($image_uri); + $this->assertRaw(file_url_transform_relative($medium_style_pipeline->getDerivativeImageUrl()->toString()) . ' 220w, ' . file_url_transform_relative($large_style_pipeline->getDerivativeImageUrl()->toString()) . ' 360w'); $this->assertRaw('media="(min-width: 851px)"'); } $this->assertRaw('/styles/large/'); @@ -321,10 +327,13 @@ protected function doTestResponsiveImageFieldFormatters($scheme, $empty_styles = // Test the fallback image style. $image = \Drupal::service('image.factory')->get($image_uri); + $large_style_pipeline = \Drupal::service('image.processor')->createInstance('derivative') + ->setImageStyle(ImageStyle::load('large')) + ->setSourceImageUri($image->getSource()); $fallback_image = [ '#theme' => 'image', '#alt' => $alt, - '#uri' => file_url_transform_relative($large_style->buildUrl($image->getSource())), + '#uri' => file_url_transform_relative($large_style_pipeline->getDerivativeImageUrl()->toString()), ]; // The image.html.twig template has a newline after the tag but // responsive-image.html.twig doesn't have one after the fallback image, so @@ -335,7 +344,10 @@ protected function doTestResponsiveImageFieldFormatters($scheme, $empty_styles = if ($scheme == 'private') { // Log out and try to access the file. $this->drupalLogout(); - $this->drupalGet($large_style->buildUrl($image_uri)); + $large_style_pipeline = \Drupal::service('image.processor')->createInstance('derivative') + ->setImageStyle(ImageStyle::load('large')) + ->setSourceImageUri($image_uri); + $this->drupalGet($large_style_pipeline->getDerivativeImageUrl()->toString()); $this->assertResponse('403', 'Access denied to image style large as anonymous user.'); $cache_tags_header = $this->drupalGetHeader('X-Drupal-Cache-Tags'); $this->assertTrue(!preg_match('/ image_style\:/', $cache_tags_header), 'No image style cache tag found.'); @@ -402,10 +414,12 @@ public function testResponsiveImageFieldFormattersEmptyMediaQuery() { $this->assertSession()->responseNotMatches('@srcset="data:image/gif;base64,R0lGODlhAQABAIABAP///wAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw== 1x".+?media=".+?/>load($nid); $image_uri = File::load($node->{$field_name}->target_id)->getFileUri(); - $this->assertPattern('/srcset="' . preg_quote(file_url_transform_relative($thumbnail_style->buildUrl($image_uri)), '/') . ' 1x".+?media="\(min-width: 0px\)"/'); + $thumbnail_style_pipeline = \Drupal::service('image.processor')->createInstance('derivative') + ->setImageStyle(ImageStyle::load('thumbnail')) + ->setSourceImageUri($image_uri); + $this->assertPattern('/srcset="' . preg_quote(file_url_transform_relative($thumbnail_style_pipeline->getDerivativeImageUrl()->toString()), '/') . ' 1x".+?media="\(min-width: 0px\)"/'); } /** @@ -448,11 +462,15 @@ public function testResponsiveImageFieldFormattersOneSource() { $this->drupalGet('node/' . $nid); // Assert the media attribute is present if it has a value. - $large_style = ImageStyle::load('large'); - $medium_style = ImageStyle::load('medium'); $node = $node_storage->load($nid); $image_uri = File::load($node->{$field_name}->target_id)->getFileUri(); - $this->assertRaw('createInstance('derivative') + ->setImageStyle(ImageStyle::load('large')) + ->setSourceImageUri($image_uri); + $medium_style_pipeline = \Drupal::service('image.processor')->createInstance('derivative') + ->setImageStyle(ImageStyle::load('medium')) + ->setSourceImageUri($image_uri); + $this->assertRaw('save(); // Create a derivative so at least one MIME type will be known. - $large_style = ImageStyle::load('large'); $image_uri = File::load($node->{$field_name}->target_id)->getFileUri(); - $large_style->createDerivative($image_uri, $large_style->buildUri($image_uri)); + \Drupal::service('image.processor')->createInstance('derivative') + ->setImageStyle(ImageStyle::load('large')) + ->setSourceImageUri($image_uri) + ->buildDerivativeImage(); // Output should contain all image styles and all breakpoints. $this->drupalGet('node/' . $nid); diff --git a/core/modules/user/tests/src/Functional/UserPictureTest.php b/core/modules/user/tests/src/Functional/UserPictureTest.php index 9aa5e45a..6936e0a9 100644 --- a/core/modules/user/tests/src/Functional/UserPictureTest.php +++ b/core/modules/user/tests/src/Functional/UserPictureTest.php @@ -113,8 +113,10 @@ public function testPictureOnNodeComment() { $this->config('system.theme.global')->set('features.node_user_picture', TRUE)->save(); $image_style_id = $this->config('core.entity_view_display.user.user.compact')->get('content.user_picture.settings.image_style'); - $style = ImageStyle::load($image_style_id); - $image_url = file_url_transform_relative($style->buildUrl($file->getfileUri())); + $pipeline = \Drupal::service('image.processor')->createInstance('derivative') + ->setImageStyle(ImageStyle::load($image_style_id)) + ->setSourceImageUri($file->getfileUri()); + $image_url = file_url_transform_relative($pipeline->getDerivativeImageUrl()->toString()); $alt_text = 'Profile picture for user ' . $this->webUser->getAccountName(); // Verify that the image is displayed on the node page. diff --git a/core/tests/Drupal/Tests/Listeners/DeprecationListenerTrait.php b/core/tests/Drupal/Tests/Listeners/DeprecationListenerTrait.php index ce6ed7a6..e4ec6c96 100644 --- a/core/tests/Drupal/Tests/Listeners/DeprecationListenerTrait.php +++ b/core/tests/Drupal/Tests/Listeners/DeprecationListenerTrait.php @@ -140,13 +140,6 @@ public static function isDeprecationSkipped($message) { */ public static function getSkippedDeprecations() { return [ - 'The Drupal\image\Entity\ImageStyle::buildUri method is deprecated since version 9.x.x and will be removed in y.y.y.', - 'The Drupal\image\Entity\ImageStyle::buildUrl method is deprecated since version 9.x.x and will be removed in y.y.y.', - 'The Drupal\image\Entity\ImageStyle::getPathToken method is deprecated since version 9.x.x and will be removed in y.y.y.', - 'The Drupal\image\Entity\ImageStyle::createDerivative method is deprecated since version 9.x.x and will be removed in y.y.y.', - 'The Drupal\image\Entity\ImageStyle::transformDimensions method is deprecated since version 9.x.x and will be removed in y.y.y.', - 'The Drupal\image\Entity\ImageStyle::getDerivativeExtension method is deprecated since version 9.x.x and will be removed in y.y.y.', - 'The Drupal\image\Entity\ImageStyle::supportsUri method is deprecated since version 9.x.x and will be removed in y.y.y.', 'The Symfony\Component\ClassLoader\ApcClassLoader class is deprecated since Symfony 3.3 and will be removed in 4.0. Use `composer install --apcu-autoloader` instead.', // The following deprecation is not triggered by DrupalCI testing since it // is a Windows only deprecation. Remove when core no longer uses