diff --git a/core/modules/image/src/Entity/ImageStyle.php b/core/modules/image/src/Entity/ImageStyle.php index f3d0207..c9e50ed 100644 --- a/core/modules/image/src/Entity/ImageStyle.php +++ b/core/modules/image/src/Entity/ImageStyle.php @@ -80,6 +80,20 @@ class ImageStyle extends ConfigEntityBase implements ImageStyleInterface, Entity protected $label; /** + * Indicates whether runtime variables should be reset. + * + * When TRUE, the runtime variables will be reset at next call of + * ::createDerivative, ::transformDimensions, ::getDerivativeExtension. + * + * @see ::createDerivative + * @see ::transformDimensions + * @see ::getDerivativeExtension + * + * @var bool + */ + protected $resetRuntimeEnvironment = TRUE; + + /** * The array of image effects for this image style. * * @var array @@ -292,9 +306,13 @@ public function createDerivative($original_uri, $derivative_uri) { return FALSE; } + if ($this->resetRuntimeEnvironment) { + $this->getEffects()->resetRuntimeVariables(); + } foreach ($this->getEffects() as $effect) { $effect->applyEffect($image); } + $this->resetRuntimeEnvironment = TRUE; if (!$image->save($derivative_uri)) { if (file_exists($derivative_uri)) { @@ -310,24 +328,40 @@ public function createDerivative($original_uri, $derivative_uri) { * {@inheritdoc} */ public function transformDimensions(array &$dimensions, $uri) { + if ($this->resetRuntimeEnvironment) { + $this->getEffects()->resetRuntimeVariables(); + } foreach ($this->getEffects() as $effect) { $effect->transformDimensions($dimensions, $uri); } + $this->resetRuntimeEnvironment = TRUE; } /** * {@inheritdoc} */ public function getDerivativeExtension($extension) { + if ($this->resetRuntimeEnvironment) { + $this->getEffects()->resetRuntimeVariables(); + } foreach ($this->getEffects() as $effect) { $extension = $effect->getDerivativeExtension($extension); } + $this->resetRuntimeEnvironment = TRUE; return $extension; } /** * {@inheritdoc} */ + public function skipRuntimeEnvironmentReset($skip = TRUE) { + $this->resetRuntimeEnvironment = !$skip; + return $this; + } + + /** + * {@inheritdoc} + */ public function getPathToken($uri) { // Return the first 8 characters. return substr(Crypt::hmacBase64($this->id() . ':' . $this->addExtension($uri), $this->getPrivateKey() . $this->getHashSalt()), 0, 8); @@ -354,7 +388,7 @@ public function getEffect($effect) { */ public function getEffects() { if (!$this->effectsCollection) { - $this->effectsCollection = new ImageEffectPluginCollection($this->getImageEffectPluginManager(), $this->effects); + $this->effectsCollection = new ImageEffectPluginCollection($this->getImageEffectPluginManager(), $this->effects, $this->id()); $this->effectsCollection->sort(); } return $this->effectsCollection; diff --git a/core/modules/image/src/ImageEffectPluginCollection.php b/core/modules/image/src/ImageEffectPluginCollection.php index ef17191..ba4af43 100644 --- a/core/modules/image/src/ImageEffectPluginCollection.php +++ b/core/modules/image/src/ImageEffectPluginCollection.php @@ -7,12 +7,45 @@ namespace Drupal\image; +use Drupal\Core\KeyValueStore\MemoryStorage; use Drupal\Core\Plugin\DefaultLazyPluginCollection; /** * A collection of image effects. */ -class ImageEffectPluginCollection extends DefaultLazyPluginCollection { +class ImageEffectPluginCollection extends DefaultLazyPluginCollection implements ImageEffectPluginCollectionInterface { + + /** + * The unique ID of the image style using these effects. + * + * @var string + */ + protected $imageStyleId; + + /** + * A pool of runtime variables for the image effects. + * + * @var \Drupal\Core\KeyValueStore\MemoryStorage + */ + protected $runtimeVariables; + + /** + * Constructs a new ImageEffectPluginCollection object. + * + * @param \Drupal\image\ImageEffectManager $manager + * The image effect manager. + * @param array $configurations + * An associative array containing the initial configuration for each plugin + * in the collection, keyed by plugin instance ID. + * @param string $image_style_id + * The unique ID of the image style using these effects. + */ + public function __construct(ImageEffectManager $manager, array $configurations, $image_style_id) { + parent::__construct($manager, $configurations); + $this->imageStyleId = $image_style_id; + $this->runtimeVariables = new MemoryStorage('runtime_variables'); + $this->resetRuntimeVariables(); + } /** * {@inheritdoc} @@ -26,6 +59,18 @@ public function &get($instance_id) { /** * {@inheritdoc} */ + protected function initializePlugin($instance_id) { + parent::initializePlugin($instance_id); + + $plugin_instance = $this->pluginInstances[$instance_id]; + if ($plugin_instance instanceof ImageStyleAwareInterface) { + $plugin_instance->setRuntimeVariables($this->runtimeVariables); + } + } + + /** + * {@inheritdoc} + */ public function sortHelper($aID, $bID) { $a_weight = $this->get($aID)->getWeight(); $b_weight = $this->get($bID)->getWeight(); @@ -36,4 +81,20 @@ public function sortHelper($aID, $bID) { return ($a_weight < $b_weight) ? -1 : 1; } + /** + * {@inheritdoc} + */ + public function getRuntimeVariables() { + return $this->runtimeVariables; + } + + /** + * {@inheritdoc} + */ + public function resetRuntimeVariables() { + $this->runtimeVariables->deleteAll(); + $this->runtimeVariables->set('image_style_id', $this->imageStyleId); + return $this; + } + } diff --git a/core/modules/image/src/ImageEffectPluginCollectionInterface.php b/core/modules/image/src/ImageEffectPluginCollectionInterface.php new file mode 100644 index 0000000..546ec7e --- /dev/null +++ b/core/modules/image/src/ImageEffectPluginCollectionInterface.php @@ -0,0 +1,37 @@ +runtimeVariables); + } + + /** + * Returns the pool of runtime variables. + * + * @return \Drupal\Core\KeyValueStore\MemoryStorage|null + * The runtime variables, or NULL if not set. + */ + protected function getRuntimeVariables() { + return $this->runtimeVariables; + } + + /** + * Implements \Drupal\image\ImageStyleAwareInterface::setRuntimeVariables(). + */ + public function setRuntimeVariables($runtime_variables) { + $this->runtimeVariables = $runtime_variables; + return $this; + } + + /** + * Gets the image style object this effect belongs to. + * + * @return \Drupal\image\ImageStyleInterface|null + * The image style this effect belongs to, or NULL if runtime variables are + * not available. + */ + protected function getImageStyle() { + if ($this->getRuntimeVariables() && $this->getRuntimeVariables()->has('image_style_id')) { + return ImageStyle::load($this->getRuntimeVariables()->get('image_style_id')); + } + return NULL; + } + +} diff --git a/core/modules/image/src/ImageStyleInterface.php b/core/modules/image/src/ImageStyleInterface.php index 9507291..43d3ae5 100644 --- a/core/modules/image/src/ImageStyleInterface.php +++ b/core/modules/image/src/ImageStyleInterface.php @@ -153,6 +153,29 @@ public function transformDimensions(array &$dimensions, $uri); public function getDerivativeExtension($extension); /** + * Indicates to skip resetting runtime variables. + * + * Allows to skip resetting runtime variables at next call of any of + * ::createDerivative, ::transformDimensions, ::getDerivativeExtension + * methods. The setting is only relevant for next call, further calls of + * these methods will be resetting the variables, unless this method is + * called again. + * + * @param bool $skip + * (optional) When TRUE, the runtime variables will not be reset at next + * call of ::createDerivative, ::transformDimensions, + * ::getDerivativeExtension. When FALSE, runtime variables will be reset. + * Defaults to TRUE. + * + * @return $this + * + * @see ::createDerivative + * @see ::transformDimensions + * @see ::getDerivativeExtension + */ + public function skipRuntimeEnvironmentReset($skip = TRUE); + + /** * Returns a specific image effect. * * @param string $effect diff --git a/core/modules/image/src/Tests/ImageAdminStylesTest.php b/core/modules/image/src/Tests/ImageAdminStylesTest.php index ed3854f..62e9aa5 100644 --- a/core/modules/image/src/Tests/ImageAdminStylesTest.php +++ b/core/modules/image/src/Tests/ImageAdminStylesTest.php @@ -98,6 +98,9 @@ function testStyle() { 'random' => 1, 'bgcolor' => '#FFFF00', ), + 'image_module_test_image_style_aware' => [ + // No options for the test effect. + ], ); // Add style form. @@ -152,6 +155,11 @@ function testStyle() { foreach ($effect_edits[$effect->getPluginId()] as $field => $value) { $this->assertEqual($value, $effect_configuration['data'][$field], SafeMarkup::format('The %field field in the %effect effect has the correct value of %value.', array('%field' => $field, '%effect' => $effect->getPluginId(), '%value' => $value))); } + // Check that ImageStyle third party settings are accessible from within + // the effect. + if ($effect->getPluginId() === 'image_module_test_image_style_aware') { + $this->assertEqual('bar', $effect->label(), 'Image style third party settings are accessible from the effect.'); + } } // Assert that every effect was saved. @@ -260,7 +268,7 @@ function testStyle() { $this->drupalPostForm($style_path, array('new' => 'image_rotate'), t('Add')); $this->drupalPostForm(NULL, $edit, t('Add effect')); $style = entity_load_unchanged('image_style', $style_name); - $this->assertEqual(count($style->getEffects()), 6, 'Rotate effect with transparent background was added.'); + $this->assertEqual(count($style->getEffects()), 7, 'Rotate effect with transparent background was added.'); // Style deletion form. @@ -466,4 +474,45 @@ public function testImageStyleAccess() { $this->assertRaw(t('Select a new effect')); } + /** + * Tests image style runtime variables. + */ + public function testImageStyleRuntimeVariables() { + // Create a test image style, with runtime variables aware image effect. + $style = ImageStyle::create(['name' => 'style_foo', 'label' => $this->randomString()]); + $effect = [ + 'id' => 'image_module_test_image_style_aware', + 'data' => [], + 'weight' => 2, + ]; + $style->addImageEffect($effect); + $effect['weight'] = 4; + $style->addImageEffect($effect); + $style->save(); + + // Style ID should be in the runtime variables. + $this->assertEqual($style->id(), $style->getEffects()->getRuntimeVariables()->get('image_style_id')); + + // Run through style transform dimensions and check runtime variable is + // set at the end. + $dimensions = [ + 'width' => 10, + 'height' => 10, + ]; + $style->transformDimensions($dimensions, NULL); + $this->assertEqual(20, $dimensions['width']); + $this->assertEqual($style->id(), $style->getEffects()->getRuntimeVariables()->get('image_style_id')); + $this->assertEqual(200, $style->getEffects()->getRuntimeVariables()->get('test_variable')); + + // Set skipping runtime environment reset and re-run transform dimensions. + // 'test_variable' should be at 400. + $style->skipRuntimeEnvironmentReset()->transformDimensions($dimensions, NULL); + $this->assertEqual(400, $style->getEffects()->getRuntimeVariables()->get('test_variable')); + + // Run transform dimensions again, the runtime variables should have been + // reset. 'test_variable' should be at 200. + $style->transformDimensions($dimensions, NULL); + $this->assertEqual(200, $style->getEffects()->getRuntimeVariables()->get('test_variable')); + } + } diff --git a/core/modules/image/tests/modules/image_module_test/config/schema/image_module_test.schema.yml b/core/modules/image/tests/modules/image_module_test/config/schema/image_module_test.schema.yml index b0e8ebf..9a8240d 100644 --- a/core/modules/image/tests/modules/image_module_test/config/schema/image_module_test.schema.yml +++ b/core/modules/image/tests/modules/image_module_test/config/schema/image_module_test.schema.yml @@ -5,3 +5,7 @@ image.style.*.third_party.image_module_test: foo: type: string label: 'Label for foo' + +image.effect.image_module_test_image_style_aware: + type: image_size + label: 'Image module test image-style-aware' diff --git a/core/modules/image/tests/modules/image_module_test/src/Plugin/ImageEffect/ImageStyleAwareTestImageEffect.php b/core/modules/image/tests/modules/image_module_test/src/Plugin/ImageEffect/ImageStyleAwareTestImageEffect.php new file mode 100644 index 0000000..3638eed --- /dev/null +++ b/core/modules/image/tests/modules/image_module_test/src/Plugin/ImageEffect/ImageStyleAwareTestImageEffect.php @@ -0,0 +1,63 @@ +setTestVariable(); + } + + /** + * {@inheritdoc} + */ + public function applyEffect(ImageInterface $image) { + $this->setTestVariable(); + return TRUE; + } + + /** + * {@inheritdoc} + */ + public function label() { + return $this->getImageStyle()->getThirdPartySetting('image_module_test', 'foo'); + } + + /** + * Sets the runtime test variable. + */ + protected function setTestVariable() { + if ($this->hasRuntimeVariables()) { + $test_variable = $this->getRuntimeVariables()->get('test_variable', 0); + $test_variable += 100; + $this->getRuntimeVariables()->set('test_variable', $test_variable); + } + } + +}