Problem/Motivation
At the moment it is possible to set/getThirdPartySettings on the image style, but the effects that are part of that style do not have any visibility on them. There are use cases where a setting at the ImageStyle level should be accessed by its ImageEffects. Also, there are cases where one effect in the sequence needs to hand over some setting to an effect coming later in the sequence.
Examples:
- In a
ImageStyle::transformDimensions()call, an effect determines dimensions based on Exif rotation information, reading the info from the file via the $uri parameter. That effect should 'flag' that autorotation has been performed already, because since the Exif metadata is not updated back to the source file, if another instance of the same effect is present in the effect collection, it will change again the dimensions. So that 'flag' would prevent the second effect instance to execute. - #204497: Make the background fill color configurable where image styles result in blank space, and in particular comment #204497-82: Make the background fill color configurable where image styles result in blank space
- Textimage sets a TPS in the ImageStyle, being the 'background color' of the image. That is used by 'Textimage text' effects part of the style to deal with cases where the image needs to extended in size, so that the color used to fill in the extended part of the image be consistent across all the effects. Also see #2098247: Provide a hook for ImageStyle::createDerivative.
Proposed resolution
Introduce an ImageEffectWithRuntimeVariablesInterface and trait so that effects that need to access the parent ImageStyle can do so, and can also set arbitary 'runtime variables' that can be accessed by further effects, and whose scope is limited within the execution of a ImageStyle::createDerivative, ::transformDimensions, or ::getDerivativeExtension call. Change the ImageEffectPluginCollection so to inject the runtime variables and the image style id to relevant effect plugins on initialization.
Remaining tasks
User interface changes
None.
API changes
Introduce a new ImageEffectWithRuntimeVariablesInterface with relevant methods.
Beta phase evaluation
| Issue category | Feature request because provides visibility of ImageStyle to its ImageEffects so that effects can access style's third party settings. |
|---|---|
| Issue priority | Normal because this is only to allow contrib to leverage the image style third party settings interface. |
| Prioritized changes | The main goal of this issue is expanding capabilities of the ImageStyle/ImageEffects system, allowing contrib to leverage the image style third party settings interface. |
| Disruption | Not disruptive, will just allow contrib to leverage the image style third party settings interface, no changes to core effects. |
Original report by @zuuperman
There are some use cases for effects, that require the style information to be given as argument to the effect callbacks. This is mostly the case when the effect settings can change per file.
For example: http://drupal.org/node/1250506#comment-4889248
The same goes for dimension callbacks, but i will update this in #1364670: ImageStyle::transformDimensions unable to deal with all effects.
| Comment | File | Size | Author |
|---|---|---|---|
| #70 | 1826362-nr-bot.txt | 132 bytes | needs-review-queue-bot |
| #58 | 1826362-58.patch | 16.07 KB | jofitz |
| #58 | interdiff-57-58.txt | 2.83 KB | jofitz |
| #57 | 1826362-57.patch | 16.44 KB | mondrake |
Comments
Comment #1
nils.destoop commentedand a patch
Comment #3
nils.destoop commentedTests had to be changed for the new argument.
Comment #5
nils.destoop commentedComment #6
nils.destoop commented#3: 1826362-3-info-for-dimensions.patch queued for re-testing.
Comment #7
claudiu.cristeaWe can inject the image style object directly in
ImageEffectInterface::applyEffect()as second argument and then pass the style inImageStyle::createDerivative():@zuuperman, can you provide a patch?
Comment #8
claudiu.cristeaComment #9
mondrakeNew patch, along the lines of #7. Had to be built from scratch since a lot changed since #3...
Comment #10
mondrakeComment #11
mondrakeCompletely new patch. Instead of passing ImageStyle to the effect methods, here I am proposing to inject the ImageStyle to the ImageEffect plugin when it gets instantiated, in a protected property. It was noted in #2098247-11: Provide a hook for ImageStyle::createDerivative that image effects may be executed independently from it being part of a style, and this approach would allow for that. Also, after the introduction of ThirdPartySettingsTrait in ImageStyle, this approach would allow to get such settings accessible from the effects.
Comment #12
fietserwinThis is better, because passing it to the apply method, also means passing it to the dimensions and extension callback. Moreover, it is quite normal for a child to know its parent.
given that
should we define NULL as default for the ImageStyleInterface parameter?
Comment #13
tim.plunkettThis IS is not clear why image effects need this, or how this is a regression from D7.
$this should be $this->id().
Please see BlockPluginCollection and SearchPluginCollection. They do not contain an entire entity, just the entity ID. Additionally, only the search one sets the ID on the plugin, the block implementation only uses it internally.
This belongs on ConfigurableImageEffectBase, not here. And ensure the methods are added to ConfigurableImageEffectInterface.
Finally, see SearchPluginCollection::initializePlugin(), it checks an additional interface that has setSearchPageId(), so that *all* search plugins aren't tied to search pages. This approach would help with "that image effects may be executed independently from it being part of a style" from above. And, it will be run on init, not on get.
Comment #14
mondrakeThanks @tim.plunkett
my intent at this stage is just to allow ImageEffects to access ThirdPartySettings set at the ImageStyle level, during the creation of image derivatives. It is not a regression. This issue is currently categorised as 'Feature request', so it should be postponed, but I would suggest it to be changed to 'task' and try to push it in 8.0, as it would look to me just as a natural completion of the introduction of ThirdPartySettings in the image style entity.
At the moment it is possible to set/getThirdPartySetting on the image style, but they are not visible to the effects.
For this reasons,
re #13.1 -
$thisis there because effects should be able to callset/getThirdPartySettingson their actual instance of the image style entity.re #13.2 -
set/getImageStylehere is relevant for any effect - not just for the configurable ones. I added the methods toImageEffectInterface.re #13.3 - moved the setter to
initializePluginApologies no interdiff :(
If we agree on above, I will update issue summary and change from 'feature request' to 'task'.
Comment #15
tim.plunkettHere's what I had in mind. Unlike the previous patch, it does not needlessly couple all image effects to their image styles.
Comment #16
mondrake#15 thank you, looks good.
The only question I still have is about passing
$this->id()vs$thisto the ImageEffectPluginCollection.My use case (exemplified) is the following: I have a style with 3 effects. The first effect will call, at runtime, setThirdPartySetting() on the ImageStyle to set a color to be used throughout the style as the background of the derivative image. The other two effects will call getThirdPartySetting() on the ImageStyle to fetch the color set up by the first effect.
$this->id()like in #15 means that each effect will load its own instance of the ImageStyle entity from storage. So, to achieve my use case I would need to save the ImageStyle entity to storage on the first effect after calling setThirdPartySetting (or alternatively, implement a hook_image_style_presave() to fetch the color from the effect and make it persistent on the ImageStyle).$this, conversely, will make the 'parameter passing' between effects simpler because effects will just set/getThirdPartySettings on the same ImageStyle instance. Still it would be possible to save ImageStyle back to storage at any point if necessary.I think both ways can work, was just sharing for thought.
Comment #17
mondrakeComment #18
tim.plunkettIf an effect modifies the style, the whole system is broken.
Comment #19
fietserwinI am not sure which way I prefer. I think it all comes down to whether or not an ImageEffect should only exist within the context of an ImageStyle. If so, there's no need for an additional interface and trait and #14 does the job well. If not, #15 might be better from an engineering point of view, but both #14 (perhaps added with some checking to make it more robust) and #15 do the job (would this limit ImageStyleAware effects to ImageStyles or is the same checking on imageStyle(Id) being set still needed/)
Besides this question, #16 poses a real problem with the patch of #15: does "ImageStyle::load();" return the same instance and does it always return an instance, even if a style does not come from storage but is created in code? I would not call the whole system broken when an effect accesses/alters the thirdPartySettings of its parent. This is where these settings were introduced for.
Thus, if we can agree on the answer to the question if "ImageEffects should only exist within the context of an ImageStyles" we can choose a patch:
yes: #14: RTBC
no: #15: NW (loading via the id does not seem correct, pass the instance).
Unlike my previous ideas, the noted comment, I now think an image effect should be tied to an image style. Outside the context of an image style, do not use image effects but use the operations directly on the image (as the image file upload resize already does).
What do others think?
Comment #20
tim.plunkettOur plugins should work outside of the code that currently uses them. Image effects are a nice wrapper around the actual operations they represent, especially the ones that are configurable. There could be any number of usages outside of image styles for these.
We've gone to great lengths to not couple our plugins to their current usages, let's please continue that.
Comment #21
fietserwinOK, so we choose to be able to use image effects in other contexts as well. In that case we continue with the patch from #15, but it is NW to address the concern mentioned in #16. @mondrake: if you change it, I will review and RTBC.
BTW: Why are these ThirdPartySettings on the ImageStyle and not on the Image that is passed around anyway? Or do we also need to know which ImageStyle is currently executing (or some other characteristics from ImageStyle) ? Because I am thinking about issue #2168511: Allow to pass save options to ImageInterface::save where we will need access to the image to pass info from ImageEffect to ImageToolkit, so thee we could use ThirdPartySettings as well, unless we say that we want to have separate save options, as that is not third party but toolkit settings.
Comment #22
tim.plunkett\Drupal\Core\Config\Entity\ThirdPartySettingsInterface is specifically for config entities.
Comment #23
mondrakeThanks @tim.plunkett for #22, I was thinking on the same line.
I agree on comment in #18, effects should not save the style back to storage at derivative creation runtime, because other requests would get an instance specific setup from config storage which is not a good idea. Did not think enough when writing #16.
#2168511: Allow to pass save options to ImageInterface::save has quite an old patch there, things changed in the meantime as we have now an ImageToolkitOperation that can manage image format conversion - the only open point there IMO is about allowing to change JPEG image quality from the config setting. But let's continue there.
RTBC from my point of view, but I cannot because of the earlier patches. @fietserwin?
Comment #24
mondrakeAdded Beta Phase Evaluation in the issue summary.
Comment #25
mondrakeComment #26
fietserwinI found some minor, documentation related, points:
Is now a \Drupal\image\ImageStyleInterface (no longer a string), so type hinting can/should also be used here and in the trait.
... for an image-style-aware ...
Implements is not a formalized phpdoc tag. Thus no parameter list, no return type and no pop-up documentation in my IDE nor in generated documentation. I assume that {@inheritdoc} is also a bit strange in a trait, but does another tag exists that can be used here? Perhaps {@inheritdoc} plus the textual comment (after all,@inheritdoc is an inline tag and phpdoc allows text around it (I think it is just the Drupal standards that don't).
#22:thanks: thus no ThirdPartySettings on Image, in that issue we will stay with what it is now (plus reroll).
Comment #27
mondrake1. Done
2. Done
3. I checked ThirdPartySettingsInterface and ThirdPartySettingsTrait and there the docs are repeated in both interface and trait. Did the same here.
We probably need a change record, I'll draft one.
Comment #28
mondrakeComment #29
mondrakeCreated draft CR https://www.drupal.org/node/2386353
Comment #30
fietserwinThanks, also @tim.plunkett for guarding the Drupal architectural parts that I (we) do not know in detail. The CR also looks good, though you might emphasize that this feature is normally not needed as image effects have their own settings/configuration storage. Only in specific cases where the settings are shared with other effects/components this comes in handy.
Comment #31
mondrake@fietserwin thanks, CR updated with your comment.
Comment #32
alexpottWe need to improve the beta evaluation to state exactly how
is allowed at this point.
Comment #33
mondrakeComment #34
mondrakeRerolled.
Comment #35
mondrakeChanged to feature request, and updated IS.
Comment #36
mondrakeComment #37
mondrake#27 was RTBC, and #34 is a plain reroll. Setting back to RTBC, the IS was updated according #32.
Comment #38
tim.plunkettThis goes against the entire premise of plugins being decoupled from their parent.
Please won't fix, or go back to the implementation in #15.
See also #20.
Comment #39
mondrake@tim.plunkett the patch in #34 is practically same as yours in #22, just minor changes in #26 and reroll. Can you confirm you'd rather
?
Thanks.
Comment #40
tim.plunkettYeah I tried to remember why I wrote #22. I cannot for the life of me figure that out. But #15/#20 match my current thoughts as well.
Comment #41
mondrakeOK then, I am setting this back to NW.
For me #15 is not much help, since each effect would open separately an ImageStyle instance from storage.
What I miss is what @claudiu.cristea commented in #2098247-2: Provide a hook for ImageStyle::createDerivative (emphasis mine):
Any help in this sense appreciated.
Comment #42
mondrakeLet's change the issue title too.
Comment #43
mondrakeThis patch builds on patch in #15, adding a pooled storage of 'runtime variables' (a MemoryStorage object) on the collection that is passed to relevant image effects. Image effects can then get/set values from that pool.
The image style id here thus becomes 'one' of the runtime variables that can be passed on to effects, with the difference that this is always initialised within the collection.
The actual 'parent' image style can be retrieved by effects via the helper method
ImageStyleAwareTrait::getImageStyle(), in a way similar to #15.I think that this addresses the concern in #38 to keep ImageEffect plugins decoupled from their parent image style - the
ImageStyleAwareTrait::hasRuntimeVariables()method should be used by implementing plugins to ensure that runtime variables (inititialised in the collection, but non-existing if the plugin is initiatied in isolation from it) exist before trying to access them.Also,
ImageStyleAware(Interface|Trait)seems no longer a good name. I would suggestImageEffectWithRuntimeVariables(Interface|Trait).Thanks for any review.
Comment #44
mondrakeActually I would also need an option to prevent runtime variables to be reset before ::transformDimensions, ::getDerivativeExtension and ::createDerivative, so that parent code could do it instead (and pass variables to the first effect in the collection if that is needed).
Comment #45
mondrakeDoing #44, with tests.
Comment #46
mondrakelearnt in #2630242-6: Provide methods to retrieve EXIF image information via the Image object, we cannot add/change methods in existing interfaces, so this has to be done differently
Comment #47
mondrakeMoved method skipRuntimeEnvironmentReset from ImageStyleInterface to ImageEffectPluginCollectionInterface, so not to have BC-breaking changes to ImageStyleInterface.
Also, per #43, renamed
ImageStyleAware(Interface|Trait)toImageEffectWithRuntimeVariables(Interface|Trait).Comment #48
mondrakeComment #49
mondrakeComment #50
mondrakeRe-rollled
Comment #53
mondrakeThis is blocking Image Effect's #2748307: Aspect Switcher.
There we are trying to add an effect that will select another image style for execution, depending on the image orientation. But without the effect knowing the 'parent' image style, it's not possible to
ensureprevent that the 'parent' style is selected as a 'substyle', which causes infinite loops, inconsistency in the config objects, and ultimately WSOD.EDIT - wrong verb, strikethrough applied
Comment #56
jhedstromPatch needs a reroll. Also, the IS needs updating since the beta evaluation phase is long over.
Comment #57
mondrakeRe-roll.
Comment #58
jofitzCorrect coding standards errors.
Comment #61
mondrakeRelated, #2986669: Split ImageStyle into the config entity and a separate event-based image processing service
Comment #70
needs-review-queue-bot commentedThe Needs Review Queue Bot tested this issue. It either no longer applies to Drupal core, or fails the Drupal core commit checks. Therefore, this issue status is now "Needs work".
Apart from a re-roll or rebase, this issue may need more work to address feedback in the issue or MR comments. To progress an issue, incorporate this feedback as part of the process of updating the issue. This helps other contributors to know what is outstanding.
Consult the Drupal Contributor Guide to find step-by-step guides for working with issues.