diff --git a/news/93.feature b/news/93.feature new file mode 100644 index 0000000..fe0847c --- /dev/null +++ b/news/93.feature @@ -0,0 +1,4 @@ +Update the resourceRegistries ETag to use the config registry modification time. +This time is set since Plone 6.0.4. +Fixes `issue 93 `_. +[maurits] diff --git a/plone/app/caching/operations/etags.py b/plone/app/caching/operations/etags.py index 8187172..20f1783 100644 --- a/plone/app/caching/operations/etags.py +++ b/plone/app/caching/operations/etags.py @@ -4,11 +4,10 @@ from plone.app.caching.operations.utils import getContext from plone.app.caching.operations.utils import getLastModifiedAnnotation from plone.base.utils import safe_hasattr +from plone.registry.interfaces import IRegistry from Products.CMFCore.interfaces import ICatalogTool from Products.CMFCore.interfaces import IMembershipTool from Products.CMFCore.utils import getToolByName -from Products.CMFPlone.resources.utils import get_override_directory -from Products.CMFPlone.resources.utils import PRODUCTION_RESOURCE_DIRECTORY from zope.component import adapter from zope.component import queryMultiAdapter from zope.component import queryUtility @@ -19,6 +18,13 @@ import time +try: + # available since Plone 6.0.4 + from Products.CMFPlone.resources.browser.resource import _RESOURCE_REGISTRY_MTIME +except ImportError: + _RESOURCE_REGISTRY_MTIME = None + + @implementer(IETagValue) @adapter(Interface, Interface) class UserID: @@ -239,22 +245,15 @@ def __init__(self, published, request): self.request = request def __call__(self): - context = getContext(self.published) - container = get_override_directory(context) - if PRODUCTION_RESOURCE_DIRECTORY not in container: + if _RESOURCE_REGISTRY_MTIME is None: return "" - production_folder = container[PRODUCTION_RESOURCE_DIRECTORY] - filename = "timestamp.txt" - if filename not in production_folder: + registry = queryUtility(IRegistry) + if registry is None: return "" - timestamp = production_folder.readFile(filename) - if not timestamp: + mtime = getattr(registry, _RESOURCE_REGISTRY_MTIME, None) + if mtime is None: return "" - # timestamp is in bytes, and we must return a string. - # On Python 2 this is the same, but not on Python 3. - if not isinstance(timestamp, str): - timestamp = timestamp.decode("utf-8") - return timestamp + return str(mtime) @implementer(IETagValue) diff --git a/plone/app/caching/tests/test_profile_with_caching_proxy.py b/plone/app/caching/tests/test_profile_with_caching_proxy.py index 8df999a..cdb62c9 100644 --- a/plone/app/caching/tests/test_profile_with_caching_proxy.py +++ b/plone/app/caching/tests/test_profile_with_caching_proxy.py @@ -73,12 +73,12 @@ def test_composite_viewsxx(self): # Can we just call that test from this context? catalog = self.portal["portal_catalog"] - skins_tool = self.portal["portal_skins"] + default_skin = self.portal["portal_skins"].default_skin # Add folder content setRoles(self.portal, TEST_USER_ID, ("Manager",)) self.portal.invokeFactory("Folder", "f1") - self.portal["f1"].title = "one" + self.portal["f1"].title = "Folder one" self.portal["f1"].description = "Folder one description" self.portal["f1"].reindexObject() @@ -105,9 +105,16 @@ def test_composite_viewsxx(self): # - turn on gzip? # - set skin? Maybe # - leave status unlocked - # + # - set the mod date on the resource registries? Probably. transaction.commit() + # Since Plone 6.0.4 we have a modification date on the registry. + from Products.CMFPlone.resources.browser.resource import ( + _RESOURCE_REGISTRY_MTIME, + ) + + mtime = str(getattr(self.registry, _RESOURCE_REGISTRY_MTIME)) + # Request the authenticated folder now = stable_now() browser = Browser(self.app) @@ -124,10 +131,8 @@ def test_composite_viewsxx(self): self.assertEqual( "max-age=0, must-revalidate, private", browser.headers["Cache-Control"] ) - self.assertEqual( - f'"|test_user_1_|{catalog.getCounter()}|en|{skins_tool.default_skin}|0|0|"', - normalize_etag(browser.headers["ETag"]), - ) + tag = f'"|test_user_1_|{catalog.getCounter()}|en|{default_skin}|0|0|{mtime}"' + self.assertEqual(tag, normalize_etag(browser.headers["ETag"])) self.assertGreater(now, dateutil.parser.parse(browser.headers["Expires"])) # Set the copy/cut cookie and then request the folder view again @@ -141,10 +146,8 @@ def test_composite_viewsxx(self): self.assertEqual( "max-age=0, must-revalidate, private", browser.headers["Cache-Control"] ) - self.assertEqual( - f'"|test_user_1_|{catalog.getCounter()}|en|{skins_tool.default_skin}|0|1|"', - normalize_etag(browser.headers["ETag"]), - ) + tag = f'"|test_user_1_|{catalog.getCounter()}|en|{default_skin}|0|1|{mtime}"' + self.assertEqual(tag, normalize_etag(browser.headers["ETag"])) # Request the authenticated page now = stable_now() @@ -163,10 +166,8 @@ def test_composite_viewsxx(self): self.assertEqual( "max-age=0, must-revalidate, private", browser.headers["Cache-Control"] ) - self.assertEqual( - f'"|test_user_1_|{catalog.getCounter()}|en|{skins_tool.default_skin}|0|"', - normalize_etag(browser.headers["ETag"]), - ) + tag = f'"|test_user_1_|{catalog.getCounter()}|en|{default_skin}|0|{mtime}"' + self.assertEqual(tag, normalize_etag(browser.headers["ETag"])) self.assertGreater(now, dateutil.parser.parse(browser.headers["Expires"])) # Request the authenticated page again -- to test RAM cache. @@ -205,7 +206,6 @@ def test_composite_viewsxx(self): # Request the anonymous folder now = stable_now() browser = Browser(self.app) - browser.handleErrors = False browser.open(self.portal["f1"].absolute_url()) self.assertEqual("plone.content.folderView", browser.headers["X-Cache-Rule"]) self.assertEqual( @@ -215,10 +215,8 @@ def test_composite_viewsxx(self): self.assertEqual( "max-age=0, must-revalidate, private", browser.headers["Cache-Control"] ) - self.assertEqual( - f'"||{catalog.getCounter()}|en|{skins_tool.default_skin}|0|0|"', - normalize_etag(browser.headers["ETag"]), - ) + tag = f'"||{catalog.getCounter()}|en|{default_skin}|0|0|{mtime}"' + self.assertEqual(tag, normalize_etag(browser.headers["ETag"])) self.assertGreater(now, dateutil.parser.parse(browser.headers["Expires"])) # Request the anonymous page @@ -234,10 +232,8 @@ def test_composite_viewsxx(self): self.assertEqual( "max-age=0, must-revalidate, private", browser.headers["Cache-Control"] ) - self.assertEqual( - f'"||{catalog.getCounter()}|en|{skins_tool.default_skin}|0|"', - normalize_etag(browser.headers["ETag"]), - ) + tag = f'"||{catalog.getCounter()}|en|{default_skin}|0|{mtime}"' + self.assertEqual(tag, normalize_etag(browser.headers["ETag"])) self.assertGreater(now, dateutil.parser.parse(browser.headers["Expires"])) # Request the anonymous page again -- to test RAM cache. @@ -257,10 +253,8 @@ def test_composite_viewsxx(self): self.assertEqual( "max-age=0, must-revalidate, private", browser.headers["Cache-Control"] ) - self.assertEqual( - f'"||{catalog.getCounter()}|en|{skins_tool.default_skin}|0|"', - normalize_etag(browser.headers["ETag"]), - ) + tag = f'"||{catalog.getCounter()}|en|{default_skin}|0|{mtime}"' + self.assertEqual(tag, normalize_etag(browser.headers["ETag"])) self.assertGreater(now, dateutil.parser.parse(browser.headers["Expires"])) # Request the anonymous page again -- with an INM header to test 304. diff --git a/plone/app/caching/tests/test_profile_without_caching_proxy.py b/plone/app/caching/tests/test_profile_without_caching_proxy.py index 07a0585..3d9076a 100644 --- a/plone/app/caching/tests/test_profile_without_caching_proxy.py +++ b/plone/app/caching/tests/test_profile_without_caching_proxy.py @@ -92,6 +92,13 @@ def test_composite_views(self): # - set the mod date on the resource registries? Probably. transaction.commit() + # Since Plone 6.0.4 we have a modification date on the registry. + from Products.CMFPlone.resources.browser.resource import ( + _RESOURCE_REGISTRY_MTIME, + ) + + mtime = str(getattr(self.registry, _RESOURCE_REGISTRY_MTIME)) + # Request the authenticated folder now = stable_now() browser = Browser(self.app) @@ -108,7 +115,7 @@ def test_composite_views(self): self.assertEqual( "max-age=0, must-revalidate, private", browser.headers["Cache-Control"] ) - tag = f'"|test_user_1_|{catalog.getCounter()}|en|{default_skin}|0|0|"' + tag = f'"|test_user_1_|{catalog.getCounter()}|en|{default_skin}|0|0|{mtime}"' self.assertEqual(tag, normalize_etag(browser.headers["ETag"])) self.assertGreater(now, dateutil.parser.parse(browser.headers["Expires"])) @@ -123,7 +130,7 @@ def test_composite_views(self): self.assertEqual( "max-age=0, must-revalidate, private", browser.headers["Cache-Control"] ) - tag = f'"|test_user_1_|{catalog.getCounter()}|en|{default_skin}|0|1|"' + tag = f'"|test_user_1_|{catalog.getCounter()}|en|{default_skin}|0|1|{mtime}"' self.assertEqual(tag, normalize_etag(browser.headers["ETag"])) # Request the authenticated page @@ -143,7 +150,7 @@ def test_composite_views(self): self.assertEqual( "max-age=0, must-revalidate, private", browser.headers["Cache-Control"] ) - tag = f'"|test_user_1_|{catalog.getCounter()}|en|{default_skin}|0|"' + tag = f'"|test_user_1_|{catalog.getCounter()}|en|{default_skin}|0|{mtime}"' self.assertEqual(tag, normalize_etag(browser.headers["ETag"])) self.assertGreater(now, dateutil.parser.parse(browser.headers["Expires"])) @@ -192,7 +199,7 @@ def test_composite_views(self): self.assertEqual( "max-age=0, must-revalidate, private", browser.headers["Cache-Control"] ) - tag = f'"||{catalog.getCounter()}|en|{default_skin}|0|0|"' + tag = f'"||{catalog.getCounter()}|en|{default_skin}|0|0|{mtime}"' self.assertEqual(tag, normalize_etag(browser.headers["ETag"])) self.assertGreater(now, dateutil.parser.parse(browser.headers["Expires"])) @@ -209,7 +216,7 @@ def test_composite_views(self): self.assertEqual( "max-age=0, must-revalidate, private", browser.headers["Cache-Control"] ) - tag = f'"||{catalog.getCounter()}|en|{default_skin}|0|"' + tag = f'"||{catalog.getCounter()}|en|{default_skin}|0|{mtime}"' self.assertEqual(tag, normalize_etag(browser.headers["ETag"])) self.assertGreater(now, dateutil.parser.parse(browser.headers["Expires"])) @@ -230,7 +237,7 @@ def test_composite_views(self): self.assertEqual( "max-age=0, must-revalidate, private", browser.headers["Cache-Control"] ) - tag = f'"||{catalog.getCounter()}|en|{default_skin}|0|"' + tag = f'"||{catalog.getCounter()}|en|{default_skin}|0|{mtime}"' self.assertEqual(tag, normalize_etag(browser.headers["ETag"])) self.assertGreater(now, dateutil.parser.parse(browser.headers["Expires"]))