diff --git a/docs/caching-proxies.rst b/docs/caching-proxies.rst
index adb3780..f5955fc 100644
--- a/docs/caching-proxies.rst
+++ b/docs/caching-proxies.rst
@@ -22,7 +22,13 @@ returned to a user, because the cache has not been updated since the item
was modified. There are three general strategies for dealing with this:
* Since resources are cached in the proxy based on their URL, you can
- "invalidate" the cached copy by changing an item's URL when it is updated. This approach has the benefit of also being able to
+ "invalidate" the cached copy by changing an item's URL when it is updated.
+ This is the approach taken by Plone's ResourceRegistries:
+ in production mode, the links that are inserted
+ into Plone's content pages for resource managed by ResourceRegistries
+ contain a time-based token, which changes when the ResourceRegistries
+ are updated, more specifically: when the resource bundles are combined.
+ This approach has the benefit of also being able to
"invalidate" content stored in a user's browser cache.
* All caching proxies support setting timeouts. This means that content may
@@ -85,7 +91,8 @@ The default purge paths include:
``Image`` types.
Files and images created (or customised) in the ZMI are purged automatically
-when modified. To purge Plone content when modified
+when modified. Files managed through the ResourceRegistries do not need
+purging, since they have "stable" URLs. To purge Plone content when modified
(or removed), you must select the content types in the control panel. By
default, only the ``File`` and ``Image`` types are purged.
diff --git a/docs/etags.rst b/docs/etags.rst
index 9b842c3..3cdc796 100644
--- a/docs/etags.rst
+++ b/docs/etags.rst
@@ -42,6 +42,11 @@ The ETag names tokens supported by default are:
* skin
The name of the current skin (theme)
+* resourceRegistries
+ A timestamp indicating the last-modified timestamp for the
+ Resource Registries. This is useful for avoiding requests for expired
+ resources from cached pages.
+
It is possible to provide additional tokens by registering an ``IETagValue``
adapter. This should be a named adapter on the published object (typically a
view, file resource or Zope page template object) and request, with a unique
diff --git a/plone/app/caching/profiles/with-caching-proxy-splitviews/registry.xml b/plone/app/caching/profiles/with-caching-proxy-splitviews/registry.xml
index 3d32eb3..22943d7 100644
--- a/plone/app/caching/profiles/with-caching-proxy-splitviews/registry.xml
+++ b/plone/app/caching/profiles/with-caching-proxy-splitviews/registry.xml
@@ -21,6 +21,7 @@
userLanguage
skin
locked
+ resourceRegistries
@@ -48,6 +49,7 @@
skin
locked
copy
+ resourceRegistries
@@ -117,4 +119,4 @@
-
+
\ No newline at end of file
diff --git a/plone/app/caching/profiles/with-caching-proxy/registry.xml b/plone/app/caching/profiles/with-caching-proxy/registry.xml
index 56c2264..0d30b49 100644
--- a/plone/app/caching/profiles/with-caching-proxy/registry.xml
+++ b/plone/app/caching/profiles/with-caching-proxy/registry.xml
@@ -21,6 +21,7 @@
userLanguage
skin
locked
+ resourceRegistries
@@ -39,6 +40,7 @@
skin
locked
copy
+ resourceRegistries
diff --git a/plone/app/caching/profiles/without-caching-proxy/registry.xml b/plone/app/caching/profiles/without-caching-proxy/registry.xml
index 3569909..bdbe9b4 100644
--- a/plone/app/caching/profiles/without-caching-proxy/registry.xml
+++ b/plone/app/caching/profiles/without-caching-proxy/registry.xml
@@ -21,6 +21,7 @@
userLanguage
skin
locked
+ resourceRegistries
@@ -39,6 +40,7 @@
skin
locked
copy
+ resourceRegistries
@@ -92,4 +94,4 @@
-
+
\ No newline at end of file
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 320371d..b3897eb 100644
--- a/plone/app/caching/tests/test_profile_with_caching_proxy.py
+++ b/plone/app/caching/tests/test_profile_with_caching_proxy.py
@@ -147,7 +147,7 @@ def test_composite_viewsxx(self):
# This should use cacheInBrowser
self.assertEqual('max-age=0, must-revalidate, private',
browser.headers['Cache-Control'])
- self.assertEqual('"|test_user_1_|%d|en|%s|0' % (catalog.getCounter(
+ self.assertEqual('"|test_user_1_|%d|en|%s|0|0' % (catalog.getCounter(
), skins_tool.default_skin), _normalize_etag(browser.headers['ETag']))
self.assertGreater(now, dateutil.parser.parse(
browser.headers['Expires']))
@@ -162,7 +162,7 @@ def test_composite_viewsxx(self):
browser.headers['X-Cache-Operation'])
self.assertEqual('max-age=0, must-revalidate, private',
browser.headers['Cache-Control'])
- self.assertEqual('"|test_user_1_|%d|en|%s|0' % (catalog.getCounter(
+ self.assertEqual('"|test_user_1_|%d|en|%s|0|1' % (catalog.getCounter(
), skins_tool.default_skin), _normalize_etag(browser.headers['ETag']))
# Request the authenticated page
@@ -181,7 +181,7 @@ def test_composite_viewsxx(self):
# This should use cacheInBrowser
self.assertEqual('max-age=0, must-revalidate, private',
browser.headers['Cache-Control'])
- self.assertEqual('"|test_user_1_|%d|en|%s' % (catalog.getCounter(
+ self.assertEqual('"|test_user_1_|%d|en|%s|0' % (catalog.getCounter(
), skins_tool.default_skin), _normalize_etag(browser.headers['ETag']))
self.assertGreater(now, dateutil.parser.parse(
browser.headers['Expires']))
@@ -227,7 +227,7 @@ def test_composite_viewsxx(self):
# This should use cacheInBrowser
self.assertEqual('max-age=0, must-revalidate, private',
browser.headers['Cache-Control'])
- self.assertEqual('"||%d|en|%s|0' % (catalog.getCounter(
+ self.assertEqual('"||%d|en|%s|0|0' % (catalog.getCounter(
), skins_tool.default_skin), _normalize_etag(browser.headers['ETag']))
self.assertGreater(now, dateutil.parser.parse(
browser.headers['Expires']))
@@ -244,7 +244,7 @@ def test_composite_viewsxx(self):
# This should use cacheInBrowser
self.assertEqual('max-age=0, must-revalidate, private',
browser.headers['Cache-Control'])
- self.assertEqual('"||%d|en|%s' % (catalog.getCounter(
+ self.assertEqual('"||%d|en|%s|0' % (catalog.getCounter(
), skins_tool.default_skin), _normalize_etag(browser.headers['ETag']))
self.assertGreater(now, dateutil.parser.parse(
browser.headers['Expires']))
@@ -264,7 +264,7 @@ def test_composite_viewsxx(self):
self.assertIn(testText, browser.contents)
self.assertEqual('max-age=0, must-revalidate, private',
browser.headers['Cache-Control'])
- self.assertEqual('"||%d|en|%s' % (catalog.getCounter(
+ self.assertEqual('"||%d|en|%s|0' % (catalog.getCounter(
), skins_tool.default_skin), _normalize_etag(browser.headers['ETag']))
self.assertGreater(now, dateutil.parser.parse(
browser.headers['Expires']))
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 d19db4e..0692758 100644
--- a/plone/app/caching/tests/test_profile_without_caching_proxy.py
+++ b/plone/app/caching/tests/test_profile_without_caching_proxy.py
@@ -135,7 +135,7 @@ def test_composite_views(self):
# This should use cacheInBrowser
self.assertEqual('max-age=0, must-revalidate, private',
browser.headers['Cache-Control'])
- tag = '"|test_user_1_|{0}|en|{1}|0'.format(
+ tag = '"|test_user_1_|{0}|en|{1}|0|0'.format(
catalog.getCounter(),
default_skin,
)
@@ -153,7 +153,7 @@ def test_composite_views(self):
browser.headers['X-Cache-Operation'])
self.assertEqual('max-age=0, must-revalidate, private',
browser.headers['Cache-Control'])
- tag = '"|test_user_1_|{0}|en|{1}|0'.format(
+ tag = '"|test_user_1_|{0}|en|{1}|0|1'.format(
catalog.getCounter(),
default_skin,
)
@@ -175,7 +175,7 @@ def test_composite_views(self):
# This should use cacheInBrowser
self.assertEqual('max-age=0, must-revalidate, private',
browser.headers['Cache-Control'])
- tag = '"|test_user_1_|{0}|en|{1}'.format(
+ tag = '"|test_user_1_|{0}|en|{1}|0'.format(
catalog.getCounter(),
default_skin,
)
@@ -223,7 +223,7 @@ def test_composite_views(self):
# This should use cacheInBrowser
self.assertEqual('max-age=0, must-revalidate, private',
browser.headers['Cache-Control'])
- tag = '"||{0}|en|{1}|0'.format(catalog.getCounter(), default_skin)
+ tag = '"||{0}|en|{1}|0|0'.format(catalog.getCounter(), default_skin)
self.assertEqual(tag, _normalize_etag(browser.headers['ETag']))
self.assertGreater(now, dateutil.parser.parse(
browser.headers['Expires']))
@@ -240,7 +240,7 @@ def test_composite_views(self):
# This should use cacheInBrowser
self.assertEqual('max-age=0, must-revalidate, private',
browser.headers['Cache-Control'])
- tag = '"||{0}|en|{1}'.format(catalog.getCounter(), default_skin)
+ tag = '"||{0}|en|{1}|0'.format(catalog.getCounter(), default_skin)
self.assertEqual(tag, _normalize_etag(browser.headers['ETag']))
self.assertGreater(now, dateutil.parser.parse(
browser.headers['Expires']))
@@ -260,7 +260,7 @@ def test_composite_views(self):
self.assertIn(testText, browser.contents)
self.assertEqual('max-age=0, must-revalidate, private',
browser.headers['Cache-Control'])
- tag = '"||{0}|en|{1}'.format(catalog.getCounter(), default_skin)
+ tag = '"||{0}|en|{1}|0'.format(catalog.getCounter(), default_skin)
self.assertEqual(tag, _normalize_etag(browser.headers['ETag']))
self.assertGreater(now, dateutil.parser.parse(
browser.headers['Expires']))