Image captions and the PortalTransforms cache
The reason has to do with the way in which the captions are added, via the html-to-captioned transform (which should be applied to anything with the text/x-html-safe output mimetype, thanks to a special transform policy that is configured for that mimetype). It turns out that PortalTransforms caches the result of a transform for an hour. It stores this result in a volatile (that means non-persistent) attribute of the value that is being transformed, so in normal cases when you are editing and replacing that value entirely, we don't have to worry about cache invalidation--that is, if I save new body text for a page, the cached transform value will be wiped out and we'll see the new text the next time we view the page. However, in the case of editing the caption of an image, I'm saving a completely different value, on a different object, than the one where the transform result is cached. So even if I reload the page I'll see the old caption.
Workaround 1: Change cache lifetime settingOption 1: Go to /portal_transforms/manage_cacheForm in the ZMI and lower the cache lifetime. But remember that this will have a negative effect on performance (particularly for pages that are viewed frequently but not served out of a reverse proxy), as the transform will have to be applied more often.
Workaround 2: Manually invalidate the cache when the image is edited
Option 2: When the image caption is edited, invalidate the transform cache on the field referencing the image. I took a first stab at doing this for a project currently underway. For this project I have a custom subclass of ATImage and images are always contained within a (folderish) article, so I modified the caption mutator as follows:
def setCaption(self, value, **kw): self.getField('caption').set(self, value, **kw) # The result of transforms is cached for up to one hour. # Our caption is inserted into article text via a transform, # so if we're located within an article, invalidate the # (volatile) cache of the article's text. parent = aq_parent(aq_inner(self)) if IArticle.providedBy(parent): value = parent.getField('text').getBaseUnit(parent) if hasattr(value, '_v_transform_cache'): delattr(value, '_v_transform_cache')
(Note: I first investigated using the Cache class from Products.PortalTransforms.cache so that I could simply do Cache(value).purgeCache() ...however, in the current PortalTransforms release the purgeCache method uses a method which was not imported, so I just recreated what that method does. I checked in a fix to the PortalTransforms bug which should make it into the next release of that package.)
For a more generic implementation of this cache invalidation, you would want to do the cache invalidation in a handler for the IObjectEdited event on ATImages, and use whatever the linkintegrity code does to determine what items contain references to the image which was edited--and are therefore candidates for cache invalidation. (The cache invalidation is only needed if the referencing field has already been rendered and is still in memory in the ZODB cache, so of course it would be good to make sure that the cache invalidation doesn't cause extra objects to be woken up.)
I don't have the need, time, or interest to work on this more generic solution myself, but it would be cool if someone tackled it. :) Perhaps some of you also have ideas for other options for how to deal with this...