diff --git a/core/modules/image/src/Entity/ImageStyle.php b/core/modules/image/src/Entity/ImageStyle.php index 85f4ceb..34a0fa3 100644 --- a/core/modules/image/src/Entity/ImageStyle.php +++ b/core/modules/image/src/Entity/ImageStyle.php @@ -282,9 +282,11 @@ public function createDerivative($original_uri, $derivative_uri) { return FALSE; } + $this->getEffects()->resetRuntimeVariables(); foreach ($this->getEffects() as $effect) { $effect->applyEffect($image); } + $this->getEffects()->skipRuntimeEnvironmentReset(FALSE); if (!$image->save($derivative_uri)) { if (file_exists($derivative_uri)) { @@ -300,18 +302,22 @@ public function createDerivative($original_uri, $derivative_uri) { * {@inheritdoc} */ public function transformDimensions(array &$dimensions, $uri) { + $this->getEffects()->resetRuntimeVariables(); foreach ($this->getEffects() as $effect) { $effect->transformDimensions($dimensions, $uri); } + $this->getEffects()->skipRuntimeEnvironmentReset(FALSE); } /** * {@inheritdoc} */ public function getDerivativeExtension($extension) { + $this->getEffects()->resetRuntimeVariables(); foreach ($this->getEffects() as $effect) { $extension = $effect->getDerivativeExtension($extension); } + $this->getEffects()->skipRuntimeEnvironmentReset(FALSE); return $extension; } @@ -344,7 +350,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..1858f09 100644 --- a/core/modules/image/src/ImageEffectPluginCollection.php +++ b/core/modules/image/src/ImageEffectPluginCollection.php @@ -7,12 +7,55 @@ 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; + + /** + * Indicates whether runtime variables should be reset. + * + * @var bool + * + * @see ::resetRuntimeVariables + * @see ::skipRuntimeEnvironmentReset + */ + protected $resetRuntimeEnvironment = TRUE; + + /** + * 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 +69,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 ImageEffectWithRuntimeVariablesInterface) { + $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 +91,30 @@ public function sortHelper($aID, $bID) { return ($a_weight < $b_weight) ? -1 : 1; } + /** + * {@inheritdoc} + */ + public function getRuntimeVariables() { + return $this->runtimeVariables; + } + + /** + * {@inheritdoc} + */ + public function resetRuntimeVariables() { + if ($this->resetRuntimeEnvironment) { + $this->runtimeVariables->deleteAll(); + $this->runtimeVariables->set('image_style_id', $this->imageStyleId); + } + return $this; + } + + /** + * {@inheritdoc} + */ + public function skipRuntimeEnvironmentReset($skip = TRUE) { + $this->resetRuntimeEnvironment = !$skip; + return $this; + } + } diff --git a/core/modules/image/src/ImageEffectPluginCollectionInterface.php b/core/modules/image/src/ImageEffectPluginCollectionInterface.php new file mode 100644 index 0000000..5a2368d --- /dev/null +++ b/core/modules/image/src/ImageEffectPluginCollectionInterface.php @@ -0,0 +1,57 @@ +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; + } + + /** + * Sets the the pool of runtime variables for the image effects. + * + * @param \Drupal\Core\KeyValueStore\MemoryStorage $runtime_variables; + * The pool of runtime variables for the image effects. + * + * @return $this + */ + public function setRuntimeVariables(MemoryStorage $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->hasRuntimeVariables() && $this->getRuntimeVariables()->has('image_style_id')) { + return ImageStyle::load($this->getRuntimeVariables()->get('image_style_id')); + } + return NULL; + } + +} diff --git a/core/modules/image/src/Tests/ImageAdminStylesTest.php b/core/modules/image/src/Tests/ImageAdminStylesTest.php index cf5916c..d1d60a1 100644 --- a/core/modules/image/src/Tests/ImageAdminStylesTest.php +++ b/core/modules/image/src/Tests/ImageAdminStylesTest.php @@ -99,6 +99,9 @@ function testStyle() { 'random' => 1, 'bgcolor' => '#FFFF00', ), + 'image_module_test_effect_with_runtime_variables' => [ + // No options for the test effect. + ], ); // Add style form. @@ -153,6 +156,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_effect_with_runtime_variables') { + $this->assertEqual('bar', $effect->label()); + } } // Assert that every effect was saved. @@ -263,7 +271,7 @@ function testStyle() { $this->drupalPostForm(NULL, $edit, t('Add effect')); $entity_type_manager = $this->container->get('entity_type.manager'); $style = $entity_type_manager->getStorage('image_style')->loadUnchanged($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. @@ -487,4 +495,46 @@ 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_effect_with_runtime_variables', + '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->getEffects()->skipRuntimeEnvironmentReset(); + $style->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..8d62a7f 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_effect_with_runtime_variables: + type: image_size + label: 'Image module test effect with runtime variables' diff --git a/core/modules/image/tests/modules/image_module_test/src/Plugin/ImageEffect/EffectWithRuntimeVariables.php b/core/modules/image/tests/modules/image_module_test/src/Plugin/ImageEffect/EffectWithRuntimeVariables.php new file mode 100644 index 0000000..ae6a4fb --- /dev/null +++ b/core/modules/image/tests/modules/image_module_test/src/Plugin/ImageEffect/EffectWithRuntimeVariables.php @@ -0,0 +1,67 @@ +setTestVariable(); + } + + /** + * {@inheritdoc} + */ + public function applyEffect(ImageInterface $image) { + $this->setTestVariable(); + return TRUE; + } + + /** + * {@inheritdoc} + */ + public function label() { + if ($this->hasRuntimeVariables()) { + return $this->getImageStyle()->getThirdPartySetting('image_module_test', 'foo'); + } + else { + return 'qux'; + } + } + + /** + * 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); + } + } + +}