Plone 6.0.5 released

Release notes for Plone 6.0.5

If you want to jump straight in, here are two important links:


Major changes since 6.0.4:

  • Zope: Do not break on requests that pass both a query string and a Content-Type header or request body.
  • Products.CMFPlone: Do not truncate the sortable_title index.
  • Update the resourceRegistries ETag to use the config registry modification time. This time is set since Plone 6.0.4.
  • Content types control panel: Show behavior name and interface.
    • Merge utils and base classes from and do not depend on it anymore.
    • Remove invalid unicode control characters for TextareaWidget.
  • plone.recipe.zeoserver: Fix lost dependencies when defining additional eggs in buildout part.
  • plone.restapi:
    • Create relations service. Query, add, delete.
    • Add portal_type title (type_title) to content response.
    • Added support for nested schemas with resolveuid deserializer.
  • plone.staticresources: Update mockup from 5.0.10 to 5.0.12:
    • pat markspeciallinks: Fix selector for tests.
    • pat recurrence: Fix default selected range option if there's no "repeat forever" button (which is default in the event behavior).
    • pat textareamimetypeselector: Async initialization of textareas. Fixes TinyMCE in modals not showing up (see Mosaic).
    • pat tinymce: Add urlconverter_callback and do not convert external links/images urls
    • pat tinymce: Do not remove current paragraph when inserting image.
  • Several packages: Fix cyclic dependencies, drop Python 2 or Plone 5.2 support.

Volto frontend

The default frontend for new Plone 6 sites is Volto. Latest release is 16.20.7. See the changelog.
Note that this is a JavaScript frontend that you need to run in a separate process with NodeJS.

Also, existing Plone sites need some or more extensive changes to be upgraded before they can use the Volto Frontend. Please read the guide on migrating from Plone Classic UI to Volto.

Classic UI

The HTML based and server side rendered UI that was present in Plone 5.2 and earlier major Plone releases is still available and has also been updated and improved upon in Plone 6. Our documentation now refers to this frontend as 'Classic UI'. Support for Classic UI is especially relevant for existing Plone sites which for whatever reason or requirements are not yet ready to be upgraded to the Volto frontend.

Python compatibility

This release supports Python 3.8, 3.9, 3.10, and 3.11.

pip, buildout, setuptools

In Plone core we use these versions to install Plone:


In general you are free to use whatever versions work for you, but these worked for us.

Note that setuptools 66 or higher is more strict with what versions it can recognize. If you run pip or buildout and it suddenly cannot find a package with a non-standard version, then this may be the cause.


For installation instructions, see the documentation.


If you find any issues, please report them in the main issue tracker.


If you want to it try out with classic UI, here is a simple setup based on PDM.
Also included is an example with ZODB RelStorage.


Does PDM work with version constraints?

I've not been able to find an equivalent to pip's -c constraints.txt.

they don't directly support it, because thay way it works is more like a package.json and the lock file. but I'm not a big fan of our huge constraints file anyway. it's hard to maintain and contains oudated sometimes pinnings because of that.

packages should declare there dependencies right and let tools like PDM or Poetry do there job.

But in PDM you can even force versions when the dependy resolver can not solve it. Or normally you can pin versions or ranges of versions like in nodejs.

I aggre.

... I miss in Python something like the Dependency Management of Maven or Gradle with an unique repository for all projects and every project/package declaring its dependencies in its pom.xml or build.gradle file.

Well, I oppose here. For small projects with a small and controlled number of packages this may work, for Plone it will end up in a dependency hell. I.e. once you want to use a newer version of a package than dependency pins in the other package allow in its range.

I am a huge fan of a controlled constraints file here, as it gives the security to get exactly whats you expect to get.

As far as i understand, there is nothing you can do in a constraints file, you couldn't do in a pyprpject.toml from the central point of view. But if single pavkages wouldn't tell, if they need a certain version of a library to function, you would need all this information in the central pyproject.toml which is harder to manage. you cal always force it there, but only the single package knows best, when they are not compatible with another library version. It's the job of tools like PDM to calculate every possible variantion and find the best working combination.

You can declare that in the case the package "xyz" should be installed it will be installed in e.g. version "1.2". But simply declaring the package "xyz" in a constraints file won't install the package. The package must explicitly be installed.

Can something like that be done with PDM?

As long as you declare (in pyproject.toml) the minimum version number it is fine, declaring the maximum or a specific version number is something I consider as harmful.

i guess, if a package in a specific version does not work with a newer version of a library it is good to havr this reflected in th dependencies. but that package will most likely have a nwer version which does not habe this constraint. So PDM would choose the newer version of that package. For me there is not point in pulling in the not working dependency. if there is no newer version of that package then there is no solution, except creating one or get rid of that package. either way i want to know it.

@mekell i don't know, if that would be possible

Central constraints -> exact versions installed
Package constraints -> "should be compatible" versions installed

In the second case, a central constraints file can be created but it is not guaranteed that the next time it will be the same.

After a year, it is quite possible that you cannot install with the second method because dependencies will pull in too newer packages. Plone has upgrades, so versions. Yes, you can freeze packages versions to a file and create a constraints.txt suitable to reinstall the same env (or quite the same).

This is correct only under the premisse that such tools have access to the necessary information/version.

PDM will try to get the newest version of a package unless stated otherwise.

If you run PDM today with Products.CMFPLone==6.0.4 it will install plone.api==2.0.3, etc.

Although the constraints.txt versions for 6.0.4 are plone.api==2.0.2, etc.

Run PDM with Products.CMFPLone==6.0.4 in a couple of weeks/months and you'll probably get newer versions.

PDM doesn't know whether those new versions are compatible with Products.CMFPLone==6.0.4. As soon as we get a non-backwards compatible version we get a problem.

Most of the plone.* packages do not declare versions. Probably because they rely on versions declared in the corresponding constraints.txt file.

For using PDM without getting the newest versions all single packages should declare versions (or version ranges) which of course would be best practice.

Without specifying versions it will be very difficult for every algorithm to construct a good dependency graph. Until then I think the best workaround is to have a "constraints" file.

The following list shows how many requirements have specs of the requirements declared by a given distribution with "plone" in its project_name for a Plone-6.0.5 installation.

12 of 75 Products.CMFPlone 6.0.5
 2 of 16 Products.PlonePAS 8.0.1
 0 of  3 plone.alterego 2.0.0
 1 of 16 plone.api 2.0.3
 0 of 43 3.1.1
 1 of 13 4.0.1
 0 of 12 3.0.3
 2 of 12 3.0.2
 0 of 19 5.0.2
 9 of 43 3.0.3
 0 of 14 2.0.0
11 of 44 3.1.1
 1 of 32 4.0.1
 7 of 38 5.0.1
 0 of  6 4.0.1
 1 of  8 2.0.0
 1 of 28 5.0.2
 3 of 26 4.0.5
 0 of 18 4.0.1
 0 of  1 6.0.14
 2 of  6 1.0.7
 1 of 45 7.0.1
 0 of 33 5.0.5
 1 of 17 2.0.4
 1 of 12 3.0.1
 3 of 14 2.0.2
 3 of 31 3.0.2
 0 of  9 2.0.0
 5 of 23 5.0.3
 2 of 20 3.0.5
 3 of 35 3.0.2
 0 of 10 2.2.2
 2 of 23 2.0.1
 0 of 11 4.0.2
 0 of 15 5.0.3
 4 of 11 4.0.1
 0 of 13 5.0.2
 2 of 35 4.2.0
 2 of 11 plone.autoform 2.0.1
 0 of  2 plone.autoinclude 1.0.1
 0 of  9 plone.base 1.1.3
 0 of  7 plone.batching 2.0.5
 0 of  7 plone.behavior 2.0.0
 2 of 11 plone.browserlayer 3.0.1
 0 of 13 plone.cachepurging 3.0.1
 0 of  9 plone.caching 2.0.0
 0 of  3 plone.contentrules 3.0.0
 6 of 45 plone.dexterity 3.0.2
 0 of  7 plone.event 2.0.0
 0 of  5 plone.folder 4.0.0
 1 of  6 plone.formwidget.namedfile 3.0.2
 0 of 13 plone.formwidget.recurrence 3.0.3
 1 of 12 plone.i18n 5.0.1
 0 of  2 plone.indexer 2.0.0
 0 of  1 plone.intelligenttext 4.0.0
 0 of  5 plone.keyring 4.0.0
 0 of  6 plone.locking 3.0.0
 0 of  9 plone.memoize 3.0.1
 3 of 18 plone.namedfile 6.1.0
 2 of 17 plone.outputfilters 5.0.2
 0 of 19 plone.portlet.collection 4.0.2
 0 of 11 plone.portlet.static 4.0.1
 0 of 18 plone.portlets 3.0.0
 0 of 14 plone.protect 5.0.0
 0 of  8 plone.registry 2.0.0
 0 of 10 plone.resource 3.0.0
 0 of  7 plone.resourceeditor 4.0.0
 0 of 11 plone.rfc822 3.0.0
 0 of 14 plone.scale 4.0.1
 0 of  6 plone.schema 2.0.0
 0 of 32 plone.schemaeditor 4.0.3
 0 of  8 plone.session 4.0.3
 0 of  5 plone.staticresources 2.0.11
 0 of  8 plone.stringinterp 2.0.1
 0 of 12 plone.subrequest 2.0.3
 1 of 12 plone.supermodel 2.0.3
 0 of  5 plone.theme 4.0.0
 0 of  3 plone.transformchain 3.0.0
 0 of  9 plone.uuid 2.0.0
 1 of  9 plone.z3cform 2.0.1
 1 of  3 plonetheme.barceloneta 3.0.2

Done with the following code:

from importlib.metadata import requires
import pkg_resources
from pkg_resources import Requirement

for project_name, version in sorted(
            distribution.project_name: distribution.version
            for distribution
            in pkg_resources.working_set
    if "plone" in project_name.lower():
        requirements = requires(project_name)
        requirements_with_spec = len(
                for requirement
                in requirements
                if Requirement.parse(requirement).specs
        print(f"{requirements_with_spec: >2} of {len(requirements): >2} {project_name} {version}")
        # print(requirements)