From f02197fc08ed042d688b707c20e9dd1903894367 Mon Sep 17 00:00:00 2001 From: gforcada Date: Mon, 9 May 2022 00:55:25 +0200 Subject: [PATCH] [fc] Repository: plone.app.iterate Branch: refs/heads/master Date: 2022-03-10T14:29:03+01:00 Author: ale-rt (ale-rt) Commit: https://github.com/plone/plone.app.iterate/commit/93f4d2284baa2879bf68af64114f2c63d14af135 Fix a typo in a a call to reindexObject Fixes #101 Files changed: A news/101.bugfix M plone/app/iterate/browser/checkout.py Repository: plone.app.iterate Branch: refs/heads/master Date: 2022-05-09T00:55:25+02:00 Author: Gil Forcada Codinachs (gforcada) Commit: https://github.com/plone/plone.app.iterate/commit/8e7e27de2a13bb2eed901bac1127eb065580db86 Merge pull request #102 from plone/110.bugfix Fix a typo in a a call to reindexObject Files changed: A news/101.bugfix M plone/app/iterate/browser/checkout.py --- last_commit.txt | 137 +++++++----------------------------------------- 1 file changed, 18 insertions(+), 119 deletions(-) diff --git a/last_commit.txt b/last_commit.txt index 57c7cb3a64..3660c7c5bc 100644 --- a/last_commit.txt +++ b/last_commit.txt @@ -1,137 +1,36 @@ -Repository: plone.app.caching +Repository: plone.app.iterate Branch: refs/heads/master -Date: 2022-05-06T10:34:56+02:00 -Author: Jens W. Klein (jensens) -Commit: https://github.com/plone/plone.app.caching/commit/d352e1673be789b9438ae75c64c6b74e124cb007 +Date: 2022-03-10T14:29:03+01:00 +Author: ale-rt (ale-rt) +Commit: https://github.com/plone/plone.app.iterate/commit/93f4d2284baa2879bf68af64114f2c63d14af135 -move to registry folder +Fix a typo in a a call to reindexObject -Files changed: -A plone/app/caching/profiles/default/registry/basesettings.xml -D plone/app/caching/profiles/default/registry.xml - -b'diff --git a/plone/app/caching/profiles/default/registry.xml b/plone/app/caching/profiles/default/registry/basesettings.xml\nsimilarity index 100%\nrename from plone/app/caching/profiles/default/registry.xml\nrename to plone/app/caching/profiles/default/registry/basesettings.xml\n' - -Repository: plone.app.caching - - -Branch: refs/heads/master -Date: 2022-05-06T12:11:11+02:00 -Author: Jens W. Klein (jensens) -Commit: https://github.com/plone/plone.app.caching/commit/a16b9b1954bf8e35a0d66ff9aa20bc7b92131da2 - -review documentation - -Files changed: -M docs/caching-control-panel.rst -M docs/caching-profiles.rst -M docs/caching-proxies.rst -M docs/composite-views.rst -M docs/etags.rst -M docs/ram-cache.rst -M docs/restapi.rst - -b'diff --git a/docs/caching-control-panel.rst b/docs/caching-control-panel.rst\nindex 4b759f9..5eb1234 100644\n--- a/docs/caching-control-panel.rst\n+++ b/docs/caching-control-panel.rst\n@@ -1,33 +1,35 @@\n The caching control panel\n -------------------------\n \n-After installation, you will find a Caching control panel in Plone\'s site\n-setup. This consists of four main tabs:\n+After installation, you will find a Caching control panel in Plone\'s site setup.\n \n-* *Change settings*, where you can control caching behaviour\n+This consists of four main tabs:\n \n-* *Import settings*, where you can import pre-defined profiles of cache\n- settings\n+Change settings\n+ where you can control caching behaviour\n \n-* *Purge caching proxy*, where you can manually purge content from a caching\n- proxy. This tab only appears if you have purging enabled under\n- *Change settings*.\n+Import settings\n+ where you can import pre-defined profiles of cache settings\n \n-* *RAM cache*, where you can view statistics about and purge the RAM cache.\n+Purge caching proxy\n+ where you can manually purge content from a caching proxy.\n+ This tab only appears if you have purging enabled under *Change settings*.\n+\n+RAM cache\n+ where you can view statistics about and purge the RAM cache.\n \n Under the settings tab, you will find four fieldsets:\n \n-* *General settings*, for global options such as turning caching on or off.\n+General settings\n+ for global options such as turning caching on or off.\n \n-* *Caching proxies*, where you can control Plone\'s use of a caching proxy\n- such as Squid or Varnish.\n+Caching proxies\n+ where you can control Plone\'s use of a caching proxy such or Varnish or a CDN.\n \n-* *Caching operation mappings*, where caching rulesets (hints about views and\n- resources used for caching purposes) can be associated with caching\n- operations (which either intercept a request to return a cached response, or\n- modifies a response to add cache control headers). This is also where\n- rulesets for legacy page templates (created through the web or the\n- portal_skins tool) are configured.\n+Caching operation mappings\n+ where caching rulesets (hints about views and resources used for caching purposes) can be associated with caching operations.\n+ Those either intercept a request to return a cached response, or modifies a response to add cache control headers.\n+ This is also where rulesets for legacy page templates (created through the web or the portal_skins tool) are configured.\n \n-* *Detailed settings*, where you can configure parameters for individual\n- caching operations.\n+Detailed settings\n+ where you can configure parameters for individual caching operations.\ndiff --git a/docs/caching-profiles.rst b/docs/caching-profiles.rst\nindex f523cd1..826acfd 100644\n--- a/docs/caching-profiles.rst\n+++ b/docs/caching-profiles.rst\n@@ -1,21 +1,17 @@\n Caching profiles\n ----------------\n \n-All persistent configuration for the caching machinery is stored in the\n-configuration registry, as managed by ``plone.app.registry``. This can be\n-modified using the ``registry.xml`` GenericSetup import step. The *Import\n-settings* tab of the control panel allows you to import these caching\n-profiles.\n+All persistent configuration for the caching machinery is stored in the configuration registry, as managed by ``plone.app.registry``.\n+This can be modified using the ``registry.xml`` GenericSetup import step.\n+The *Import settings* tab of the control panel allows you to import these caching profiles.\n \n \n Default caching profiles\n ~~~~~~~~~~~~~~~~~~~~~~~~\n \n-``plone.app.caching`` includes three default caching profiles. Two of these\n-profiles encapsulate the cache settings that are known to work well with a\n-typical default Plone installation. The third is an example profile for a\n-"split-view" caching setup (see the split-view discussion later in this\n-document).\n+``plone.app.caching`` includes three default caching profiles.\n+Two of these profiles encapsulate the cache settings that are known to work well with a typical default Plone installation.\n+The third is an example profile for a "split-view" caching setup (see the split-view discussion later in this document).\n \n The three default caching profiles:\n \ndiff --git a/docs/caching-proxies.rst b/docs/caching-proxies.rst\nindex f5955fc..46b974e 100644\n--- a/docs/caching-proxies.rst\n+++ b/docs/caching-proxies.rst\n@@ -1,80 +1,60 @@\n Caching proxies\n ---------------\n \n-It is common to place a so-called caching reverse proxy in front of Zope\n-when hosting large Plone sites. On Unix, a popular option is `Varnish`_,\n-although `Squid`_ is also a good choice. On Windows, you can use Squid\n-or the (commercial, but better) `Enfold Proxy`_.\n-\n-It is important to realise that whilst ``plone.app.caching`` provides\n-some functionality for controlling how Plone interacts with a caching\n-proxy, the proxy itself must be configured separately.\n-\n-Some operations in ``plone.app.caching`` can set response headers that\n-instruct the caching proxy how best to cache content. For example, it is\n-normally a good idea to cache static resources (such as images and\n-stylesheets) and "downloadables" (such as Plone content of the types ``File``\n-or ``Image``) in the proxy. This content will then be served to most users\n-straight from the proxy, which is much faster than Zope.\n-\n-The downside of this approach is that an old version of a content item may\n-returned to a user, because the cache has not been updated since the item\n-was modified. There are three general strategies for dealing with this:\n-\n-* Since resources are cached in the proxy based on their URL, you can\n- "invalidate" the cached copy by changing an item\'s URL when it is updated.\n- This is the approach taken by Plone\'s ResourceRegistries:\n- in production mode, the links that are inserted\n- into Plone\'s content pages for resource managed by ResourceRegistries\n- contain a time-based token, which changes when the ResourceRegistries\n- are updated, more specifically: when the resource bundles are combined.\n- This approach has the benefit of also being able to\n- "invalidate" content stored in a user\'s browser cache.\n-\n-* All caching proxies support setting timeouts. This means that content may\n- be stale, but typically only up to a few minutes. This is sometimes an\n- acceptable policy for high-volume sites where most users do not log in.\n-\n-* Most caching proxies support receiving PURGE requests for paths that\n- should be purged. For example, if the proxy has cached a resource at\n- ``/logo.jpg``, and that object is modified, a PURGE request could be sent\n- to the proxy (originating from Zope, not the client) with the same path to\n- force the proxy to fetch a new version the next time the item is requested.\n-\n-The final option, of course is to avoid caching content in the proxy\n-altogether. The default policies will not allow standard content pages to\n-be cached in the proxy, because it is too difficult to invalidate cached\n-instances. For example, if you change a content item\'s title, that may\n-require invalidation of a number of pages where that title appears in the\n-navigation tree, folder listings, ``Collections``, portlets, and so on.\n-Tracking all these dependencies and purging in an efficient manner is\n-impossible unless the caching proxy configuration is highly customised for\n-the site.\n+It is common to place a so-called caching reverse proxy in front of Zope when hosting large Plone sites.\n+If hosted by yourself, a popular option is `Varnish`_.\n+On the other hand there are commercial hosted CDN services like Cloudflare, CloudFront, Fastly and more.\n+\n+It is important to realise that whilst ``plone.app.caching`` provides some functionality for controlling how Plone interacts with a caching proxy.\n+The proxy itself must be configured separately.\n+\n+Some operations in ``plone.app.caching`` can set response headers that instruct the caching proxy how best to cache content.\n+For example, it is normally a good idea to cache static resources (such as images and stylesheets) and "downloadables" in the proxy.\n+This are i.e. Plone content of the types ``File`` or ``Image``.\n+This content will then be served to most users straight from the proxy, which is much faster than Zope.\n+\n+The downside of this approach is that an old version of a content item may returned to a user, because the cache has not been updated since the item was modified.\n+There are three general strategies for dealing with this:\n+\n+* 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.\n+ This is the approach taken by Plone\'s resource management:\n+ in production mode, the links that are inserted into Plone\'s content pages for resource managed by Plone contain a hash-based token, which changes when the main bundle file changes.\n+ This approach has the benefit of also being able to "invalidate" content stored in a user\'s browser cache.\n+\n+* All caching proxies support setting timeouts.\n+ This means that content may be stale, but typically only up to a few minutes.\n+ This is sometimes an acceptable policy for high-volume sites where most users do not log in.\n+\n+* Most caching proxies support receiving PURGE requests for paths that should be purged.\n+ Given the proxy has cached a resource at ``/logo.jpg``, and that object is modified.\n+ Then a PURGE request could be sent to the proxy (originating from Zope, not the client) with the same path to force the proxy to fetch a new version the next time the item is requested.\n+\n+The final option, of course is to avoid caching content in the proxy altogether.\n+The default policies will not allow standard listing pages to be cached in the proxy, because it is too difficult to invalidate cached instances.\n+For example, if you change a content item\'s title, that may require invalidation of a number of pages where that title appears in the navigation tree, folder listings, ``Collections``, portlets, and so on.\n+Tracking all these dependencies and purging in an efficient manner is impossible unless the caching proxy configuration is highly customised for the site.\n+\n+Nevertheless a "terse" policy allows caching for just a few seconds of all pages to reduce the load on the backend on high traffic situations.\n \n \n Purging a caching proxy\n ~~~~~~~~~~~~~~~~~~~~~~~\n \n Synchronous and asynchronous purging is enabled via `plone.cachepurging`_.\n-In the control panel, you can configure the use of a proxy via various\n-options, such as:\n+In the control panel, you can configure the use of a proxy via various options, such as:\n \n * Whether or not to enable purging globally.\n \n * The address of the caching server to which PURGE requests should be sent.\n \n-* Whether or not virtual host rewriting takes place before the caching proxy\n- receives a URL or not. This has implications for how the PURGE path is\n- constructed.\n+* Whether or not virtual host rewriting takes place before the caching proxy receives a URL or not.\n+ This has implications for how the PURGE path is constructed.\n \n-* Any domain aliases for your site, to enable correct purging of content\n- served via e.g. http://example.com and http://www.example.com.\n+* Any domain aliases for your site, to enable correct purging of content served via e.g. http://example.com and http://www.example.com.\n \n-The default purging policy is geared mainly towards purging file and image\n-resources, not content pages, although basic purging of content pages is\n-included. The actual paths to purge are constructed from a number of\n-components providing the ``IPurgePaths`` interface. See ``plone.cachepurging``\n-for details on how this works, especially if you need to write your own.\n+The default purging policy is geared mainly towards purging file and image resources, not content pages, although basic purging of content pages is included.\n+The actual paths to purge are constructed from a number of components providing the ``IPurgePaths`` interface.\n+See ``plone.cachepurging`` for details on how this works, especially if you need to write your own.\n \n The default purge paths include:\n \n@@ -86,56 +66,27 @@ The default purge paths include:\n \n * ${object_path}/${default-view} -- in case a default view template is used\n \n-* The download URLs for any Archetypes object fields, in the case of\n- Archetypes content. This includes support for the standard ``File`` and\n- ``Image`` types.\n+* The download URLs for any content object fields, given the type contains blobs.\n+This includes support for the standard ``File`` and ``Image`` types.\n \n-Files and images created (or customised) in the ZMI are purged automatically\n-when modified. Files managed through the ResourceRegistries do not need\n-purging, since they have "stable" URLs. To purge Plone content when modified\n-(or removed), you must select the content types in the control panel. By\n-default, only the ``File`` and ``Image`` types are purged.\n+Files and images created (or customised) in the ZMI are purged automatically when modified.\n+Bundles managed through the resource registration do not need purging, since they have "stable" URLs.\n+To purge Plone content when modified (or removed), you must select the content types in the control panel.\n+By default, only the ``File`` and ``Image`` types are purged.\n \n-You should not enable purging for types that are not likely to be cached in\n-the proxy. Although purging happens asynchronously at the end of the request,\n-it may still place unnecessary load on your server.\n+You should not enable purging for types that are not likely to be cached in the proxy.\n+Although purging happens asynchronously at the end of the request, it may still place unnecessary load on your server.\n \n-Finally, you can use the *Purge* tab in the control panel to manually purge\n-one or more URLs. This is a useful way to debug cache purging, as well as\n-a quick solution for the awkward situation where your boss walks in and\n-wonders why the "about us" page is still showing that old picture of him,\n-before he had a new haircut.\n+Finally, you can use the *Purge* tab in the control panel to manually purge one or more URLs.\n+This is a useful way to debug cache purging.\n+It offers as well as a quick solution for the awkward situation where your boss walks in and wonders why the "about us" page is still showing that old picture of him, before he had a new haircut.\n \n \n Installing and configuring a caching proxy\n ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n \n-There are buildout recipes for building and configuring proxy configs: `plone.recipe.squid`_ and `plone.recipe.varnish`_.\n-\n-\n-Running Plone behind Apache 2.2 with mod_cache\n-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n-\n-Apache 2.2 has a known bug around its handling of the HTTP response header\n-CacheControl with value max-age=0 or headers Expires with a date in the past.\n-In these scenarios mod_cache will not cache the response no matter what value\n-of s-maxage is set.\n-\n-https://issues.apache.org/bugzilla/show_bug.cgi?id=35247\n-\n-One possible workaround for this is to use mod_headers directives in your\n-Apache configuration to set max-age=1 if s-maxage is positive and max-age is 0\n-and also to drop the Expires header\n-\n-Header edit Cache-Control max-age=0(.*s-maxage=[1-9].*) max-age=1$1\n-Header unset Expires\n-\n-Dropping the Expires header has the disadvantage that HTTP 1.0 clients and\n-proxies may not cache your responses as you wish.\n+There is a buildout recipes for building and configuring proxy configs: `plone.recipe.varnish`_.\n \n .. _Varnish: http://varnish-cache.org\n-.. _Squid: http://squid-cache.org\n-.. _Enfold Proxy: http://enfoldsystems.com/software/proxy/\n-.. _plone.recipe.squid: http://pypi.python.org/pypi/plone.recipe.squid\n .. _plone.recipe.varnish: http://pypi.python.org/pypi/plone.recipe.varnish\n .. _plone.cachepurging: http://pypi.python.org/pypi/plone.cachepurging\ndiff --git a/docs/composite-views.rst b/docs/composite-views.rst\nindex c7c8a54..1035502 100644\n--- a/docs/composite-views.rst\n+++ b/docs/composite-views.rst\n@@ -1,29 +1,19 @@\n Composite views\n ---------------\n \n-A ``composite view`` is just a general term for most page views you see when\n-you visit a Plone site. It includes all content item views, content folder\n-views, and many template views. For our purposes, the distinguishing\n-characteristic of composite views is the difficulty inherent in keeping track\n-of all changes that might affect the final composited view. Because of the\n-difficulty of dependancy tracking, composite views are often notoriously\n-difficult to purge reliably from caching proxies so the default caching\n-profiles set headers which expire the cache immediately (i.e. *weak caching*).\n+A ``composite view`` is just a general term for most page views you see when you visit a Plone site.\n+It includes all content item views, content folder views, and many template views.\n+For our purposes, the distinguishing characteristic of composite views is the difficulty inherent in keeping track of all changes that might affect the final composited view.\n+Because of the difficulty of dependancy tracking, composite views are often notoriously difficult to purge reliably from caching proxies so the default caching profiles set headers which expire the cache immediately (i.e. *weak caching*).\n \n-However, most of the inline resources linked to from the composite view (css,\n-javascript, images, etc.) can be cached very well in proxy so the overall\n-speed of most composite views will always be better with a caching proxy in\n-front despite the page itself not being cached.\n-\n-Also, when using Squid as a caching proxy, we can still see some additional\n-speed improvement as Squid supports conditional requests to the backend and\n-304 responses from plone.app.caching are relatively quick. This means that\n-even though the proxy cache will expire immediately, Squid can revalidate its\n-cache relatively quickly. Varnish does not currently support conditional\n-requests to the backend.\n+However, most of the inline resources linked to from the composite view (css, javascript, images, etc.) can be cached very well in proxy so the overall speed of most composite views will always be better with a caching proxy in front despite the page itself not being cached.\n \n For relatively stable composite views or for those views for which you can\n tolerate some potential staleness, you might be tempted to try switching from\n *weak caching* to *moderate caching* with the ``s-maxage`` expiration\n value set to some tolerable value but first make sure you understand the\n issues regarding "split view" caching (see below).\n+\n+A way to speedup a site with lots of composite view is to use "Terse" caching.\n+It caches a page for just a small time (be default 10s in broser and 60s in the caching proxy).\n+Thus the content is almost up to date, but on high load visitors are getting pages served from the cache.\ndiff --git a/docs/etags.rst b/docs/etags.rst\nindex 3cdc796..dac1b29 100644\n--- a/docs/etags.rst\n+++ b/docs/etags.rst\n@@ -1,75 +1,65 @@\n ETags\n -----\n \n-ETags are used in to check whether pages need to be re-calculated or can be\n-served from cache. An ETag is simply a string. Under ``plone.app.caching``,\n-it is a string of tokens separated by pipe characters. The tokens hold values\n-such as a user id, the current skin name, or a counter indicating how many\n-objects have been added to the site. The idea is that the browser sends a\n-request with the ETag included in an ``If-None-Match`` header. Plone can then\n-quickly calculate the current ETag for the requested resource. If the ETag\n-is the same, Plone can reply with ``304 NOT MODIFIED`` response, telling the\n-browser to use its cached copy. Otherwise, Plone renders the page and returns\n-it as normal.\n-\n-Many caching operations use ETags. The tokens to include are typically\n-listed in an ``etags`` tuple in the operation\'s options.\n+ETags are used in to check whether pages need to be re-calculated or can be served from cache.\n+An ETag is simply a string. Under ``plone.app.caching``, it is a string of tokens separated by pipe characters.\n+The tokens hold values such as a user id, the current skin name, or a counter indicating how many objects have been added to the site.\n+The idea is that the browser sends a request with the ETag included in an ``If-None-Match`` header.\n+Plone can then quickly calculate the current ETag for the requested resource.\n+If the ETag is the same, Plone can reply with ``304 NOT MODIFIED`` response, telling the browser to use its cached copy.\n+Otherwise, Plone renders the page and returns it as normal.\n+\n+Many caching operations use ETags. The tokens to include are typically listed in an ``etags`` tuple in the operation\'s options.\n \n The ETag names tokens supported by default are:\n \n-* userid\n+userid\n The current user\'s id\n \n-* roles\n+roles\n A list of the current user\'s roles in the given context\n \n-* language\n+language\n The language(s) accepted by the browser, in the ``ACCEPT_LANGUAGE`` header\n \n-* userLanguage\n+userLanguage\n The current user\'s preferred language\n \n-* lastModified\n+lastModified\n A timestamp indicating the last-modified date of the given context\n \n-* catalogCounter\n- A counter that is incremented each time the catalog is updated, i.e. each\n- time content in the site is changed.\n+catalogCounter\n+ A counter that is incremented each time the catalog is updated.\n+ I.e. each time content in the site is changed.\n \n-* locked\n+locked\n Whether or not the given context is locked for editing.\n \n-* skin\n+skin\n The name of the current skin (theme)\n \n-* resourceRegistries\n- A timestamp indicating the last-modified timestamp for the\n- Resource Registries. This is useful for avoiding requests for expired\n- resources from cached pages.\n+resourceRegistries\n+ A timestamp indicating the last-modified timestamp for the Resource Registries.\n+ This is useful for avoiding requests for expired resources from cached pages.\n \n-It is possible to provide additional tokens by registering an ``IETagValue``\n-adapter. This should be a named adapter on the published object (typically a\n-view, file resource or Zope page template object) and request, with a unique\n-name. The name is used to look up the component. Thus, you can also override\n-one of the tokens above for a particular type of context or request (e.g. via\n-a browser layer), by registering a more specific adapter with the same name.\n+It is possible to provide additional tokens by registering an ``IETagValue`` adapter.\n+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 name.\n+The name is used to look up the component. Thus, you can also override one of the tokens above for a particular type of context or request (e.g. via a browser layer), by registering a more specific adapter with the same name.\n \n As an example, here is the ``language`` adapter::\n \n- from zope.interface import implements\n- from zope.interface import Interface\n-\n- from zope.component import adapts\n from plone.app.caching.interfaces import IETagValue\n+ from zope.component import adapter\n+ from zope.interface import implementer\n+ from zope.interface import Interface\n \n+ @implementer(IETagValue)\n+ @adapter(Interface, Interface)\n class Language(object):\n """The ``language`` etag component, returning the value of the\n HTTP_ACCEPT_LANGUAGE request key.\n """\n \n- implements(IETagValue)\n- adapts(Interface, Interface)\n-\n def __init__(self, published, request):\n self.published = published\n self.request = request\ndiff --git a/docs/ram-cache.rst b/docs/ram-cache.rst\nindex 1df0b5e..d9282a1 100644\n--- a/docs/ram-cache.rst\n+++ b/docs/ram-cache.rst\n@@ -1,64 +1,49 @@\n The RAM cache\n -------------\n \n-In addition to caching content in users\' browsers (through setting appropriate\n-response headers) and a caching proxy, Plone can cache certain information in\n-memory. This is done in two main ways:\n+In addition to caching content in users\' browsers (through setting appropriate response headers) and a caching proxy, Plone can cache certain information in memory.\n+This is done in two main ways:\n \n-* Developers may use the ``plone.memoize`` package\'s ``ram`` module to cache\n- the results of certain functions in RAM. For example, some viewlets and\n- portlets cache their rendered output in RAM for a time, alleviating the need\n- to calculate them every time.\n+* Developers may use the ``plone.memoize`` package\'s ``ram`` module to cache the results of certain functions in RAM.\n+ For example, some viewlets and portlets cache their rendered output in RAM for a time, alleviating the need to calculate them every time.\n \n-* Some caching operations may cache an entire response in memory, so that\n- they can later intercept the request to return a cached response..\n+* Some caching operations may cache an entire response in memory, so that they can later intercept the request to return a cached response.\n \n-Caching in RAM in Zope is not as efficient as caching in a proxy, for a number\n-of reasons:\n+Caching in RAM in Zope is not as efficient as caching in a proxy, for a number of reasons:\n \n-* Zope still has to perform traversal, security, transaction management and so\n- on before serving a request with a RAM-cached response.\n+* Zope still has to perform traversal, security, transaction management and so on before serving a request with a RAM-cached response.\n \n-* Zope\'s use of memory is not as efficient as that of a finely optimised\n- caching proxy.\n+* Zope\'s use of memory is not as efficient as that of a finely optimised caching proxy.\n \n-* Storing lots of content in RAM may compete with the standard ZODB object\n- cache and other memory pools used by Zope, thus slowing down Zope overall.\n+* Storing lots of content in RAM may compete with the standard ZODB object cache and other memory pools used by Zope, thus slowing down Zope overall.\n \n-* In multi-client ZEO setups, the RAM cache is (by default at least) not\n- shared among instances (though it is shared among threads in that instance).\n- Thus, each ZEO client process will maintain its own cache.\n+* In multi-client ZEO setups, the RAM cache is (by default at least) not shared among instances (though it is shared among threads in that instance).\n+ Thus, each Plone client process will maintain its own cache.\n \n-You can use the *RAM cache* tab in the caching control panel to view\n-statistics about the use of the RAM cache. On the *Change settings* tab, you\n-can also control the size of the cache, and the frequency with which it is\n-purged of old items.\n+You can use the *RAM cache* tab in the caching control panel to view statistics about the use of the RAM cache.\n+On the *Change settings* tab, you can also control the size of the cache, and the frequency with which it is purged of old items.\n \n \n Alternative RAM cache implementations\n ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n \n-The RAM cache exposed through ``plone.memoize.ram`` is looked up via an\n-``ICacheChoser`` utility. The default implementation looks up a\n-``zope.ramcache.interfaces.ram.IRAMCache`` utility. Plone installs a local\n-such utility (to allows its settings to be persisted - the cache itself is\n-not persistent), which is shared by all users of the cache.\n+The RAM cache exposed through ``plone.memoize.ram`` is looked up via an ``ICacheChoser`` utility.\n+The default implementation looks up a ``zope.ramcache.interfaces.ram.IRAMCache`` utility.\n+Plone installs a local such utility (to allows its settings to be persisted - the cache itself is not persistent), which is shared by all users of the cache.\n \n-You can provide your own ``ICacheChooser`` utility to change this policy,\n-by installing this as a local utility or overriding it in ``overrides.zcml``.\n-One reason to do this may be to back the cache with a `memcached`_ server,\n-which would allow a single cache to be shared among multiple Zope clients.\n+You can provide your own ``ICacheChooser`` utility to change this policy, by installing this as a local utility or overriding it in ``overrides.zcml``.\n+One reason to do this may be to back the cache with a `memcached`_ server, which would allow a single cache to be shared among multiple Zope clients.\n \n Below is a sketch of such a cache chooser, courtesy of Wojciech Lichota::\n \n- from threading import local\n- from pylibmc import Client\n- from zope.interface import implements\n from plone.memoize.interfaces import ICacheChooser\n from plone.memoize.ram import MemcacheAdapter\n+ from pylibmc import Client\n+ from threading import local\n+ from zope.interface import implementer\n \n+ @implementer(ICacheChooser)\n class MemcachedCacheChooser(object):\n- implements(ICacheChooser)\n _v_thread_local = local()\n \n def getClient(self):\ndiff --git a/docs/restapi.rst b/docs/restapi.rst\nindex 45544d4..9c0b211 100644\n--- a/docs/restapi.rst\n+++ b/docs/restapi.rst\n@@ -12,11 +12,12 @@ We have endpoints following classic `plone.content.itemView` content and do not\n Those can be handled with the default rules, including purge.\n \n There are others delivering dynamic content, like search, impossible to purge.\n-Those shall be cached using a shorttime cache (like some seconds to some minutes.\n+Those shall be cached using a shorttime cache (like some seconds to some minutes).\n \n-A rulesetType will be introduced called `plone.content.dynamic`.\n-It will is similar to be configured to cache by default in browser 15sec, in caching-proxy 60 seconds.\n-Its goal is primary to reduce the load/peak-load on the server. Also, it reduces the impact of loading the same endpoint more than one time.\n+This get covered by the rulesetType `plone.content.dynamic`.\n+It is configured to cache by default in browser 10sec, in caching-proxy 60 seconds.\n+Its goal is primary to reduce the load/peak-load on the server.\n+Also, it reduces the impact of loading the same endpoint more than one time in one page.\n \n \n plone.restapi GET endpoints\n' - -Repository: plone.app.caching - - -Branch: refs/heads/master -Date: 2022-05-06T12:14:44+02:00 -Author: Jens W. Klein (jensens) -Commit: https://github.com/plone/plone.app.caching/commit/e644972efca9ce8bbe074f71d1e254d97e4387f8 - -remove split-view - it was not maintained for a while and is pointless - -Files changed: -M docs/caching-profiles.rst -M plone/app/caching/caching.zcml -D docs/split-views.rst -D plone/app/caching/profiles/with-caching-proxy-splitviews/registry.xml - -b'diff --git a/docs/caching-profiles.rst b/docs/caching-profiles.rst\nindex 826acfd..6ff8803 100644\n--- a/docs/caching-profiles.rst\n+++ b/docs/caching-profiles.rst\n@@ -11,7 +11,6 @@ Default caching profiles\n \n ``plone.app.caching`` includes three default caching profiles.\n Two of these profiles encapsulate the cache settings that are known to work well with a typical default Plone installation.\n-The third is an example profile for a "split-view" caching setup (see the split-view discussion later in this document).\n \n The three default caching profiles:\n \n@@ -24,11 +23,6 @@ The three default caching profiles:\n profile are some settings to enable proxy caching of files/images\n in content space and content feeds.\n \n-* **With caching proxy (and split-view caching)**\n- An example profile for a caching proxy setup with split-view\n- caching enabled. This example requires a special proxy setup.\n-\n-\n Custom caching profiles\n ~~~~~~~~~~~~~~~~~~~~~~~\n \ndiff --git a/docs/split-views.rst b/docs/split-views.rst\ndeleted file mode 100644\nindex fe70321..0000000\n--- a/docs/split-views.rst\n+++ /dev/null\n@@ -1,32 +0,0 @@\n-Split views\n------------\n-\n-A non-zero expiration for proxy or browser caching of a composite view will\n-often require some special handling to deal with "split view" caching.\n-\n-Caching proxies and browsers keep track of cached entries by using the URL\n-as a key. If a Vary header is included in the response then those request\n-headers listed in Vary are also included in the cache key. In most cases,\n-this is sufficient to uniquely identify all responses. However, there are\n-exceptions. We call these exceptions "split views". Anytime you have\n-multiple views sharing the same cache key, you have a split view problem.\n-Split views cannot be cached in proxies or browsers without mixing up the\n-responses.\n-\n-In the Plone case, most composite views are also split views because they\n-provide different views to anonymous and authenticated requests.\n-In Plone, authenticated requests are tracked via cookies which are not\n-usually used in cache keys.\n-\n-One solution to this problem is to add a ``Vary:Cookie`` response header but,\n-unfortunately, since cookies are used for all sorts of state maintenance and\n-web tracking, this will usually result in very inefficient caching.\n-\n-Another solution is to enforce a different domain name, different path,\n-or different protocol (https/http) for authenticated versus anonymous\n-responses.\n-\n-Yet another solution involves intercepting the request and dynamically adding\n-a special ``X-Anonymous`` header to the anonymous request and then adding\n-``Vary:X-Anonymous`` to the split view response so that this header will added\n-to the cache key.\ndiff --git a/plone/app/caching/caching.zcml b/plone/app/caching/caching.zcml\nindex e081d57..adf9a34 100644\n--- a/plone/app/caching/caching.zcml\n+++ b/plone/app/caching/caching.zcml\n@@ -27,15 +27,6 @@\n directory="profiles/without-caching-proxy"\n />\n \n- \n-\n \n \ndiff --git a/plone/app/caching/profiles/with-caching-proxy-splitviews/registry.xml b/plone/app/caching/profiles/with-caching-proxy-splitviews/registry.xml\ndeleted file mode 100644\nindex 718d54e..0000000\n--- a/plone/app/caching/profiles/with-caching-proxy-splitviews/registry.xml\n+++ /dev/null\n@@ -1,156 +0,0 @@\n-\n-\n- \n- \n- plone.app.caching.strongCaching\n- plone.app.caching.strongCaching\n- plone.app.caching.moderateCaching\n- plone.app.caching.moderateCaching\n- plone.app.caching.moderateCaching\n- plone.app.caching.moderateCaching\n- \n- \n-\n-\n- \n- \n- \n- \n- userid\n- catalogCounter\n- userLanguage\n- skin\n- locked\n- resourceRegistries\n- \n- \n- \n- \n- True\n- \n- \n- \n- 3600\n- \n- \n- \n- X-Anonymous\n- \n-\n-\n-\n- \n- \n- \n- \n- userid\n- catalogCounter\n- userLanguage\n- skin\n- locked\n- copy\n- resourceRegistries\n- \n- \n- \n- \n- True\n- \n- \n- \n- 3600\n- \n- \n- \n- X-Anonymous\n- \n-\n-\n- \n- \n- \n- \n- userid\n- catalogCounter\n- userLanguage\n- skin\n- locked\n- copy\n- \n- \n- \n- \n- True\n- \n- \n- \n- Accept\n- \n- \n- \n- 0\n- \n- \n- \n- 0\n- \n- \n- \n- X-Anonymous, Accept\n- \n-\n-\n- \n- \n- \n- 86400\n- \n- \n- \n- \n- userid\n- catalogCounter\n- userLanguage\n- skin\n- \n- \n- \n- \n- True\n- \n-\n-\n- \n- \n- \n- 86400\n- \n- \n- \n- True\n- \n-\n-\n- \n- \n- \n- 86400\n- \n- \n- \n- True\n- \n-\n-\n- \n- \n- \n- 31536000\n- \n- \n- \n- True\n- \n-\n-\n-\n' - -Repository: plone.app.caching - - -Branch: refs/heads/master -Date: 2022-05-06T12:16:34+02:00 -Author: Jens W. Klein (jensens) -Commit: https://github.com/plone/plone.app.caching/commit/b6f35f30b01f5e8711dc831e6085e9b89953f3ac - -minor code cleanup - -Files changed: -M plone/app/caching/lastmodified.py -M plone/app/caching/lookup.py -M plone/app/caching/operations/default.py -M plone/app/caching/operations/etags.py -M plone/app/caching/operations/ramcache.py -M plone/app/caching/operations/utils.py -M plone/app/caching/purge.py -M plone/app/caching/tests/test_etags.py -M plone/app/caching/tests/test_lastmodified.py -M plone/app/caching/tests/test_operation_utils.py -M plone/app/caching/utils.py - -b'diff --git a/plone/app/caching/lastmodified.py b/plone/app/caching/lastmodified.py\nindex b303565..f606dc0 100644\n--- a/plone/app/caching/lastmodified.py\n+++ b/plone/app/caching/lastmodified.py\n@@ -14,6 +14,7 @@\n \n \n try:\n+ # zope.dublincore is not partof Plone core, but might get used by addons\n from zope.dublincore.interfaces import IDCTimes\n except ImportError:\n \ndiff --git a/plone/app/caching/lookup.py b/plone/app/caching/lookup.py\nindex ff6cd76..1c2ae31 100644\n--- a/plone/app/caching/lookup.py\n+++ b/plone/app/caching/lookup.py\n@@ -62,14 +62,14 @@ def __call__(self):\n \n registry = queryUtility(IRegistry)\n if registry is None:\n- return None\n+ return\n \n ploneCacheSettings = registry.forInterface(IPloneCacheSettings, check=False)\n \n # 2. Get the name of the published object\n name = getattr(self.published, "__name__", None)\n if name is None:\n- return None\n+ return\n \n # 3. Look up the published name in the page template mapping\n ruleset = (\n@@ -82,12 +82,12 @@ def __call__(self):\n # 4. Find the parent of the published object\n parent = getattr(self.published, "__parent__", None)\n if parent is None:\n- return None\n+ return\n \n # 4.1. If the parent is a content object:\n parentPortalType = getattr(aq_base(parent), "portal_type", None)\n if parentPortalType is None:\n- return None\n+ return\n \n # 4.1.1. Get the default view of the parent content object\n defaultView = getObjectDefaultView(parent)\n@@ -95,7 +95,7 @@ def __call__(self):\n # 4.1.2. If the name of the published object is the same as the\n # default view of the parent:\n if defaultView != name:\n- return None\n+ return\n \n # 4.1.2.1. Look up the parent type in the content type\n # mapping\ndiff --git a/plone/app/caching/operations/default.py b/plone/app/caching/operations/default.py\nindex d3fbbbb..c129c03 100644\n--- a/plone/app/caching/operations/default.py\n+++ b/plone/app/caching/operations/default.py\n@@ -124,7 +124,7 @@ def interceptResponse(self, rulename, response, class_=None):\n \n # Check for cache stop request variables\n if cacheStop(self.request, rulename):\n- return None\n+ return\n \n # Check if this should be a 304 response\n if not isModified(self.request, etag=etag, lastModified=lastModified):\n@@ -152,8 +152,6 @@ def interceptResponse(self, rulename, response, class_=None):\n self.published, self.request, response, *cached\n )\n \n- return None\n-\n def modifyResponse(self, rulename, response, class_=None):\n options = lookupOptions(class_ or self.__class__, rulename)\n \n@@ -381,7 +379,7 @@ def __init__(self, published, request):\n self.request = request\n \n def interceptResponse(self, rulename, response):\n- return None\n+ return\n \n def modifyResponse(self, rulename, response):\n doNotCache(self.published, self.request, response)\ndiff --git a/plone/app/caching/operations/etags.py b/plone/app/caching/operations/etags.py\nindex 94987b0..cc504e2 100644\n--- a/plone/app/caching/operations/etags.py\n+++ b/plone/app/caching/operations/etags.py\n@@ -31,11 +31,11 @@ def __init__(self, published, request):\n def __call__(self):\n tool = queryUtility(IMembershipTool)\n if tool is None:\n- return None\n+ return\n \n member = tool.getAuthenticatedMember()\n if member is None:\n- return None\n+ return\n \n return member.getId()\n \n@@ -54,14 +54,14 @@ def __init__(self, published, request):\n def __call__(self):\n tool = queryUtility(IMembershipTool)\n if tool is None:\n- return None\n+ return\n \n if bool(tool.isAnonymousUser()):\n return "Anonymous"\n \n member = tool.getAuthenticatedMember()\n if member is None:\n- return None\n+ return\n \n return ";".join(sorted(member.getRolesInContext(getContext(self.published))))\n \n@@ -104,7 +104,7 @@ def __call__(self):\n (context, self.request), name="plone_portal_state"\n )\n if portal_state is None:\n- return None\n+ return\n return portal_state.default_language()\n \n \n@@ -122,7 +122,7 @@ def __init__(self, published, request):\n def __call__(self):\n lastModified = getLastModifiedAnnotation(self.published, self.request)\n if lastModified is None:\n- return None\n+ return\n return str(time.mktime(lastModified.utctimetuple()))\n \n \n@@ -140,7 +140,7 @@ def __init__(self, published, request):\n def __call__(self):\n catalog = queryUtility(ICatalogTool)\n if catalog is None:\n- return None\n+ return\n return str(catalog.getCounter())\n \n \n@@ -161,7 +161,7 @@ def __call__(self):\n (context, self.request), name="plone_context_state"\n )\n if context_state is None:\n- return None\n+ return\n return "1" if context_state.is_locked() else "0"\n \n \n@@ -179,7 +179,7 @@ def __call__(self):\n \n portal_skins = getToolByName(context, "portal_skins", None)\n if portal_skins is None:\n- return None\n+ return\n \n requestVariable = portal_skins.getRequestVarname()\n if requestVariable in self.request:\n@@ -192,7 +192,7 @@ def __call__(self):\n @adapter(Interface, Interface)\n class AnonymousOrRandom:\n """The ``anonymousOrRandom`` etag component. This is normally added\n- implicitly by the ``anonOnly`` setting. It will return None for anonymous\n+ implicitly by the ``anonOnly`` setting. It will return for anonymous\n users, but a random number for logged-in ones. The idea is to force a\n re-fetch of a page every time for logged-in users.\n """\n@@ -204,9 +204,9 @@ def __init__(self, published, request):\n def __call__(self):\n tool = queryUtility(IMembershipTool)\n if tool is None:\n- return None\n+ return\n if bool(tool.isAnonymousUser()):\n- return None\n+ return\n return f"{time.time()}{random.randint(0, 1000)}"\n \n \ndiff --git a/plone/app/caching/operations/ramcache.py b/plone/app/caching/operations/ramcache.py\nindex 8c4657e..be59cb2 100644\n--- a/plone/app/caching/operations/ramcache.py\n+++ b/plone/app/caching/operations/ramcache.py\n@@ -30,12 +30,10 @@ def transformUnicode(self, result, encoding):\n storeResponseInRAMCache(\n self.request, self.request.response, result.encode(encoding)\n )\n- return None\n \n def transformBytes(self, result, encoding):\n if self.responseIsSuccess() and IRAMCached.providedBy(self.request):\n storeResponseInRAMCache(self.request, self.request.response, result)\n- return None\n \n def transformIterable(self, result, encoding):\n if self.responseIsSuccess() and IRAMCached.providedBy(self.request):\n@@ -43,8 +41,6 @@ def transformIterable(self, result, encoding):\n storeResponseInRAMCache(self.request, self.request.response, result)\n # ITransform contract allows to return an "encoded string" aka bytes\n return result\n- return None\n \n def responseIsSuccess(self):\n- status = self.request.response.getStatus()\n- return status == 200\n+ return self.request.response.getStatus() == 200\ndiff --git a/plone/app/caching/operations/utils.py b/plone/app/caching/operations/utils.py\nindex 274233b..2d8327f 100644\n--- a/plone/app/caching/operations/utils.py\n+++ b/plone/app/caching/operations/utils.py\n@@ -251,7 +251,7 @@ def cacheInRAM(\n \n annotations = IAnnotations(request, None)\n if annotations is None:\n- return None\n+ return\n \n key = getRAMCacheKey(request, etag=etag, lastModified=lastModified)\n \n@@ -479,7 +479,7 @@ def checkType(context):\n published = published.__parent__\n \n if not checkType(published):\n- return None\n+ return\n \n return published\n \n@@ -508,10 +508,10 @@ def parseDateTime(str):\n try:\n dt = dateutil.parser.parse(str)\n except ValueError:\n- return None\n+ return\n \n if not dt:\n- return None\n+ return\n \n if dt.tzinfo is None:\n dt = datetime.datetime(\n@@ -534,7 +534,7 @@ def getLastModifiedAnnotation(published, request, lastModified=True):\n """\n \n if not lastModified:\n- return None\n+ return\n \n annotations = IAnnotations(request, None)\n if annotations is not None:\n@@ -558,15 +558,15 @@ def getLastModified(published, lastModified=True):\n """\n \n if not lastModified:\n- return None\n+ return\n \n lastModified = ILastModified(published, None)\n if lastModified is None:\n- return None\n+ return\n \n dt = lastModified()\n if dt is None:\n- return None\n+ return\n \n if dt.tzinfo is None:\n dt = datetime.datetime(\n@@ -593,8 +593,7 @@ def getExpiration(maxage):\n now = datetime.datetime.now()\n if maxage > 0:\n return now + datetime.timedelta(seconds=maxage)\n- else:\n- return now - datetime.timedelta(days=3650)\n+ return now - datetime.timedelta(days=3650)\n \n \n def getETagAnnotation(published, request, keys=(), extraTokens=()):\n@@ -603,7 +602,7 @@ def getETagAnnotation(published, request, keys=(), extraTokens=()):\n """\n \n if not keys and not extraTokens:\n- return None\n+ return\n \n annotations = IAnnotations(request, None)\n if annotations is not None:\n@@ -632,7 +631,7 @@ def getETag(published, request, keys=(), extraTokens=()):\n All tokens will be concatenated into an ETag string, separated by pipes.\n """\n if not keys and not extraTokens:\n- return None\n+ return\n \n tokens = []\n noTokens = True\n@@ -654,7 +653,7 @@ def getETag(published, request, keys=(), extraTokens=()):\n tokens.append(token)\n \n if noTokens:\n- return None\n+ return\n \n etag = "|" + "|".join(tokens)\n etag = etag.replace(",", ";") # commas are bad in etags\n@@ -727,10 +726,8 @@ def getRAMCache(globalKey=PAGE_CACHE_KEY):\n """\n \n chooser = queryUtility(ICacheChooser)\n- if chooser is None:\n- return None\n-\n- return chooser(globalKey)\n+ if chooser is not None:\n+ return chooser(globalKey)\n \n \n def getRAMCacheKey(request, etag=None, lastModified=None):\n@@ -835,10 +832,10 @@ def fetchFromRAMCache(\n \n cache = getRAMCache(globalKey)\n if cache is None:\n- return None\n+ return\n \n key = getRAMCacheKey(request, etag=etag, lastModified=lastModified)\n if key is None:\n- return None\n+ return\n \n return cache.get(key, default)\ndiff --git a/plone/app/caching/purge.py b/plone/app/caching/purge.py\nindex 4162b4a..850aeb0 100644\n--- a/plone/app/caching/purge.py\n+++ b/plone/app/caching/purge.py\n@@ -163,11 +163,11 @@ def _getRoot(self):\n \n plone_utils = getToolByName(self.context, "plone_utils", None)\n if plone_utils is None:\n- return None\n+ return\n \n thread = plone_utils.getDiscussionThread(self.context)\n if not thread:\n- return None\n+ return\n \n return thread[0]\n \ndiff --git a/plone/app/caching/tests/test_etags.py b/plone/app/caching/tests/test_etags.py\nindex 81a6f2e..986b4b0 100644\n--- a/plone/app/caching/tests/test_etags.py\n+++ b/plone/app/caching/tests/test_etags.py\n@@ -49,7 +49,7 @@ def __init__(self):\n pass\n \n def getAuthenticatedMember(self):\n- return None\n+ return\n \n provideUtility(DummyMembershipTool())\n \n@@ -101,7 +101,7 @@ def __init__(self):\n pass\n \n def getAuthenticatedMember(self):\n- return None\n+ return\n \n def isAnonymousUser(self):\n return True\n@@ -215,7 +215,7 @@ def __init__(self, context):\n self.context = context\n \n def __call__(self):\n- return None\n+ return\n \n provideAdapter(DummyLastModified)\n \ndiff --git a/plone/app/caching/tests/test_lastmodified.py b/plone/app/caching/tests/test_lastmodified.py\nindex 6af6a30..bcd4f29 100644\n--- a/plone/app/caching/tests/test_lastmodified.py\n+++ b/plone/app/caching/tests/test_lastmodified.py\n@@ -154,7 +154,7 @@ class Dummy:\n def modified(self):\n if self._mod is not None:\n return DateTime.DateTime(self._mod)\n- return None\n+ return\n \n d = Dummy()\n \ndiff --git a/plone/app/caching/tests/test_operation_utils.py b/plone/app/caching/tests/test_operation_utils.py\nindex 1f5dffb..4a60ee1 100644\n--- a/plone/app/caching/tests/test_operation_utils.py\n+++ b/plone/app/caching/tests/test_operation_utils.py\n@@ -967,7 +967,7 @@ def __init__(self, context):\n self.context = context\n \n def __call__(self):\n- return None\n+ return\n \n provideAdapter(DummyLastModified)\n \n@@ -1117,7 +1117,7 @@ def __init__(self, published, request):\n self.request = request\n \n def __call__(self):\n- return None\n+ return\n \n provideAdapter(BarETag, name="bar")\n \ndiff --git a/plone/app/caching/utils.py b/plone/app/caching/utils.py\nindex 2db4ded..483d201 100644\n--- a/plone/app/caching/utils.py\n+++ b/plone/app/caching/utils.py\n@@ -53,7 +53,7 @@ def getObjectDefaultView(context):\n pass\n \n if not IDynamicType.providedBy(context):\n- return None\n+ return\n \n fti = context.getTypeInfo()\n try:\n@@ -62,7 +62,7 @@ def getObjectDefaultView(context):\n action = fti.getActionInfo("object/view")["url"].split("/")[-1]\n except ValueError:\n # If the action doesn\'t exist, stop\n- return None\n+ return\n \n # Try resolving method aliases because we need a real template_id here\n if action:\n' - -Repository: plone.app.caching - - -Branch: refs/heads/master -Date: 2022-05-06T12:23:00+02:00 -Author: Jens W. Klein (jensens) -Commit: https://github.com/plone/plone.app.caching/commit/bc643a48c0730df067cf082551913302404ef2ea - -add news file +Fixes #101 Files changed: -A news/99.breaking -A news/99.bugfix +A news/101.bugfix +M plone/app/iterate/browser/checkout.py -b'diff --git a/news/99.breaking b/news/99.breaking\nnew file mode 100644\nindex 0000000..7d931ad\n--- /dev/null\n+++ b/news/99.breaking\n@@ -0,0 +1,3 @@\n+Remove unmaintained Split-View profile.\n+[jensens]\n+\ndiff --git a/news/99.bugfix b/news/99.bugfix\nnew file mode 100644\nindex 0000000..db6f369\n--- /dev/null\n+++ b/news/99.bugfix\n@@ -0,0 +1,2 @@\n+Minor code cleanup and review/overhaul docs.\n+[jensens]\n' +b'diff --git a/news/101.bugfix b/news/101.bugfix\nnew file mode 100644\nindex 0000000..e3f4fba\n--- /dev/null\n+++ b/news/101.bugfix\n@@ -0,0 +1 @@\n+Fix a typo in a a call to reindexObject\ndiff --git a/plone/app/iterate/browser/checkout.py b/plone/app/iterate/browser/checkout.py\nindex b38912a..73f10f8 100644\n--- a/plone/app/iterate/browser/checkout.py\n+++ b/plone/app/iterate/browser/checkout.py\n@@ -75,7 +75,7 @@ def __call__(self):\n \n # we do this for metadata update side affects which will update\n # lock info\n- context.reindexObject("review_state")\n+ context.reindexObject(idxs=["review_state"])\n \n IStatusMessage(self.request).addStatusMessage(\n _("Check-out created"), type="info"\n' -Repository: plone.app.caching +Repository: plone.app.iterate Branch: refs/heads/master -Date: 2022-05-07T16:59:54-03:00 -Author: Érico Andrei (ericof) -Commit: https://github.com/plone/plone.app.caching/commit/9843fd453691fad8e56e8facd278b231d7825f36 +Date: 2022-05-09T00:55:25+02:00 +Author: Gil Forcada Codinachs (gforcada) +Commit: https://github.com/plone/plone.app.iterate/commit/8e7e27de2a13bb2eed901bac1127eb065580db86 -Merge pull request #99 from plone/cleanup +Merge pull request #102 from plone/110.bugfix -Cleanup +Fix a typo in a a call to reindexObject Files changed: -A news/99.breaking -A news/99.bugfix -A plone/app/caching/profiles/default/registry/basesettings.xml -M docs/caching-control-panel.rst -M docs/caching-profiles.rst -M docs/caching-proxies.rst -M docs/composite-views.rst -M docs/etags.rst -M docs/ram-cache.rst -M docs/restapi.rst -M plone/app/caching/caching.zcml -M plone/app/caching/lastmodified.py -M plone/app/caching/lookup.py -M plone/app/caching/operations/default.py -M plone/app/caching/operations/etags.py -M plone/app/caching/operations/ramcache.py -M plone/app/caching/operations/utils.py -M plone/app/caching/purge.py -M plone/app/caching/tests/test_etags.py -M plone/app/caching/tests/test_lastmodified.py -M plone/app/caching/tests/test_operation_utils.py -M plone/app/caching/utils.py -D docs/split-views.rst -D plone/app/caching/profiles/default/registry.xml -D plone/app/caching/profiles/with-caching-proxy-splitviews/registry.xml +A news/101.bugfix +M plone/app/iterate/browser/checkout.py -b'diff --git a/docs/caching-control-panel.rst b/docs/caching-control-panel.rst\nindex 4b759f9..5eb1234 100644\n--- a/docs/caching-control-panel.rst\n+++ b/docs/caching-control-panel.rst\n@@ -1,33 +1,35 @@\n The caching control panel\n -------------------------\n \n-After installation, you will find a Caching control panel in Plone\'s site\n-setup. This consists of four main tabs:\n+After installation, you will find a Caching control panel in Plone\'s site setup.\n \n-* *Change settings*, where you can control caching behaviour\n+This consists of four main tabs:\n \n-* *Import settings*, where you can import pre-defined profiles of cache\n- settings\n+Change settings\n+ where you can control caching behaviour\n \n-* *Purge caching proxy*, where you can manually purge content from a caching\n- proxy. This tab only appears if you have purging enabled under\n- *Change settings*.\n+Import settings\n+ where you can import pre-defined profiles of cache settings\n \n-* *RAM cache*, where you can view statistics about and purge the RAM cache.\n+Purge caching proxy\n+ where you can manually purge content from a caching proxy.\n+ This tab only appears if you have purging enabled under *Change settings*.\n+\n+RAM cache\n+ where you can view statistics about and purge the RAM cache.\n \n Under the settings tab, you will find four fieldsets:\n \n-* *General settings*, for global options such as turning caching on or off.\n+General settings\n+ for global options such as turning caching on or off.\n \n-* *Caching proxies*, where you can control Plone\'s use of a caching proxy\n- such as Squid or Varnish.\n+Caching proxies\n+ where you can control Plone\'s use of a caching proxy such or Varnish or a CDN.\n \n-* *Caching operation mappings*, where caching rulesets (hints about views and\n- resources used for caching purposes) can be associated with caching\n- operations (which either intercept a request to return a cached response, or\n- modifies a response to add cache control headers). This is also where\n- rulesets for legacy page templates (created through the web or the\n- portal_skins tool) are configured.\n+Caching operation mappings\n+ where caching rulesets (hints about views and resources used for caching purposes) can be associated with caching operations.\n+ Those either intercept a request to return a cached response, or modifies a response to add cache control headers.\n+ This is also where rulesets for legacy page templates (created through the web or the portal_skins tool) are configured.\n \n-* *Detailed settings*, where you can configure parameters for individual\n- caching operations.\n+Detailed settings\n+ where you can configure parameters for individual caching operations.\ndiff --git a/docs/caching-profiles.rst b/docs/caching-profiles.rst\nindex f523cd1..6ff8803 100644\n--- a/docs/caching-profiles.rst\n+++ b/docs/caching-profiles.rst\n@@ -1,21 +1,16 @@\n Caching profiles\n ----------------\n \n-All persistent configuration for the caching machinery is stored in the\n-configuration registry, as managed by ``plone.app.registry``. This can be\n-modified using the ``registry.xml`` GenericSetup import step. The *Import\n-settings* tab of the control panel allows you to import these caching\n-profiles.\n+All persistent configuration for the caching machinery is stored in the configuration registry, as managed by ``plone.app.registry``.\n+This can be modified using the ``registry.xml`` GenericSetup import step.\n+The *Import settings* tab of the control panel allows you to import these caching profiles.\n \n \n Default caching profiles\n ~~~~~~~~~~~~~~~~~~~~~~~~\n \n-``plone.app.caching`` includes three default caching profiles. Two of these\n-profiles encapsulate the cache settings that are known to work well with a\n-typical default Plone installation. The third is an example profile for a\n-"split-view" caching setup (see the split-view discussion later in this\n-document).\n+``plone.app.caching`` includes three default caching profiles.\n+Two of these profiles encapsulate the cache settings that are known to work well with a typical default Plone installation.\n \n The three default caching profiles:\n \n@@ -28,11 +23,6 @@ The three default caching profiles:\n profile are some settings to enable proxy caching of files/images\n in content space and content feeds.\n \n-* **With caching proxy (and split-view caching)**\n- An example profile for a caching proxy setup with split-view\n- caching enabled. This example requires a special proxy setup.\n-\n-\n Custom caching profiles\n ~~~~~~~~~~~~~~~~~~~~~~~\n \ndiff --git a/docs/caching-proxies.rst b/docs/caching-proxies.rst\nindex f5955fc..46b974e 100644\n--- a/docs/caching-proxies.rst\n+++ b/docs/caching-proxies.rst\n@@ -1,80 +1,60 @@\n Caching proxies\n ---------------\n \n-It is common to place a so-called caching reverse proxy in front of Zope\n-when hosting large Plone sites. On Unix, a popular option is `Varnish`_,\n-although `Squid`_ is also a good choice. On Windows, you can use Squid\n-or the (commercial, but better) `Enfold Proxy`_.\n-\n-It is important to realise that whilst ``plone.app.caching`` provides\n-some functionality for controlling how Plone interacts with a caching\n-proxy, the proxy itself must be configured separately.\n-\n-Some operations in ``plone.app.caching`` can set response headers that\n-instruct the caching proxy how best to cache content. For example, it is\n-normally a good idea to cache static resources (such as images and\n-stylesheets) and "downloadables" (such as Plone content of the types ``File``\n-or ``Image``) in the proxy. This content will then be served to most users\n-straight from the proxy, which is much faster than Zope.\n-\n-The downside of this approach is that an old version of a content item may\n-returned to a user, because the cache has not been updated since the item\n-was modified. There are three general strategies for dealing with this:\n-\n-* Since resources are cached in the proxy based on their URL, you can\n- "invalidate" the cached copy by changing an item\'s URL when it is updated.\n- This is the approach taken by Plone\'s ResourceRegistries:\n- in production mode, the links that are inserted\n- into Plone\'s content pages for resource managed by ResourceRegistries\n- contain a time-based token, which changes when the ResourceRegistries\n- are updated, more specifically: when the resource bundles are combined.\n- This approach has the benefit of also being able to\n- "invalidate" content stored in a user\'s browser cache.\n-\n-* All caching proxies support setting timeouts. This means that content may\n- be stale, but typically only up to a few minutes. This is sometimes an\n- acceptable policy for high-volume sites where most users do not log in.\n-\n-* Most caching proxies support receiving PURGE requests for paths that\n- should be purged. For example, if the proxy has cached a resource at\n- ``/logo.jpg``, and that object is modified, a PURGE request could be sent\n- to the proxy (originating from Zope, not the client) with the same path to\n- force the proxy to fetch a new version the next time the item is requested.\n-\n-The final option, of course is to avoid caching content in the proxy\n-altogether. The default policies will not allow standard content pages to\n-be cached in the proxy, because it is too difficult to invalidate cached\n-instances. For example, if you change a content item\'s title, that may\n-require invalidation of a number of pages where that title appears in the\n-navigation tree, folder listings, ``Collections``, portlets, and so on.\n-Tracking all these dependencies and purging in an efficient manner is\n-impossible unless the caching proxy configuration is highly customised for\n-the site.\n+It is common to place a so-called caching reverse proxy in front of Zope when hosting large Plone sites.\n+If hosted by yourself, a popular option is `Varnish`_.\n+On the other hand there are commercial hosted CDN services like Cloudflare, CloudFront, Fastly and more.\n+\n+It is important to realise that whilst ``plone.app.caching`` provides some functionality for controlling how Plone interacts with a caching proxy.\n+The proxy itself must be configured separately.\n+\n+Some operations in ``plone.app.caching`` can set response headers that instruct the caching proxy how best to cache content.\n+For example, it is normally a good idea to cache static resources (such as images and stylesheets) and "downloadables" in the proxy.\n+This are i.e. Plone content of the types ``File`` or ``Image``.\n+This content will then be served to most users straight from the proxy, which is much faster than Zope.\n+\n+The downside of this approach is that an old version of a content item may returned to a user, because the cache has not been updated since the item was modified.\n+There are three general strategies for dealing with this:\n+\n+* 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.\n+ This is the approach taken by Plone\'s resource management:\n+ in production mode, the links that are inserted into Plone\'s content pages for resource managed by Plone contain a hash-based token, which changes when the main bundle file changes.\n+ This approach has the benefit of also being able to "invalidate" content stored in a user\'s browser cache.\n+\n+* All caching proxies support setting timeouts.\n+ This means that content may be stale, but typically only up to a few minutes.\n+ This is sometimes an acceptable policy for high-volume sites where most users do not log in.\n+\n+* Most caching proxies support receiving PURGE requests for paths that should be purged.\n+ Given the proxy has cached a resource at ``/logo.jpg``, and that object is modified.\n+ Then a PURGE request could be sent to the proxy (originating from Zope, not the client) with the same path to force the proxy to fetch a new version the next time the item is requested.\n+\n+The final option, of course is to avoid caching content in the proxy altogether.\n+The default policies will not allow standard listing pages to be cached in the proxy, because it is too difficult to invalidate cached instances.\n+For example, if you change a content item\'s title, that may require invalidation of a number of pages where that title appears in the navigation tree, folder listings, ``Collections``, portlets, and so on.\n+Tracking all these dependencies and purging in an efficient manner is impossible unless the caching proxy configuration is highly customised for the site.\n+\n+Nevertheless a "terse" policy allows caching for just a few seconds of all pages to reduce the load on the backend on high traffic situations.\n \n \n Purging a caching proxy\n ~~~~~~~~~~~~~~~~~~~~~~~\n \n Synchronous and asynchronous purging is enabled via `plone.cachepurging`_.\n-In the control panel, you can configure the use of a proxy via various\n-options, such as:\n+In the control panel, you can configure the use of a proxy via various options, such as:\n \n * Whether or not to enable purging globally.\n \n * The address of the caching server to which PURGE requests should be sent.\n \n-* Whether or not virtual host rewriting takes place before the caching proxy\n- receives a URL or not. This has implications for how the PURGE path is\n- constructed.\n+* Whether or not virtual host rewriting takes place before the caching proxy receives a URL or not.\n+ This has implications for how the PURGE path is constructed.\n \n-* Any domain aliases for your site, to enable correct purging of content\n- served via e.g. http://example.com and http://www.example.com.\n+* Any domain aliases for your site, to enable correct purging of content served via e.g. http://example.com and http://www.example.com.\n \n-The default purging policy is geared mainly towards purging file and image\n-resources, not content pages, although basic purging of content pages is\n-included. The actual paths to purge are constructed from a number of\n-components providing the ``IPurgePaths`` interface. See ``plone.cachepurging``\n-for details on how this works, especially if you need to write your own.\n+The default purging policy is geared mainly towards purging file and image resources, not content pages, although basic purging of content pages is included.\n+The actual paths to purge are constructed from a number of components providing the ``IPurgePaths`` interface.\n+See ``plone.cachepurging`` for details on how this works, especially if you need to write your own.\n \n The default purge paths include:\n \n@@ -86,56 +66,27 @@ The default purge paths include:\n \n * ${object_path}/${default-view} -- in case a default view template is used\n \n-* The download URLs for any Archetypes object fields, in the case of\n- Archetypes content. This includes support for the standard ``File`` and\n- ``Image`` types.\n+* The download URLs for any content object fields, given the type contains blobs.\n+This includes support for the standard ``File`` and ``Image`` types.\n \n-Files and images created (or customised) in the ZMI are purged automatically\n-when modified. Files managed through the ResourceRegistries do not need\n-purging, since they have "stable" URLs. To purge Plone content when modified\n-(or removed), you must select the content types in the control panel. By\n-default, only the ``File`` and ``Image`` types are purged.\n+Files and images created (or customised) in the ZMI are purged automatically when modified.\n+Bundles managed through the resource registration do not need purging, since they have "stable" URLs.\n+To purge Plone content when modified (or removed), you must select the content types in the control panel.\n+By default, only the ``File`` and ``Image`` types are purged.\n \n-You should not enable purging for types that are not likely to be cached in\n-the proxy. Although purging happens asynchronously at the end of the request,\n-it may still place unnecessary load on your server.\n+You should not enable purging for types that are not likely to be cached in the proxy.\n+Although purging happens asynchronously at the end of the request, it may still place unnecessary load on your server.\n \n-Finally, you can use the *Purge* tab in the control panel to manually purge\n-one or more URLs. This is a useful way to debug cache purging, as well as\n-a quick solution for the awkward situation where your boss walks in and\n-wonders why the "about us" page is still showing that old picture of him,\n-before he had a new haircut.\n+Finally, you can use the *Purge* tab in the control panel to manually purge one or more URLs.\n+This is a useful way to debug cache purging.\n+It offers as well as a quick solution for the awkward situation where your boss walks in and wonders why the "about us" page is still showing that old picture of him, before he had a new haircut.\n \n \n Installing and configuring a caching proxy\n ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n \n-There are buildout recipes for building and configuring proxy configs: `plone.recipe.squid`_ and `plone.recipe.varnish`_.\n-\n-\n-Running Plone behind Apache 2.2 with mod_cache\n-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n-\n-Apache 2.2 has a known bug around its handling of the HTTP response header\n-CacheControl with value max-age=0 or headers Expires with a date in the past.\n-In these scenarios mod_cache will not cache the response no matter what value\n-of s-maxage is set.\n-\n-https://issues.apache.org/bugzilla/show_bug.cgi?id=35247\n-\n-One possible workaround for this is to use mod_headers directives in your\n-Apache configuration to set max-age=1 if s-maxage is positive and max-age is 0\n-and also to drop the Expires header\n-\n-Header edit Cache-Control max-age=0(.*s-maxage=[1-9].*) max-age=1$1\n-Header unset Expires\n-\n-Dropping the Expires header has the disadvantage that HTTP 1.0 clients and\n-proxies may not cache your responses as you wish.\n+There is a buildout recipes for building and configuring proxy configs: `plone.recipe.varnish`_.\n \n .. _Varnish: http://varnish-cache.org\n-.. _Squid: http://squid-cache.org\n-.. _Enfold Proxy: http://enfoldsystems.com/software/proxy/\n-.. _plone.recipe.squid: http://pypi.python.org/pypi/plone.recipe.squid\n .. _plone.recipe.varnish: http://pypi.python.org/pypi/plone.recipe.varnish\n .. _plone.cachepurging: http://pypi.python.org/pypi/plone.cachepurging\ndiff --git a/docs/composite-views.rst b/docs/composite-views.rst\nindex c7c8a54..1035502 100644\n--- a/docs/composite-views.rst\n+++ b/docs/composite-views.rst\n@@ -1,29 +1,19 @@\n Composite views\n ---------------\n \n-A ``composite view`` is just a general term for most page views you see when\n-you visit a Plone site. It includes all content item views, content folder\n-views, and many template views. For our purposes, the distinguishing\n-characteristic of composite views is the difficulty inherent in keeping track\n-of all changes that might affect the final composited view. Because of the\n-difficulty of dependancy tracking, composite views are often notoriously\n-difficult to purge reliably from caching proxies so the default caching\n-profiles set headers which expire the cache immediately (i.e. *weak caching*).\n+A ``composite view`` is just a general term for most page views you see when you visit a Plone site.\n+It includes all content item views, content folder views, and many template views.\n+For our purposes, the distinguishing characteristic of composite views is the difficulty inherent in keeping track of all changes that might affect the final composited view.\n+Because of the difficulty of dependancy tracking, composite views are often notoriously difficult to purge reliably from caching proxies so the default caching profiles set headers which expire the cache immediately (i.e. *weak caching*).\n \n-However, most of the inline resources linked to from the composite view (css,\n-javascript, images, etc.) can be cached very well in proxy so the overall\n-speed of most composite views will always be better with a caching proxy in\n-front despite the page itself not being cached.\n-\n-Also, when using Squid as a caching proxy, we can still see some additional\n-speed improvement as Squid supports conditional requests to the backend and\n-304 responses from plone.app.caching are relatively quick. This means that\n-even though the proxy cache will expire immediately, Squid can revalidate its\n-cache relatively quickly. Varnish does not currently support conditional\n-requests to the backend.\n+However, most of the inline resources linked to from the composite view (css, javascript, images, etc.) can be cached very well in proxy so the overall speed of most composite views will always be better with a caching proxy in front despite the page itself not being cached.\n \n For relatively stable composite views or for those views for which you can\n tolerate some potential staleness, you might be tempted to try switching from\n *weak caching* to *moderate caching* with the ``s-maxage`` expiration\n value set to some tolerable value but first make sure you understand the\n issues regarding "split view" caching (see below).\n+\n+A way to speedup a site with lots of composite view is to use "Terse" caching.\n+It caches a page for just a small time (be default 10s in broser and 60s in the caching proxy).\n+Thus the content is almost up to date, but on high load visitors are getting pages served from the cache.\ndiff --git a/docs/etags.rst b/docs/etags.rst\nindex 3cdc796..dac1b29 100644\n--- a/docs/etags.rst\n+++ b/docs/etags.rst\n@@ -1,75 +1,65 @@\n ETags\n -----\n \n-ETags are used in to check whether pages need to be re-calculated or can be\n-served from cache. An ETag is simply a string. Under ``plone.app.caching``,\n-it is a string of tokens separated by pipe characters. The tokens hold values\n-such as a user id, the current skin name, or a counter indicating how many\n-objects have been added to the site. The idea is that the browser sends a\n-request with the ETag included in an ``If-None-Match`` header. Plone can then\n-quickly calculate the current ETag for the requested resource. If the ETag\n-is the same, Plone can reply with ``304 NOT MODIFIED`` response, telling the\n-browser to use its cached copy. Otherwise, Plone renders the page and returns\n-it as normal.\n-\n-Many caching operations use ETags. The tokens to include are typically\n-listed in an ``etags`` tuple in the operation\'s options.\n+ETags are used in to check whether pages need to be re-calculated or can be served from cache.\n+An ETag is simply a string. Under ``plone.app.caching``, it is a string of tokens separated by pipe characters.\n+The tokens hold values such as a user id, the current skin name, or a counter indicating how many objects have been added to the site.\n+The idea is that the browser sends a request with the ETag included in an ``If-None-Match`` header.\n+Plone can then quickly calculate the current ETag for the requested resource.\n+If the ETag is the same, Plone can reply with ``304 NOT MODIFIED`` response, telling the browser to use its cached copy.\n+Otherwise, Plone renders the page and returns it as normal.\n+\n+Many caching operations use ETags. The tokens to include are typically listed in an ``etags`` tuple in the operation\'s options.\n \n The ETag names tokens supported by default are:\n \n-* userid\n+userid\n The current user\'s id\n \n-* roles\n+roles\n A list of the current user\'s roles in the given context\n \n-* language\n+language\n The language(s) accepted by the browser, in the ``ACCEPT_LANGUAGE`` header\n \n-* userLanguage\n+userLanguage\n The current user\'s preferred language\n \n-* lastModified\n+lastModified\n A timestamp indicating the last-modified date of the given context\n \n-* catalogCounter\n- A counter that is incremented each time the catalog is updated, i.e. each\n- time content in the site is changed.\n+catalogCounter\n+ A counter that is incremented each time the catalog is updated.\n+ I.e. each time content in the site is changed.\n \n-* locked\n+locked\n Whether or not the given context is locked for editing.\n \n-* skin\n+skin\n The name of the current skin (theme)\n \n-* resourceRegistries\n- A timestamp indicating the last-modified timestamp for the\n- Resource Registries. This is useful for avoiding requests for expired\n- resources from cached pages.\n+resourceRegistries\n+ A timestamp indicating the last-modified timestamp for the Resource Registries.\n+ This is useful for avoiding requests for expired resources from cached pages.\n \n-It is possible to provide additional tokens by registering an ``IETagValue``\n-adapter. This should be a named adapter on the published object (typically a\n-view, file resource or Zope page template object) and request, with a unique\n-name. The name is used to look up the component. Thus, you can also override\n-one of the tokens above for a particular type of context or request (e.g. via\n-a browser layer), by registering a more specific adapter with the same name.\n+It is possible to provide additional tokens by registering an ``IETagValue`` adapter.\n+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 name.\n+The name is used to look up the component. Thus, you can also override one of the tokens above for a particular type of context or request (e.g. via a browser layer), by registering a more specific adapter with the same name.\n \n As an example, here is the ``language`` adapter::\n \n- from zope.interface import implements\n- from zope.interface import Interface\n-\n- from zope.component import adapts\n from plone.app.caching.interfaces import IETagValue\n+ from zope.component import adapter\n+ from zope.interface import implementer\n+ from zope.interface import Interface\n \n+ @implementer(IETagValue)\n+ @adapter(Interface, Interface)\n class Language(object):\n """The ``language`` etag component, returning the value of the\n HTTP_ACCEPT_LANGUAGE request key.\n """\n \n- implements(IETagValue)\n- adapts(Interface, Interface)\n-\n def __init__(self, published, request):\n self.published = published\n self.request = request\ndiff --git a/docs/ram-cache.rst b/docs/ram-cache.rst\nindex 1df0b5e..d9282a1 100644\n--- a/docs/ram-cache.rst\n+++ b/docs/ram-cache.rst\n@@ -1,64 +1,49 @@\n The RAM cache\n -------------\n \n-In addition to caching content in users\' browsers (through setting appropriate\n-response headers) and a caching proxy, Plone can cache certain information in\n-memory. This is done in two main ways:\n+In addition to caching content in users\' browsers (through setting appropriate response headers) and a caching proxy, Plone can cache certain information in memory.\n+This is done in two main ways:\n \n-* Developers may use the ``plone.memoize`` package\'s ``ram`` module to cache\n- the results of certain functions in RAM. For example, some viewlets and\n- portlets cache their rendered output in RAM for a time, alleviating the need\n- to calculate them every time.\n+* Developers may use the ``plone.memoize`` package\'s ``ram`` module to cache the results of certain functions in RAM.\n+ For example, some viewlets and portlets cache their rendered output in RAM for a time, alleviating the need to calculate them every time.\n \n-* Some caching operations may cache an entire response in memory, so that\n- they can later intercept the request to return a cached response..\n+* Some caching operations may cache an entire response in memory, so that they can later intercept the request to return a cached response.\n \n-Caching in RAM in Zope is not as efficient as caching in a proxy, for a number\n-of reasons:\n+Caching in RAM in Zope is not as efficient as caching in a proxy, for a number of reasons:\n \n-* Zope still has to perform traversal, security, transaction management and so\n- on before serving a request with a RAM-cached response.\n+* Zope still has to perform traversal, security, transaction management and so on before serving a request with a RAM-cached response.\n \n-* Zope\'s use of memory is not as efficient as that of a finely optimised\n- caching proxy.\n+* Zope\'s use of memory is not as efficient as that of a finely optimised caching proxy.\n \n-* Storing lots of content in RAM may compete with the standard ZODB object\n- cache and other memory pools used by Zope, thus slowing down Zope overall.\n+* Storing lots of content in RAM may compete with the standard ZODB object cache and other memory pools used by Zope, thus slowing down Zope overall.\n \n-* In multi-client ZEO setups, the RAM cache is (by default at least) not\n- shared among instances (though it is shared among threads in that instance).\n- Thus, each ZEO client process will maintain its own cache.\n+* In multi-client ZEO setups, the RAM cache is (by default at least) not shared among instances (though it is shared among threads in that instance).\n+ Thus, each Plone client process will maintain its own cache.\n \n-You can use the *RAM cache* tab in the caching control panel to view\n-statistics about the use of the RAM cache. On the *Change settings* tab, you\n-can also control the size of the cache, and the frequency with which it is\n-purged of old items.\n+You can use the *RAM cache* tab in the caching control panel to view statistics about the use of the RAM cache.\n+On the *Change settings* tab, you can also control the size of the cache, and the frequency with which it is purged of old items.\n \n \n Alternative RAM cache implementations\n ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n \n-The RAM cache exposed through ``plone.memoize.ram`` is looked up via an\n-``ICacheChoser`` utility. The default implementation looks up a\n-``zope.ramcache.interfaces.ram.IRAMCache`` utility. Plone installs a local\n-such utility (to allows its settings to be persisted - the cache itself is\n-not persistent), which is shared by all users of the cache.\n+The RAM cache exposed through ``plone.memoize.ram`` is looked up via an ``ICacheChoser`` utility.\n+The default implementation looks up a ``zope.ramcache.interfaces.ram.IRAMCache`` utility.\n+Plone installs a local such utility (to allows its settings to be persisted - the cache itself is not persistent), which is shared by all users of the cache.\n \n-You can provide your own ``ICacheChooser`` utility to change this policy,\n-by installing this as a local utility or overriding it in ``overrides.zcml``.\n-One reason to do this may be to back the cache with a `memcached`_ server,\n-which would allow a single cache to be shared among multiple Zope clients.\n+You can provide your own ``ICacheChooser`` utility to change this policy, by installing this as a local utility or overriding it in ``overrides.zcml``.\n+One reason to do this may be to back the cache with a `memcached`_ server, which would allow a single cache to be shared among multiple Zope clients.\n \n Below is a sketch of such a cache chooser, courtesy of Wojciech Lichota::\n \n- from threading import local\n- from pylibmc import Client\n- from zope.interface import implements\n from plone.memoize.interfaces import ICacheChooser\n from plone.memoize.ram import MemcacheAdapter\n+ from pylibmc import Client\n+ from threading import local\n+ from zope.interface import implementer\n \n+ @implementer(ICacheChooser)\n class MemcachedCacheChooser(object):\n- implements(ICacheChooser)\n _v_thread_local = local()\n \n def getClient(self):\ndiff --git a/docs/restapi.rst b/docs/restapi.rst\nindex 45544d4..9c0b211 100644\n--- a/docs/restapi.rst\n+++ b/docs/restapi.rst\n@@ -12,11 +12,12 @@ We have endpoints following classic `plone.content.itemView` content and do not\n Those can be handled with the default rules, including purge.\n \n There are others delivering dynamic content, like search, impossible to purge.\n-Those shall be cached using a shorttime cache (like some seconds to some minutes.\n+Those shall be cached using a shorttime cache (like some seconds to some minutes).\n \n-A rulesetType will be introduced called `plone.content.dynamic`.\n-It will is similar to be configured to cache by default in browser 15sec, in caching-proxy 60 seconds.\n-Its goal is primary to reduce the load/peak-load on the server. Also, it reduces the impact of loading the same endpoint more than one time.\n+This get covered by the rulesetType `plone.content.dynamic`.\n+It is configured to cache by default in browser 10sec, in caching-proxy 60 seconds.\n+Its goal is primary to reduce the load/peak-load on the server.\n+Also, it reduces the impact of loading the same endpoint more than one time in one page.\n \n \n plone.restapi GET endpoints\ndiff --git a/docs/split-views.rst b/docs/split-views.rst\ndeleted file mode 100644\nindex fe70321..0000000\n--- a/docs/split-views.rst\n+++ /dev/null\n@@ -1,32 +0,0 @@\n-Split views\n------------\n-\n-A non-zero expiration for proxy or browser caching of a composite view will\n-often require some special handling to deal with "split view" caching.\n-\n-Caching proxies and browsers keep track of cached entries by using the URL\n-as a key. If a Vary header is included in the response then those request\n-headers listed in Vary are also included in the cache key. In most cases,\n-this is sufficient to uniquely identify all responses. However, there are\n-exceptions. We call these exceptions "split views". Anytime you have\n-multiple views sharing the same cache key, you have a split view problem.\n-Split views cannot be cached in proxies or browsers without mixing up the\n-responses.\n-\n-In the Plone case, most composite views are also split views because they\n-provide different views to anonymous and authenticated requests.\n-In Plone, authenticated requests are tracked via cookies which are not\n-usually used in cache keys.\n-\n-One solution to this problem is to add a ``Vary:Cookie`` response header but,\n-unfortunately, since cookies are used for all sorts of state maintenance and\n-web tracking, this will usually result in very inefficient caching.\n-\n-Another solution is to enforce a different domain name, different path,\n-or different protocol (https/http) for authenticated versus anonymous\n-responses.\n-\n-Yet another solution involves intercepting the request and dynamically adding\n-a special ``X-Anonymous`` header to the anonymous request and then adding\n-``Vary:X-Anonymous`` to the split view response so that this header will added\n-to the cache key.\ndiff --git a/news/99.breaking b/news/99.breaking\nnew file mode 100644\nindex 0000000..7d931ad\n--- /dev/null\n+++ b/news/99.breaking\n@@ -0,0 +1,3 @@\n+Remove unmaintained Split-View profile.\n+[jensens]\n+\ndiff --git a/news/99.bugfix b/news/99.bugfix\nnew file mode 100644\nindex 0000000..db6f369\n--- /dev/null\n+++ b/news/99.bugfix\n@@ -0,0 +1,2 @@\n+Minor code cleanup and review/overhaul docs.\n+[jensens]\ndiff --git a/plone/app/caching/caching.zcml b/plone/app/caching/caching.zcml\nindex e081d57..adf9a34 100644\n--- a/plone/app/caching/caching.zcml\n+++ b/plone/app/caching/caching.zcml\n@@ -27,15 +27,6 @@\n directory="profiles/without-caching-proxy"\n />\n \n- \n-\n \n \ndiff --git a/plone/app/caching/lastmodified.py b/plone/app/caching/lastmodified.py\nindex b303565..f606dc0 100644\n--- a/plone/app/caching/lastmodified.py\n+++ b/plone/app/caching/lastmodified.py\n@@ -14,6 +14,7 @@\n \n \n try:\n+ # zope.dublincore is not partof Plone core, but might get used by addons\n from zope.dublincore.interfaces import IDCTimes\n except ImportError:\n \ndiff --git a/plone/app/caching/lookup.py b/plone/app/caching/lookup.py\nindex ff6cd76..1c2ae31 100644\n--- a/plone/app/caching/lookup.py\n+++ b/plone/app/caching/lookup.py\n@@ -62,14 +62,14 @@ def __call__(self):\n \n registry = queryUtility(IRegistry)\n if registry is None:\n- return None\n+ return\n \n ploneCacheSettings = registry.forInterface(IPloneCacheSettings, check=False)\n \n # 2. Get the name of the published object\n name = getattr(self.published, "__name__", None)\n if name is None:\n- return None\n+ return\n \n # 3. Look up the published name in the page template mapping\n ruleset = (\n@@ -82,12 +82,12 @@ def __call__(self):\n # 4. Find the parent of the published object\n parent = getattr(self.published, "__parent__", None)\n if parent is None:\n- return None\n+ return\n \n # 4.1. If the parent is a content object:\n parentPortalType = getattr(aq_base(parent), "portal_type", None)\n if parentPortalType is None:\n- return None\n+ return\n \n # 4.1.1. Get the default view of the parent content object\n defaultView = getObjectDefaultView(parent)\n@@ -95,7 +95,7 @@ def __call__(self):\n # 4.1.2. If the name of the published object is the same as the\n # default view of the parent:\n if defaultView != name:\n- return None\n+ return\n \n # 4.1.2.1. Look up the parent type in the content type\n # mapping\ndiff --git a/plone/app/caching/operations/default.py b/plone/app/caching/operations/default.py\nindex d3fbbbb..c129c03 100644\n--- a/plone/app/caching/operations/default.py\n+++ b/plone/app/caching/operations/default.py\n@@ -124,7 +124,7 @@ def interceptResponse(self, rulename, response, class_=None):\n \n # Check for cache stop request variables\n if cacheStop(self.request, rulename):\n- return None\n+ return\n \n # Check if this should be a 304 response\n if not isModified(self.request, etag=etag, lastModified=lastModified):\n@@ -152,8 +152,6 @@ def interceptResponse(self, rulename, response, class_=None):\n self.published, self.request, response, *cached\n )\n \n- return None\n-\n def modifyResponse(self, rulename, response, class_=None):\n options = lookupOptions(class_ or self.__class__, rulename)\n \n@@ -381,7 +379,7 @@ def __init__(self, published, request):\n self.request = request\n \n def interceptResponse(self, rulename, response):\n- return None\n+ return\n \n def modifyResponse(self, rulename, response):\n doNotCache(self.published, self.request, response)\ndiff --git a/plone/app/caching/operations/etags.py b/plone/app/caching/operations/etags.py\nindex 94987b0..cc504e2 100644\n--- a/plone/app/caching/operations/etags.py\n+++ b/plone/app/caching/operations/etags.py\n@@ -31,11 +31,11 @@ def __init__(self, published, request):\n def __call__(self):\n tool = queryUtility(IMembershipTool)\n if tool is None:\n- return None\n+ return\n \n member = tool.getAuthenticatedMember()\n if member is None:\n- return None\n+ return\n \n return member.getId()\n \n@@ -54,14 +54,14 @@ def __init__(self, published, request):\n def __call__(self):\n tool = queryUtility(IMembershipTool)\n if tool is None:\n- return None\n+ return\n \n if bool(tool.isAnonymousUser()):\n return "Anonymous"\n \n member = tool.getAuthenticatedMember()\n if member is None:\n- return None\n+ return\n \n return ";".join(sorted(member.getRolesInContext(getContext(self.published))))\n \n@@ -104,7 +104,7 @@ def __call__(self):\n (context, self.request), name="plone_portal_state"\n )\n if portal_state is None:\n- return None\n+ return\n return portal_state.default_language()\n \n \n@@ -122,7 +122,7 @@ def __init__(self, published, request):\n def __call__(self):\n lastModified = getLastModifiedAnnotation(self.published, self.request)\n if lastModified is None:\n- return None\n+ return\n return str(time.mktime(lastModified.utctimetuple()))\n \n \n@@ -140,7 +140,7 @@ def __init__(self, published, request):\n def __call__(self):\n catalog = queryUtility(ICatalogTool)\n if catalog is None:\n- return None\n+ return\n return str(catalog.getCounter())\n \n \n@@ -161,7 +161,7 @@ def __call__(self):\n (context, self.request), name="plone_context_state"\n )\n if context_state is None:\n- return None\n+ return\n return "1" if context_state.is_locked() else "0"\n \n \n@@ -179,7 +179,7 @@ def __call__(self):\n \n portal_skins = getToolByName(context, "portal_skins", None)\n if portal_skins is None:\n- return None\n+ return\n \n requestVariable = portal_skins.getRequestVarname()\n if requestVariable in self.request:\n@@ -192,7 +192,7 @@ def __call__(self):\n @adapter(Interface, Interface)\n class AnonymousOrRandom:\n """The ``anonymousOrRandom`` etag component. This is normally added\n- implicitly by the ``anonOnly`` setting. It will return None for anonymous\n+ implicitly by the ``anonOnly`` setting. It will return for anonymous\n users, but a random number for logged-in ones. The idea is to force a\n re-fetch of a page every time for logged-in users.\n """\n@@ -204,9 +204,9 @@ def __init__(self, published, request):\n def __call__(self):\n tool = queryUtility(IMembershipTool)\n if tool is None:\n- return None\n+ return\n if bool(tool.isAnonymousUser()):\n- return None\n+ return\n return f"{time.time()}{random.randint(0, 1000)}"\n \n \ndiff --git a/plone/app/caching/operations/ramcache.py b/plone/app/caching/operations/ramcache.py\nindex 8c4657e..be59cb2 100644\n--- a/plone/app/caching/operations/ramcache.py\n+++ b/plone/app/caching/operations/ramcache.py\n@@ -30,12 +30,10 @@ def transformUnicode(self, result, encoding):\n storeResponseInRAMCache(\n self.request, self.request.response, result.encode(encoding)\n )\n- return None\n \n def transformBytes(self, result, encoding):\n if self.responseIsSuccess() and IRAMCached.providedBy(self.request):\n storeResponseInRAMCache(self.request, self.request.response, result)\n- return None\n \n def transformIterable(self, result, encoding):\n if self.responseIsSuccess() and IRAMCached.providedBy(self.request):\n@@ -43,8 +41,6 @@ def transformIterable(self, result, encoding):\n storeResponseInRAMCache(self.request, self.request.response, result)\n # ITransform contract allows to return an "encoded string" aka bytes\n return result\n- return None\n \n def responseIsSuccess(self):\n- status = self.request.response.getStatus()\n- return status == 200\n+ return self.request.response.getStatus() == 200\ndiff --git a/plone/app/caching/operations/utils.py b/plone/app/caching/operations/utils.py\nindex 274233b..2d8327f 100644\n--- a/plone/app/caching/operations/utils.py\n+++ b/plone/app/caching/operations/utils.py\n@@ -251,7 +251,7 @@ def cacheInRAM(\n \n annotations = IAnnotations(request, None)\n if annotations is None:\n- return None\n+ return\n \n key = getRAMCacheKey(request, etag=etag, lastModified=lastModified)\n \n@@ -479,7 +479,7 @@ def checkType(context):\n published = published.__parent__\n \n if not checkType(published):\n- return None\n+ return\n \n return published\n \n@@ -508,10 +508,10 @@ def parseDateTime(str):\n try:\n dt = dateutil.parser.parse(str)\n except ValueError:\n- return None\n+ return\n \n if not dt:\n- return None\n+ return\n \n if dt.tzinfo is None:\n dt = datetime.datetime(\n@@ -534,7 +534,7 @@ def getLastModifiedAnnotation(published, request, lastModified=True):\n """\n \n if not lastModified:\n- return None\n+ return\n \n annotations = IAnnotations(request, None)\n if annotations is not None:\n@@ -558,15 +558,15 @@ def getLastModified(published, lastModified=True):\n """\n \n if not lastModified:\n- return None\n+ return\n \n lastModified = ILastModified(published, None)\n if lastModified is None:\n- return None\n+ return\n \n dt = lastModified()\n if dt is None:\n- return None\n+ return\n \n if dt.tzinfo is None:\n dt = datetime.datetime(\n@@ -593,8 +593,7 @@ def getExpiration(maxage):\n now = datetime.datetime.now()\n if maxage > 0:\n return now + datetime.timedelta(seconds=maxage)\n- else:\n- return now - datetime.timedelta(days=3650)\n+ return now - datetime.timedelta(days=3650)\n \n \n def getETagAnnotation(published, request, keys=(), extraTokens=()):\n@@ -603,7 +602,7 @@ def getETagAnnotation(published, request, keys=(), extraTokens=()):\n """\n \n if not keys and not extraTokens:\n- return None\n+ return\n \n annotations = IAnnotations(request, None)\n if annotations is not None:\n@@ -632,7 +631,7 @@ def getETag(published, request, keys=(), extraTokens=()):\n All tokens will be concatenated into an ETag string, separated by pipes.\n """\n if not keys and not extraTokens:\n- return None\n+ return\n \n tokens = []\n noTokens = True\n@@ -654,7 +653,7 @@ def getETag(published, request, keys=(), extraTokens=()):\n tokens.append(token)\n \n if noTokens:\n- return None\n+ return\n \n etag = "|" + "|".join(tokens)\n etag = etag.replace(",", ";") # commas are bad in etags\n@@ -727,10 +726,8 @@ def getRAMCache(globalKey=PAGE_CACHE_KEY):\n """\n \n chooser = queryUtility(ICacheChooser)\n- if chooser is None:\n- return None\n-\n- return chooser(globalKey)\n+ if chooser is not None:\n+ return chooser(globalKey)\n \n \n def getRAMCacheKey(request, etag=None, lastModified=None):\n@@ -835,10 +832,10 @@ def fetchFromRAMCache(\n \n cache = getRAMCache(globalKey)\n if cache is None:\n- return None\n+ return\n \n key = getRAMCacheKey(request, etag=etag, lastModified=lastModified)\n if key is None:\n- return None\n+ return\n \n return cache.get(key, default)\ndiff --git a/plone/app/caching/profiles/default/registry.xml b/plone/app/caching/profiles/default/registry/basesettings.xml\nsimilarity index 100%\nrename from plone/app/caching/profiles/default/registry.xml\nrename to plone/app/caching/profiles/default/registry/basesettings.xml\ndiff --git a/plone/app/caching/profiles/with-caching-proxy-splitviews/registry.xml b/plone/app/caching/profiles/with-caching-proxy-splitviews/registry.xml\ndeleted file mode 100644\nindex 718d54e..0000000\n--- a/plone/app/caching/profiles/with-caching-proxy-splitviews/registry.xml\n+++ /dev/null\n@@ -1,156 +0,0 @@\n-\n-\n- \n- \n- plone.app.caching.strongCaching\n- plone.app.caching.strongCaching\n- plone.app.caching.moderateCaching\n- plone.app.caching.moderateCaching\n- plone.app.caching.moderateCaching\n- plone.app.caching.moderateCaching\n- \n- \n-\n-\n- \n- \n- \n- \n- userid\n- catalogCounter\n- userLanguage\n- skin\n- locked\n- resourceRegistries\n- \n- \n- \n- \n- True\n- \n- \n- \n- 3600\n- \n- \n- \n- X-Anonymous\n- \n-\n-\n-\n- \n- \n- \n- \n- userid\n- catalogCounter\n- userLanguage\n- skin\n- locked\n- copy\n- resourceRegistries\n- \n- \n- \n- \n- True\n- \n- \n- \n- 3600\n- \n- \n- \n- X-Anonymous\n- \n-\n-\n- \n- \n- \n- \n- userid\n- catalogCounter\n- userLanguage\n- skin\n- locked\n- copy\n- \n- \n- \n- \n- True\n- \n- \n- \n- Accept\n- \n- \n- \n- 0\n- \n- \n- \n- 0\n- \n- \n- \n- X-Anonymous, Accept\n- \n-\n-\n- \n- \n- \n- 86400\n- \n- \n- \n- \n- userid\n- catalogCounter\n- userLanguage\n- skin\n- \n- \n- \n- \n- True\n- \n-\n-\n- \n- \n- \n- 86400\n- \n- \n- \n- True\n- \n-\n-\n- \n- \n- \n- 86400\n- \n- \n- \n- True\n- \n-\n-\n- \n- \n- \n- 31536000\n- \n- \n- \n- True\n- \n-\n-\n-\ndiff --git a/plone/app/caching/purge.py b/plone/app/caching/purge.py\nindex 4162b4a..850aeb0 100644\n--- a/plone/app/caching/purge.py\n+++ b/plone/app/caching/purge.py\n@@ -163,11 +163,11 @@ def _getRoot(self):\n \n plone_utils = getToolByName(self.context, "plone_utils", None)\n if plone_utils is None:\n- return None\n+ return\n \n thread = plone_utils.getDiscussionThread(self.context)\n if not thread:\n- return None\n+ return\n \n return thread[0]\n \ndiff --git a/plone/app/caching/tests/test_etags.py b/plone/app/caching/tests/test_etags.py\nindex 81a6f2e..986b4b0 100644\n--- a/plone/app/caching/tests/test_etags.py\n+++ b/plone/app/caching/tests/test_etags.py\n@@ -49,7 +49,7 @@ def __init__(self):\n pass\n \n def getAuthenticatedMember(self):\n- return None\n+ return\n \n provideUtility(DummyMembershipTool())\n \n@@ -101,7 +101,7 @@ def __init__(self):\n pass\n \n def getAuthenticatedMember(self):\n- return None\n+ return\n \n def isAnonymousUser(self):\n return True\n@@ -215,7 +215,7 @@ def __init__(self, context):\n self.context = context\n \n def __call__(self):\n- return None\n+ return\n \n provideAdapter(DummyLastModified)\n \ndiff --git a/plone/app/caching/tests/test_lastmodified.py b/plone/app/caching/tests/test_lastmodified.py\nindex 6af6a30..bcd4f29 100644\n--- a/plone/app/caching/tests/test_lastmodified.py\n+++ b/plone/app/caching/tests/test_lastmodified.py\n@@ -154,7 +154,7 @@ class Dummy:\n def modified(self):\n if self._mod is not None:\n return DateTime.DateTime(self._mod)\n- return None\n+ return\n \n d = Dummy()\n \ndiff --git a/plone/app/caching/tests/test_operation_utils.py b/plone/app/caching/tests/test_operation_utils.py\nindex 1f5dffb..4a60ee1 100644\n--- a/plone/app/caching/tests/test_operation_utils.py\n+++ b/plone/app/caching/tests/test_operation_utils.py\n@@ -967,7 +967,7 @@ def __init__(self, context):\n self.context = context\n \n def __call__(self):\n- return None\n+ return\n \n provideAdapter(DummyLastModified)\n \n@@ -1117,7 +1117,7 @@ def __init__(self, published, request):\n self.request = request\n \n def __call__(self):\n- return None\n+ return\n \n provideAdapter(BarETag, name="bar")\n \ndiff --git a/plone/app/caching/utils.py b/plone/app/caching/utils.py\nindex 2db4ded..483d201 100644\n--- a/plone/app/caching/utils.py\n+++ b/plone/app/caching/utils.py\n@@ -53,7 +53,7 @@ def getObjectDefaultView(context):\n pass\n \n if not IDynamicType.providedBy(context):\n- return None\n+ return\n \n fti = context.getTypeInfo()\n try:\n@@ -62,7 +62,7 @@ def getObjectDefaultView(context):\n action = fti.getActionInfo("object/view")["url"].split("/")[-1]\n except ValueError:\n # If the action doesn\'t exist, stop\n- return None\n+ return\n \n # Try resolving method aliases because we need a real template_id here\n if action:\n' +b'diff --git a/news/101.bugfix b/news/101.bugfix\nnew file mode 100644\nindex 0000000..e3f4fba\n--- /dev/null\n+++ b/news/101.bugfix\n@@ -0,0 +1 @@\n+Fix a typo in a a call to reindexObject\ndiff --git a/plone/app/iterate/browser/checkout.py b/plone/app/iterate/browser/checkout.py\nindex b38912a..73f10f8 100644\n--- a/plone/app/iterate/browser/checkout.py\n+++ b/plone/app/iterate/browser/checkout.py\n@@ -75,7 +75,7 @@ def __call__(self):\n \n # we do this for metadata update side affects which will update\n # lock info\n- context.reindexObject("review_state")\n+ context.reindexObject(idxs=["review_state"])\n \n IStatusMessage(self.request).addStatusMessage(\n _("Check-out created"), type="info"\n'