From 7d30b3b5999cd0d691a6a95ac4bb836da39b148d Mon Sep 17 00:00:00 2001 From: mauritsvanrees Date: Mon, 2 Sep 2024 16:35:17 +0200 Subject: [PATCH] [fc] Repository: plone.app.upgrade Branch: refs/heads/master Date: 2024-08-13T18:51:41+02:00 Author: Maurits van Rees (mauritsvanrees) Commit: https://github.com/plone/plone.app.upgrade/commit/100cf0e4c061c8c22130f06e523150f39a2f9563 6.1: Cleanup plone.app.discussion settings when the package is not available. If the site contains comments, we throw an error and stop the upgrade. The advice then is to add the `plone.app.discussion` package. Files changed: A news/211.bugfix A plone/app/upgrade/v61/profiles/uninstall-discussion/actions.xml A plone/app/upgrade/v61/profiles/uninstall-discussion/browserlayer.xml A plone/app/upgrade/v61/profiles/uninstall-discussion/catalog.xml A plone/app/upgrade/v61/profiles/uninstall-discussion/controlpanel.xml A plone/app/upgrade/v61/profiles/uninstall-discussion/registry.xml A plone/app/upgrade/v61/profiles/uninstall-discussion/types.xml A plone/app/upgrade/v61/profiles/uninstall-discussion/workflows.xml M plone/app/upgrade/v61/configure.zcml M plone/app/upgrade/v61/final.py M plone/app/upgrade/v61/profiles.zcml Repository: plone.app.upgrade Branch: refs/heads/master Date: 2024-09-02T16:35:17+02:00 Author: Maurits van Rees (mauritsvanrees) Commit: https://github.com/plone/plone.app.upgrade/commit/97c18165d6a88e813cf1682db1f8b3c2a7d3c4a2 Merge pull request #330 from plone/pa-discussion-core-addon 6.1: Cleanup plone.app.discussion settings when package is missing Files changed: A news/211.bugfix A plone/app/upgrade/v61/profiles/uninstall-discussion/actions.xml A plone/app/upgrade/v61/profiles/uninstall-discussion/browserlayer.xml A plone/app/upgrade/v61/profiles/uninstall-discussion/catalog.xml A plone/app/upgrade/v61/profiles/uninstall-discussion/controlpanel.xml A plone/app/upgrade/v61/profiles/uninstall-discussion/registry.xml A plone/app/upgrade/v61/profiles/uninstall-discussion/types.xml A plone/app/upgrade/v61/profiles/uninstall-discussion/workflows.xml M plone/app/upgrade/v61/configure.zcml M plone/app/upgrade/v61/final.py M plone/app/upgrade/v61/profiles.zcml --- last_commit.txt | 217 +++++++++--------------------------------------- 1 file changed, 37 insertions(+), 180 deletions(-) diff --git a/last_commit.txt b/last_commit.txt index 4e2c2bcf5e..b9f7a9654a 100644 --- a/last_commit.txt +++ b/last_commit.txt @@ -1,198 +1,55 @@ -Repository: plone.app.dexterity +Repository: plone.app.upgrade Branch: refs/heads/master -Date: 2023-06-06T18:14:52+02:00 -Author: Jens W. Klein (jensens) -Commit: https://github.com/plone/plone.app.dexterity/commit/bb8f2c8a892f00364006e8ffef95f98a417ab981 - -move plone.allowdiscussion behavior to plone.app.discussion - -Files changed: -M plone/app/dexterity/behaviors/configure.zcml -M plone/app/dexterity/behaviors/discussion.py - -b'diff --git a/plone/app/dexterity/behaviors/configure.zcml b/plone/app/dexterity/behaviors/configure.zcml\nindex c28738fe..a105dbf7 100644\n--- a/plone/app/dexterity/behaviors/configure.zcml\n+++ b/plone/app/dexterity/behaviors/configure.zcml\n@@ -105,14 +105,6 @@\n provides=".nextprevious.INextPreviousToggle"\n />\n \n- \n- \n-\n \n \n -Commit: https://github.com/plone/plone.app.dexterity/commit/3778d627d8ea5b75843a0db06cff168a51b574a7 - -Update plone/app/dexterity/behaviors/discussion.py - -Co-authored-by: David Glick <david@glicksoftware.com> - -Files changed: -M plone/app/dexterity/behaviors/discussion.py - -b'diff --git a/plone/app/dexterity/behaviors/discussion.py b/plone/app/dexterity/behaviors/discussion.py\nindex a2dd36ce..2c059cd5 100644\n--- a/plone/app/dexterity/behaviors/discussion.py\n+++ b/plone/app/dexterity/behaviors/discussion.py\n@@ -1,6 +1,6 @@\n from zope.deferredimport import deprecated\n \n deprecated(\n- "IAllowDiscussion is here deprecated. Import from plone.app.z3cform.widgets.select instead (will be removed in Plone 7)"\n+ "IAllowDiscussion is deprecated here. Import from plone.app.discussion.behaviors instead (will be removed in Plone 7)"\n IAllowDiscussion="plone.app.discussion.behaviors:IAllowDiscussion",\n )\n' - -Repository: plone.app.dexterity - - -Branch: refs/heads/master -Date: 2023-07-13T14:55:09+02:00 -Author: Jens W. Klein (jensens) -Commit: https://github.com/plone/plone.app.dexterity/commit/bed36a6aeffbf90594613c8b35cfdccd2472201c - -Merge branch 'master' into pa-discussion-coreaddon - -Files changed: -A .flake8 -A .github/workflows/meta.yml -A news/55bda5c9.internal -M .editorconfig -M .gitignore -M .meta.toml -M .pre-commit-config.yaml -M pyproject.toml -M setup.py -M tox.ini -D setup.cfg - -b'diff --git a/.editorconfig b/.editorconfig\nindex b4158b89..919b4116 100644\n--- a/.editorconfig\n+++ b/.editorconfig\n@@ -1,5 +1,6 @@\n # Generated from:\n # https://github.com/plone/meta/tree/master/config/default\n+# See the inline comments on how to expand/tweak this configuration file\n #\n # EditorConfig Configuration file, for more details see:\n # http://EditorConfig.org\n@@ -32,8 +33,21 @@ indent_size = 4\n # 2 space indentation\n indent_size = 2\n \n+[*.{json,jsonl,js,jsx,ts,tsx,css,less,scss,html}] # Frontend development\n+# 2 space indentation\n+indent_size = 2\n+\n [{Makefile,.gitmodules}]\n # Tab indentation (no size specified, but view as 4 spaces)\n indent_style = tab\n indent_size = unset\n tab_width = unset\n+\n+\n+##\n+# Add extra configuration options in .meta.toml:\n+# [editorconfig]\n+# extra_lines = """\n+# _your own configuration lines_\n+# """\n+##\ndiff --git a/setup.cfg b/.flake8\nsimilarity index 66%\nrename from setup.cfg\nrename to .flake8\nindex e85fb4a6..32538e5c 100644\n--- a/setup.cfg\n+++ b/.flake8\n@@ -1,8 +1,6 @@\n # Generated from:\n # https://github.com/plone/meta/tree/master/config/default\n-[bdist_wheel]\n-universal = 0\n-\n+# See the inline comments on how to expand/tweak this configuration file\n [flake8]\n doctests = 1\n ignore =\n@@ -17,9 +15,10 @@ ignore =\n per-file-ignores =\n plone/app/dexterity/textindexer/__init__.py:F401\n \n-[check-manifest]\n-ignore =\n- .editorconfig\n- .meta.toml\n- .pre-commit-config.yaml\n- tox.ini\n+##\n+# Add extra configuration options in .meta.toml:\n+# [flake8]\n+# extra_lines = """\n+# _your own configuration lines_\n+# """\n+##\ndiff --git a/.github/workflows/meta.yml b/.github/workflows/meta.yml\nnew file mode 100644\nindex 00000000..4748f0f4\n--- /dev/null\n+++ b/.github/workflows/meta.yml\n@@ -0,0 +1,28 @@\n+# Generated from:\n+# https://github.com/plone/meta/tree/master/config/default\n+# See the inline comments on how to expand/tweak this configuration file\n+name: Meta\n+on:\n+ push:\n+ branches:\n+ - master\n+ - main\n+ pull_request:\n+ branches:\n+ - master\n+ - main\n+ workflow_dispatch:\n+\n+jobs:\n+ qa:\n+ uses: plone/meta/.github/workflows/qa.yml@master\n+ test:\n+ uses: plone/meta/.github/workflows/test.yml@master\n+ coverage:\n+ uses: plone/meta/.github/workflows/coverage.yml@master\n+ dependencies:\n+ uses: plone/meta/.github/workflows/dependencies.yml@master\n+ release-ready:\n+ uses: plone/meta/.github/workflows/release_ready.yml@master\n+ circular:\n+ uses: plone/meta/.github/workflows/circular.yml@master\ndiff --git a/.gitignore b/.gitignore\nindex 223fd6c7..0690a904 100644\n--- a/.gitignore\n+++ b/.gitignore\n@@ -1,45 +1,50 @@\n-/develop-eggs\n-/eggs\n-/fake-eggs\n-/bin\n-/parts\n-/downloads\n-/var\n-/build\n-/dist\n-/local.cfg\n-.coverage\n-/*.egg-info\n-/.installed.cfg\n-*~\n+# Generated from:\n+# https://github.com/plone/meta/tree/master/config/default\n+# See the inline comments on how to expand/tweak this configuration file\n+# python related\n+*.egg-info\n *.pyc\n-/.Python\n-/include\n-/lib\n-/.project\n-/.pydevproject\n-/.mr.developer.cfg\n-/src/plone*\n-/src/collective*\n *.pyo\n-*.tmp*\n-*.mo\n-docs/Makefile\n-docs/make.bat\n-docs/doctrees\n-docs/html\n-*.egg\n-*.EGG\n-*.EGG-INFO\n-*.kpf\n-*.swp\n-*.wpr\n-.*.cfg\n-.hg/\n-.bzr/\n-.svn/\n \n+# tools related\n+build/\n+.coverage\n+coverage.xml\n+dist/\n+docs/_build\n+__pycache__/\n+.tox\n+.vscode/\n+node_modules/\n+\n+# venv / buildout related\n+bin/\n+develop-eggs/\n+eggs/\n+.eggs/\n+etc/\n+.installed.cfg\n+lib/\n+lib64\n+.mr.developer.cfg\n+parts/\n+pyvenv.cfg\n+var/\n+\n+# mxdev\n+/instance/\n+/.make-sentinels/\n+/*-mxdev.txt\n+/reports/\n+/sources/\n+/venv/\n+.installed.txt\n \n \n-# OSX\n-.DS_Store\n+##\n+# Add extra configuration options in .meta.toml:\n+# [gitignore]\n+# extra_lines = """\n+# _your own configuration lines_\n+# """\n+##\ndiff --git a/.meta.toml b/.meta.toml\nindex 07dde259..46eeeeff 100644\n--- a/.meta.toml\n+++ b/.meta.toml\n@@ -1,5 +1,16 @@\n # Generated from:\n # https://github.com/plone/meta/tree/master/config/default\n+# See the inline comments on how to expand/tweak this configuration file\n [meta]\n template = "default"\n-commit-id = "47959565"\n+commit-id = "55bda5c9"\n+\n+[pyproject]\n+codespell_ignores = "hove"\n+dependencies_ignores = "[\'plone.app.relationfield\', \'plone.directives.form\', \'plone.directives.dexterity\', \'five.grok\', \'plone.app.intid\', \'plone.contentrules\', \'plone.schema\', \'z3c.relationfield\']"\n+\n+[flake8]\n+extra_lines = """\n+per-file-ignores =\n+ plone/app/dexterity/textindexer/__init__.py:F401\n+"""\ndiff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml\nindex ea50e477..0d259b7f 100644\n--- a/.pre-commit-config.yaml\n+++ b/.pre-commit-config.yaml\n@@ -1,12 +1,13 @@\n # Generated from:\n # https://github.com/plone/meta/tree/master/config/default\n+# See the inline comments on how to expand/tweak this configuration file\n ci:\n autofix_prs: false\n autoupdate_schedule: monthly\n \n repos:\n - repo: https://github.com/asottile/pyupgrade\n- rev: v3.4.0\n+ rev: v3.8.0\n hooks:\n - id: pyupgrade\n args: [--py38-plus]\n@@ -19,19 +20,35 @@ repos:\n hooks:\n - id: black\n - repo: https://github.com/collective/zpretty\n- rev: 3.1.0a2\n+ rev: 3.1.0\n hooks:\n - id: zpretty\n+\n+##\n+# Add extra configuration options in .meta.toml:\n+# [pre_commit]\n+# zpretty_extra_lines = """\n+# _your own configuration lines_\n+# """\n+##\n - repo: https://github.com/PyCQA/flake8\n rev: 6.0.0\n hooks:\n - id: flake8\n - repo: https://github.com/codespell-project/codespell\n- rev: v2.2.4\n+ rev: v2.2.5\n hooks:\n - id: codespell\n additional_dependencies:\n - tomli\n+\n+##\n+# Add extra configuration options in .meta.toml:\n+# [pre_commit]\n+# codespell_extra_lines = """\n+# _your own configuration lines_\n+# """\n+##\n - repo: https://github.com/mgedmin/check-manifest\n rev: "0.49"\n hooks:\n@@ -40,3 +57,20 @@ repos:\n rev: "4.2"\n hooks:\n - id: pyroma\n+- repo: https://github.com/mgedmin/check-python-versions\n+ rev: "0.21.3"\n+ hooks:\n+ - id: check-python-versions\n+ args: [\'--only\', \'setup.py,pyproject.toml\']\n+- repo: https://github.com/collective/i18ndude\n+ rev: "6.0.0"\n+ hooks:\n+ - id: i18ndude\n+\n+##\n+# Add extra configuration options in .meta.toml:\n+# [pre_commit]\n+# extra_lines = """\n+# _your own configuration lines_\n+# """\n+##\ndiff --git a/news/55bda5c9.internal b/news/55bda5c9.internal\nnew file mode 100644\nindex 00000000..c08f5399\n--- /dev/null\n+++ b/news/55bda5c9.internal\n@@ -0,0 +1,2 @@\n+Update configuration files.\n+[plone devs]\ndiff --git a/pyproject.toml b/pyproject.toml\nindex ccd37afe..6e0a8720 100644\n--- a/pyproject.toml\n+++ b/pyproject.toml\n@@ -1,8 +1,9 @@\n # Generated from:\n # https://github.com/plone/meta/tree/master/config/default\n+# See the inline comments on how to expand/tweak this configuration file\n [tool.towncrier]\n-filename = "CHANGES.rst"\n directory = "news/"\n+filename = "CHANGES.rst"\n title_format = "{version} ({project_date})"\n underlines = ["-", ""]\n \n@@ -42,25 +43,92 @@ profile = "plone"\n [tool.black]\n target-version = ["py38"]\n \n+[tool.codespell]\n+ignore-words-list = "discreet,hove"\n+skip = "*.po,"\n+##\n+# Add extra configuration options in .meta.toml:\n+# [pyproject]\n+# codespell_ignores = "foo,bar"\n+# codespell_skip = "*.po,*.map,package-lock.json"\n+##\n+\n [tool.dependencychecker]\n Zope = [\n # Zope own provided namespaces\n \'App\', \'OFS\', \'Products.Five\', \'Products.OFSP\', \'Products.PageTemplates\',\n \'Products.SiteAccess\', \'Shared\', \'Testing\', \'ZPublisher\', \'ZTUtils\',\n \'Zope2\', \'webdav\', \'zmi\',\n+ # ExtensionClass own provided namespaces\n+ \'ExtensionClass\', \'ComputedAttribute\', \'MethodObject\',\n # Zope dependencies\n- \'Acquisition\', \'DateTime\', \'transaction\', \'zExceptions\', \'ZODB\', \'zope.component\',\n- \'zope.configuration\', \'zope.container\', \'zope.deferredimport\', \'zope.event\',\n- \'zope.exceptions\', \'zope.globalrequest\', \'zope.i18n\', \'zope.i18nmessageid\',\n- \'zope.interface\', \'zope.lifecycleevent\', \'zope.location\', \'zope.publisher\',\n- \'zope.schema\', \'zope.security\', \'zope.site\', \'zope.traversing\', \'AccessControl\',\n+ \'AccessControl\', \'Acquisition\', \'AuthEncoding\', \'beautifulsoup4\', \'BTrees\',\n+ \'cffi\', \'Chameleon\', \'DateTime\', \'DocumentTemplate\',\n+ \'MultiMapping\', \'multipart\', \'PasteDeploy\', \'Persistence\', \'persistent\',\n+ \'pycparser\', \'python-gettext\', \'pytz\', \'RestrictedPython\', \'roman\',\n+ \'soupsieve\', \'transaction\', \'waitress\', \'WebOb\', \'WebTest\', \'WSGIProxy2\',\n+ \'z3c.pt\', \'zc.lockfile\', \'ZConfig\', \'zExceptions\', \'ZODB\', \'zodbpickle\',\n+ \'zope.annotation\', \'zope.browser\', \'zope.browsermenu\', \'zope.browserpage\',\n+ \'zope.browserresource\', \'zope.cachedescriptors\', \'zope.component\',\n+ \'zope.configuration\', \'zope.container\', \'zope.contentprovider\',\n+ \'zope.contenttype\', \'zope.datetime\', \'zope.deferredimport\',\n+ \'zope.deprecation\', \'zope.dottedname\', \'zope.event\', \'zope.exceptions\',\n+ \'zope.filerepresentation\', \'zope.globalrequest\', \'zope.hookable\',\n+ \'zope.i18n\', \'zope.i18nmessageid\', \'zope.interface\', \'zope.lifecycleevent\',\n+ \'zope.location\', \'zope.pagetemplate\', \'zope.processlifetime\', \'zope.proxy\',\n+ \'zope.ptresource\', \'zope.publisher\', \'zope.schema\', \'zope.security\',\n+ \'zope.sequencesort\', \'zope.site\', \'zope.size\', \'zope.structuredtext\',\n+ \'zope.tal\', \'zope.tales\', \'zope.testbrowser\', \'zope.testing\',\n+ \'zope.traversing\', \'zope.viewlet\'\n+]\n+\'Products.CMFCore\' = [\n+ \'docutils\', \'five.localsitemanager\', \'Missing\', \'Products.BTreeFolder2\',\n+ \'Products.GenericSetup\', \'Products.MailHost\', \'Products.PythonScripts\',\n+ \'Products.StandardCacheManagers\', \'Products.ZCatalog\', \'Record\',\n+ \'zope.sendmail\', \'Zope\'\n ]\n \'plone.base\' = [\n- \'AccessControl\', \'Products.BTreeFolder2\', \'Products.CMFCore\',\n- \'Products.CMFDynamicViewFTI\', \'zope.deprecation\',\n+ \'plone.batching\', \'plone.registry\', \'plone.schema\',\'plone.z3cform\',\n+ \'Products.CMFCore\', \'Products.CMFDynamicViewFTI\',\n ]\n python-dateutil = [\'dateutil\']\n-ignore-packages = ["plone.app.relationfield", "plone.directives.form", "plone.directives.dexterity", "five.grok", "plone.app.intid", "plone.contentrules", "plone.schema", "z3c.relationfield"]\n+ignore-packages = [\'plone.app.relationfield\', \'plone.directives.form\', \'plone.directives.dexterity\', \'five.grok\', \'plone.app.intid\', \'plone.contentrules\', \'plone.schema\', \'z3c.relationfield\']\n \n-[tool.codespell]\n-ignore-words-list = "discreet,hove"\n+##\n+# Add extra configuration options in .meta.toml:\n+# [pyproject]\n+# dependencies_ignores = "[\'zestreleaser.towncrier\']"\n+# dependencies_mappings = [\n+# "gitpython = [\'git\']",\n+# "pygithub = [\'github\']",\n+# ]\n+# """\n+##\n+\n+[tool.check-manifest]\n+ignore = [\n+ ".editorconfig",\n+ ".meta.toml",\n+ ".pre-commit-config.yaml",\n+ "tox.ini",\n+ ".flake8",\n+ "mx.ini",\n+\n+]\n+##\n+# Add extra configuration options in .meta.toml:\n+# [pyproject]\n+# check_manifest_ignores = """\n+# "*.map.js",\n+# "*.pyc",\n+# """\n+##\n+\n+\n+##\n+# Add extra configuration options in .meta.toml:\n+# [pyproject]\n+# extra_lines = """\n+# _your own configuration lines_\n+# """\n+##\ndiff --git a/setup.py b/setup.py\nindex fc6724a0..d167da1f 100644\n--- a/setup.py\n+++ b/setup.py\n@@ -1,3 +1,4 @@\n+from pathlib import Path\n from setuptools import find_packages\n from setuptools import setup\n \n@@ -10,10 +11,10 @@\n "alternative to Archetypes that is more light-weight and modular."\n )\n \n-long_description = "{}\\n{}\\n{}".format(\n- open("README.rst").read(),\n- open("RELEASE_NOTES.rst").read(),\n- open("CHANGES.rst").read(),\n+long_description = (\n+ f"{Path(\'README.rst\').read_text()}\\n"\n+ f"{Path(\'RELEASE_NOTES.rst\').read_text()}\\n"\n+ f"{Path(\'CHANGES.rst\').read_text()}"\n )\n \n setup(\n@@ -21,6 +22,9 @@\n version=version,\n description=short_description,\n long_description=long_description,\n+ long_description_content_type="text/x-rst",\n+ # Get more strings from\n+ # https://pypi.org/classifiers/\n classifiers=[\n "Development Status :: 5 - Production/Stable",\n "Environment :: Web Environment",\n@@ -59,7 +63,6 @@\n "lxml",\n "plone.base",\n "plone.app.content",\n- "plone.app.layout",\n "plone.app.uuid",\n "plone.app.z3cform>=1.1.0",\n "plone.autoform>=1.1",\n@@ -90,6 +93,7 @@\n "plone.i18n",\n "plone.testing",\n "robotsuite",\n+ "plone.api",\n ],\n "grok": [\n "five.grok",\ndiff --git a/tox.ini b/tox.ini\nindex 79a0772e..d82659fb 100644\n--- a/tox.ini\n+++ b/tox.ini\n@@ -1,16 +1,36 @@\n # Generated from:\n # https://github.com/plone/meta/tree/master/config/default\n+# See the inline comments on how to expand/tweak this configuration file\n [tox]\n # We need 4.4.0 for constrain_package_deps.\n min_version = 4.4.0\n envlist =\n- format\n lint\n test\n+ dependencies\n+\n+\n+##\n+# Add extra configuration options in .meta.toml:\n+# [tox]\n+# envlist_lines = """\n+# my_other_environment\n+# """\n+# config_lines = """\n+# my_extra_top_level_tox_configuration_lines\n+# """\n+##\n \n [testenv]\n+skip_install = true\n allowlist_externals =\n- sh\n+ echo\n+ false\n+# Make sure typos like `tox -e formaat` are caught instead of silently doing nothing.\n+# See https://github.com/tox-dev/tox/issues/2858.\n+commands =\n+ echo "Unrecognized environment name {envname}"\n+ false\n \n [testenv:format]\n description = automatically reformat code\n@@ -32,22 +52,95 @@ commands =\n pre-commit run -a\n \n [testenv:dependencies]\n-description = check if the package defines all its dependencies and generate a graph out of them\n+description = check if the package defines all its dependencies\n+skip_install = true\n deps =\n+ build\n z3c.dependencychecker==2.11\n+commands =\n+ python -m build --sdist --no-isolation\n+ dependencychecker\n+\n+[testenv:dependencies-graph]\n+description = generate a graph out of the dependencies of the package\n+skip_install = false\n+allowlist_externals =\n+ sh\n+deps =\n pipdeptree==2.5.1\n graphviz # optional dependency of pipdeptree\n commands =\n- dependencychecker\n- sh -c \'pipdeptree --exclude setuptools,wheel,pipdeptree,z3c.dependencychecker,zope.interface,zope.component --graph-output svg > dependencies.svg\'\n+ sh -c \'pipdeptree --exclude setuptools,wheel,pipdeptree,zope.interface,zope.component --graph-output svg > dependencies.svg\'\n \n [testenv:test]\n-usedevelop = true\n+description = run the distribution tests\n+use_develop = true\n+skip_install = false\n+constrain_package_deps = true\n+set_env = ROBOT_BROWSER=headlesschrome\n+deps =\n+ zope.testrunner\n+ -c https://dist.plone.org/release/6.0-dev/constraints.txt\n+commands =\n+ zope-testrunner --all --test-path={toxinidir} -s plone.app.dexterity {posargs}\n+extras =\n+ test\n+\n+[testenv:coverage]\n+description = get a test coverage report\n+use_develop = true\n+skip_install = false\n constrain_package_deps = true\n+set_env = ROBOT_BROWSER=headlesschrome\n deps =\n+ coverage\n zope.testrunner\n -c https://dist.plone.org/release/6.0-dev/constraints.txt\n commands =\n- zope-testrunner --test-path={toxinidir} -s plone.app.dexterity\n+ coverage run --source plone.app.dexterity {envbindir}/zope-testrunner --quiet --all --test-path={toxinidir} -s plone.app.dexterity {posargs}\n+ coverage report -m --format markdown\n extras =\n test\n+\n+[testenv:release-check]\n+description = ensure that the distribution is ready to release\n+skip_install = true\n+deps =\n+ twine\n+ build\n+ towncrier\n+ -c https://dist.plone.org/release/6.0-dev/constraints.txt\n+commands =\n+ # fake version to not have to install the package\n+ # we build the change log as news entries might break\n+ # the README that is displayed on PyPI\n+ towncrier build --version=100.0.0 --yes\n+ python -m build --sdist --no-isolation\n+ twine check dist/*\n+\n+[testenv:circular]\n+description = ensure there are no cyclic dependencies\n+use_develop = true\n+skip_install = false\n+allowlist_externals =\n+ sh\n+deps =\n+ pipdeptree\n+ pipforester\n+ -c https://dist.plone.org/release/6.0-dev/constraints.txt\n+commands =\n+ # Generate the full dependency tree\n+ sh -c \'pipdeptree -j > forest.json\'\n+ # Generate a DOT graph with the circular dependencies, if any\n+ pipforester -i forest.json -o forest.dot --cycles\n+ # Report if there are any circular dependencies, i.e. error if there are any\n+ pipforester -i forest.json --check-cycles -o /dev/null\n+\n+\n+##\n+# Add extra configuration options in .meta.toml:\n+# [tox]\n+# extra_lines = """\n+# my_other_environment\n+# """\n+##\n' - -Repository: plone.app.dexterity - - -Branch: refs/heads/master -Date: 2024-05-20T20:45:51+02:00 -Author: Jens W. Klein (jensens) -Commit: https://github.com/plone/plone.app.dexterity/commit/c5d845819c9c3379ea9b18aa693f80db021fcac5 - -better phraseing and changelog, - SyntaxError - -Files changed: -A news/371.bugfix -M plone/app/dexterity/behaviors/discussion.py - -b'diff --git a/news/371.bugfix b/news/371.bugfix\nnew file mode 100644\nindex 00000000..5f63bea4\n--- /dev/null\n+++ b/news/371.bugfix\n@@ -0,0 +1,3 @@\n+`plone.app.discussion` is now a core addon.\n+The `plone.discussion` behavior class was moved over there.\n+[@jensens]\ndiff --git a/plone/app/dexterity/behaviors/discussion.py b/plone/app/dexterity/behaviors/discussion.py\nindex 2c059cd5..532ab3ba 100644\n--- a/plone/app/dexterity/behaviors/discussion.py\n+++ b/plone/app/dexterity/behaviors/discussion.py\n@@ -1,6 +1,7 @@\n from zope.deferredimport import deprecated\n \n+\n deprecated(\n- "IAllowDiscussion is deprecated here. Import from plone.app.discussion.behaviors instead (will be removed in Plone 7)"\n+ "IAllowDiscussion import from here is deprecated. Import from plone.app.discussion.behaviors instead (will be removed in Plone 7)",\n IAllowDiscussion="plone.app.discussion.behaviors:IAllowDiscussion",\n )\n' - -Repository: plone.app.dexterity - - -Branch: refs/heads/master -Date: 2024-05-20T20:46:44+02:00 -Author: Jens W. Klein (jensens) -Commit: https://github.com/plone/plone.app.dexterity/commit/72fcbbe1a71c532b6248df5db9d71ec3bad923f5 - -Merge branch 'master' into pa-discussion-coreaddon - -Files changed: -A dependabot.yml -A news/+meta.internal -A news/387.bugfix -A news/6e36bcc4.internal -A plone/app/dexterity/tests/test_namefromtitle.py -M .editorconfig -M .flake8 -M .github/workflows/meta.yml -M .gitignore -M .meta.toml -M .pre-commit-config.yaml -M CHANGES.rst -M docs/advanced/behaviours.rst -M docs/advanced/custom-add-and-edit-forms.rst -M docs/advanced/vocabularies.rst -M docs/behaviors/index.rst -M docs/behaviors/intro.rst -M docs/behaviors/providing-marker-interfaces.rst -M docs/custom-views.rst -M docs/intro.rst -M docs/schema-driven-types.rst -M docs/testing/integration-tests.rst -M plone/app/dexterity/behaviors/configure.zcml -M plone/app/dexterity/behaviors/metadata.py -M plone/app/dexterity/behaviors/related.py -M plone/app/dexterity/browser/import_types.py -M plone/app/dexterity/tests/test_constrains.py -M plone/app/dexterity/tests/test_doctests.py -M plone/app/dexterity/tests/test_export.py -M plone/app/dexterity/tests/test_import.py -M plone/app/dexterity/textindexer/behavior.py -M plone/app/dexterity/textindexer/tests/behaviors.py -M pyproject.toml -M setup.py -M tox.ini -D news/55bda5c9.internal -D plone/app/dexterity/tests/namefromtitle.txt -D plone/app/dexterity/tests/test_permissions.py - -b'diff --git a/.editorconfig b/.editorconfig\nindex 919b4116..5b3c112c 100644\n--- a/.editorconfig\n+++ b/.editorconfig\n@@ -1,5 +1,5 @@\n # Generated from:\n-# https://github.com/plone/meta/tree/master/config/default\n+# https://github.com/plone/meta/tree/main/config/default\n # See the inline comments on how to expand/tweak this configuration file\n #\n # EditorConfig Configuration file, for more details see:\n@@ -13,7 +13,8 @@\n root = true\n \n \n-[*] # For All Files\n+[*]\n+# Default settings for all files.\n # Unix-style newlines with a newline ending every file\n end_of_line = lf\n insert_final_newline = true\n@@ -29,13 +30,15 @@ max_line_length = off\n # 4 space indentation\n indent_size = 4\n \n-[*.{yml,zpt,pt,dtml,zcml}]\n+[*.{yml,zpt,pt,dtml,zcml,html,xml}]\n # 2 space indentation\n indent_size = 2\n \n-[*.{json,jsonl,js,jsx,ts,tsx,css,less,scss,html}] # Frontend development\n+[*.{json,jsonl,js,jsx,ts,tsx,css,less,scss}]\n+# Frontend development\n # 2 space indentation\n indent_size = 2\n+max_line_length = 80\n \n [{Makefile,.gitmodules}]\n # Tab indentation (no size specified, but view as 4 spaces)\ndiff --git a/.flake8 b/.flake8\nindex 32538e5c..841e8300 100644\n--- a/.flake8\n+++ b/.flake8\n@@ -1,5 +1,5 @@\n # Generated from:\n-# https://github.com/plone/meta/tree/master/config/default\n+# https://github.com/plone/meta/tree/main/config/default\n # See the inline comments on how to expand/tweak this configuration file\n [flake8]\n doctests = 1\ndiff --git a/.github/workflows/meta.yml b/.github/workflows/meta.yml\nindex 4748f0f4..b8edec02 100644\n--- a/.github/workflows/meta.yml\n+++ b/.github/workflows/meta.yml\n@@ -1,5 +1,5 @@\n # Generated from:\n-# https://github.com/plone/meta/tree/master/config/default\n+# https://github.com/plone/meta/tree/main/config/default\n # See the inline comments on how to expand/tweak this configuration file\n name: Meta\n on:\n@@ -13,16 +13,56 @@ on:\n - main\n workflow_dispatch:\n \n+##\n+# To set environment variables for all jobs, add in .meta.toml:\n+# [github]\n+# env = """\n+# debug: 1\n+# image-name: \'org/image\'\n+# image-tag: \'latest\'\n+# """\n+##\n+\n jobs:\n qa:\n- uses: plone/meta/.github/workflows/qa.yml@master\n+ uses: plone/meta/.github/workflows/qa.yml@main\n test:\n- uses: plone/meta/.github/workflows/test.yml@master\n+ uses: plone/meta/.github/workflows/test.yml@main\n coverage:\n- uses: plone/meta/.github/workflows/coverage.yml@master\n+ uses: plone/meta/.github/workflows/coverage.yml@main\n dependencies:\n- uses: plone/meta/.github/workflows/dependencies.yml@master\n- release-ready:\n- uses: plone/meta/.github/workflows/release_ready.yml@master\n+ uses: plone/meta/.github/workflows/dependencies.yml@main\n+ release_ready:\n+ uses: plone/meta/.github/workflows/release_ready.yml@main\n circular:\n- uses: plone/meta/.github/workflows/circular.yml@master\n+ uses: plone/meta/.github/workflows/circular.yml@main\n+\n+##\n+# To modify the list of default jobs being created add in .meta.toml:\n+# [github]\n+# jobs = [\n+# "qa",\n+# "test",\n+# "coverage",\n+# "dependencies",\n+# "release_ready",\n+# "circular",\n+# ]\n+##\n+\n+##\n+# To request that some OS level dependencies get installed\n+# when running tests/coverage jobs, add in .meta.toml:\n+# [github]\n+# os_dependencies = "git libxml2 libxslt"\n+##\n+\n+\n+##\n+# Specify additional jobs in .meta.toml:\n+# [github]\n+# extra_lines = """\n+# another:\n+# uses: org/repo/.github/workflows/file.yml@main\n+# """\n+##\ndiff --git a/.gitignore b/.gitignore\nindex 0690a904..486392f6 100644\n--- a/.gitignore\n+++ b/.gitignore\n@@ -1,14 +1,18 @@\n # Generated from:\n-# https://github.com/plone/meta/tree/master/config/default\n+# https://github.com/plone/meta/tree/main/config/default\n # See the inline comments on how to expand/tweak this configuration file\n # python related\n *.egg-info\n *.pyc\n *.pyo\n \n+# translation related\n+*.mo\n+\n # tools related\n build/\n .coverage\n+.*project\n coverage.xml\n dist/\n docs/_build\n@@ -24,12 +28,14 @@ eggs/\n .eggs/\n etc/\n .installed.cfg\n+include/\n lib/\n lib64\n .mr.developer.cfg\n parts/\n pyvenv.cfg\n var/\n+local.cfg\n \n # mxdev\n /instance/\ndiff --git a/.meta.toml b/.meta.toml\nindex 46eeeeff..60afbc81 100644\n--- a/.meta.toml\n+++ b/.meta.toml\n@@ -1,16 +1,19 @@\n # Generated from:\n-# https://github.com/plone/meta/tree/master/config/default\n+# https://github.com/plone/meta/tree/main/config/default\n # See the inline comments on how to expand/tweak this configuration file\n [meta]\n template = "default"\n-commit-id = "55bda5c9"\n+commit-id = "5d669ee9"\n \n [pyproject]\n codespell_ignores = "hove"\n-dependencies_ignores = "[\'plone.app.relationfield\', \'plone.directives.form\', \'plone.directives.dexterity\', \'five.grok\', \'plone.app.intid\', \'plone.contentrules\', \'plone.schema\', \'z3c.relationfield\']"\n+dependencies_ignores = "[\'plone.app.content\', \'plone.app.relationfield\', \'plone.directives.form\', \'plone.directives.dexterity\', \'five.grok\', \'plone.app.intid\', \'plone.contentrules\', \'plone.schema\', \'z3c.relationfield\']"\n \n [flake8]\n extra_lines = """\n per-file-ignores =\n plone/app/dexterity/textindexer/__init__.py:F401\n """\n+\n+[tox]\n+constraints_file = "https://dist.plone.org/release/6.1-dev/constraints.txt"\ndiff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml\nindex 0d259b7f..d60054bd 100644\n--- a/.pre-commit-config.yaml\n+++ b/.pre-commit-config.yaml\n@@ -1,5 +1,5 @@\n # Generated from:\n-# https://github.com/plone/meta/tree/master/config/default\n+# https://github.com/plone/meta/tree/main/config/default\n # See the inline comments on how to expand/tweak this configuration file\n ci:\n autofix_prs: false\n@@ -7,16 +7,16 @@ ci:\n \n repos:\n - repo: https://github.com/asottile/pyupgrade\n- rev: v3.8.0\n+ rev: v3.15.2\n hooks:\n - id: pyupgrade\n args: [--py38-plus]\n - repo: https://github.com/pycqa/isort\n- rev: 5.12.0\n+ rev: 5.13.2\n hooks:\n - id: isort\n - repo: https://github.com/psf/black\n- rev: 23.3.0\n+ rev: 24.4.2\n hooks:\n - id: black\n - repo: https://github.com/collective/zpretty\n@@ -32,11 +32,19 @@ repos:\n # """\n ##\n - repo: https://github.com/PyCQA/flake8\n- rev: 6.0.0\n+ rev: 7.0.0\n hooks:\n - id: flake8\n+\n+##\n+# Add extra configuration options in .meta.toml:\n+# [pre_commit]\n+# flake8_extra_lines = """\n+# _your own configuration lines_\n+# """\n+##\n - repo: https://github.com/codespell-project/codespell\n- rev: v2.2.5\n+ rev: v2.2.6\n hooks:\n - id: codespell\n additional_dependencies:\n@@ -58,15 +66,25 @@ repos:\n hooks:\n - id: pyroma\n - repo: https://github.com/mgedmin/check-python-versions\n- rev: "0.21.3"\n+ rev: "0.22.0"\n hooks:\n - id: check-python-versions\n args: [\'--only\', \'setup.py,pyproject.toml\']\n - repo: https://github.com/collective/i18ndude\n- rev: "6.0.0"\n+ rev: "6.2.0"\n hooks:\n - id: i18ndude\n \n+\n+##\n+# Add extra configuration options in .meta.toml:\n+# [pre_commit]\n+# i18ndude_extra_lines = """\n+# _your own configuration lines_\n+# """\n+##\n+\n+\n ##\n # Add extra configuration options in .meta.toml:\n # [pre_commit]\ndiff --git a/CHANGES.rst b/CHANGES.rst\nindex 217c81a5..9e3fbfd2 100644\n--- a/CHANGES.rst\n+++ b/CHANGES.rst\n@@ -8,6 +8,30 @@ Changelog\n \n .. towncrier release notes start\n \n+3.2.0 (2023-11-03)\n+------------------\n+\n+Internal:\n+\n+\n+- Make the dependency on ``plone.app.content`` conditional.\n+ This is for ``INameFromTitle``, which we want to move to ``plone.base``.\n+ [maurits] (#3858)\n+\n+\n+3.1.2 (2023-10-25)\n+------------------\n+\n+Internal:\n+\n+\n+- Update configuration files.\n+ [plone devs] (55bda5c9)\n+- Move some tests to `plone.app.content` to avoid\n+ a circular dependency with that package.\n+ [gforcada] (#3858)\n+\n+\n 3.1.1 (2023-05-22)\n ------------------\n \ndiff --git a/dependabot.yml b/dependabot.yml\nnew file mode 100644\nindex 00000000..bbd3ab05\n--- /dev/null\n+++ b/dependabot.yml\n@@ -0,0 +1,11 @@\n+# Generated from:\n+# https://github.com/plone/meta/tree/main/config/default\n+# See the inline comments on how to expand/tweak this configuration file\n+version: 2\n+updates:\n+\n+ - package-ecosystem: "github-actions"\n+ directory: "/"\n+ schedule:\n+ # Check for updates to GitHub Actions every week\n+ interval: "weekly"\ndiff --git a/docs/advanced/behaviours.rst b/docs/advanced/behaviours.rst\nindex fbd4a1dc..25735f26 100644\n--- a/docs/advanced/behaviours.rst\n+++ b/docs/advanced/behaviours.rst\n@@ -3,7 +3,7 @@ Using behaviors\n \n **Finding and adding behaviors**\n \n-Dexterity introduces the concept of *behaviors* \xe2\x80\x93 re-usable bundles of\n+Dexterity introduces the concept of *behaviors* \xe2\x80\x93 reusable bundles of\n functionality and/or form fields which can be turned on or off on a\n per-type basis.\n \ndiff --git a/docs/advanced/custom-add-and-edit-forms.rst b/docs/advanced/custom-add-and-edit-forms.rst\nindex 7a7e6731..10be03cd 100644\n--- a/docs/advanced/custom-add-and-edit-forms.rst\n+++ b/docs/advanced/custom-add-and-edit-forms.rst\n@@ -91,7 +91,7 @@ it needs to know which ``portal_type`` to use.\n \n You should realise that the FTIs in the ``portal_types`` tool can be\n modified through the web.\n-It is even possible to create new types through the web that re-use existing\n+It is even possible to create new types through the web that reuse existing\n classes and factories.\n \n For this reason, add forms are looked up via a namespace traversal\ndiff --git a/docs/advanced/vocabularies.rst b/docs/advanced/vocabularies.rst\nindex 36fa58af..66c98460 100644\n--- a/docs/advanced/vocabularies.rst\n+++ b/docs/advanced/vocabularies.rst\n@@ -224,7 +224,7 @@ Named vocabularies\n ~~~~~~~~~~~~~~~~~~~~\n \n Context source binders are great for simple dynamic vocabularies.\n-They are also re-usable, since you can import the source from a single location and use it in multiple instances.\n+They are also reusable, since you can import the source from a single location and use it in multiple instances.\n \n Sometimes, however, we want to provide an additional level of decoupling, by using *named* vocabularies.\n These are similar to context source binders,\ndiff --git a/docs/behaviors/index.rst b/docs/behaviors/index.rst\nindex da1be4b7..8120d966 100644\n--- a/docs/behaviors/index.rst\n+++ b/docs/behaviors/index.rst\n@@ -1,7 +1,7 @@\n Behaviors\n ==========\n \n-**How to create re-usable behaviors for Dexterity types**\n+**How to create reusable behaviors for Dexterity types**\n \n .. toctree::\n :maxdepth: 2\ndiff --git a/docs/behaviors/intro.rst b/docs/behaviors/intro.rst\nindex 163c9b71..976a6917 100644\n--- a/docs/behaviors/intro.rst\n+++ b/docs/behaviors/intro.rst\n@@ -5,7 +5,7 @@ Introduction\n \n This manual should teach you everything you need to know to write your own behaviors, but not how to integrate them into another framework.\n \n-*Behaviors* are re-usable bundles of functionality that can be enabled or disabled on a per-content type basis.\n+*Behaviors* are reusable bundles of functionality that can be enabled or disabled on a per-content type basis.\n Examples might include:\n \n - A set of form fields (on standard add and edit forms),\n@@ -17,8 +17,8 @@ You would typically not write a behavior as a one-off.\n Behaviors are normally used when:\n \n - You want to share fields and functionality across multiple types easily.\n- Behaviors allow you to write a schema and associated components (e.g. adapters, event handlers, views, viwelets) once and re-use them easily.\n-- A more experienced developer is making functionality available for re-use by less experienced integrators.\n+ Behaviors allow you to write a schema and associated components (e.g. adapters, event handlers, views, viwelets) once and reuse them easily.\n+- A more experienced developer is making functionality available for reuse by less experienced integrators.\n For example, a behavior can be packaged up and release as an add-on product.\n Integrators can then install that product and use the behavior in their own types, either in code or through-the-web.\n \ndiff --git a/docs/behaviors/providing-marker-interfaces.rst b/docs/behaviors/providing-marker-interfaces.rst\nindex 6e4ade79..0c21295c 100644\n--- a/docs/behaviors/providing-marker-interfaces.rst\n+++ b/docs/behaviors/providing-marker-interfaces.rst\n@@ -65,7 +65,7 @@ The ZCML registration looks like this:\n />\n \n Notice the use of the *AnnotationStorage* factory.\n-This is a re-usable factory that can be used to easily create behaviors from schema interfaces that store their values in annotations.\n+This is a reusable factory that can be used to easily create behaviors from schema interfaces that store their values in annotations.\n We\xe2\x80\x99ll describe this in more detail later.\n We could just as easily have provided our own factory in this example.\n \n@@ -226,6 +226,6 @@ This is quite a complex behavior, but hopefully you can see what\xe2\x80\x99s going on:\n - Similarly, we register a multi-adapter to *IIndexer*, as provided by *plone.indexer*.\n \n Although this behavior provides a lot of functionality, it is no more difficult for integrators to use than any other:\n-they would simply list the behavior interface (*iz.behaviors.reviewers.IReviewers* in this case) in the FTI, and all this functionality comes to life. This is the true power of behaviors: developers can bundle up complex functionality into re-usable behaviors, which can then be enabled on a per-type basis by integrators (or the same developers in lazier moments).\n+they would simply list the behavior interface (*iz.behaviors.reviewers.IReviewers* in this case) in the FTI, and all this functionality comes to life. This is the true power of behaviors: developers can bundle up complex functionality into reusable behaviors, which can then be enabled on a per-type basis by integrators (or the same developers in lazier moments).\n \n .. _plone.pony: http://pypi.python.org/pypi/plone.pony\ndiff --git a/docs/custom-views.rst b/docs/custom-views.rst\nindex 02ef8b16..604536f3 100644\n--- a/docs/custom-views.rst\n+++ b/docs/custom-views.rst\n@@ -206,7 +206,7 @@ Display view\n In the previous section, we created a browser view.\n This kind of view is the most common.\n Sometimes we want to make use of the widgets and information in the type\xe2\x80\x99s schema more directly.\n-For example to invoke transforms or re-use more complex HTML.\n+For example to invoke transforms or reuse more complex HTML.\n \n To do this, you can use a *display view*.\n This is really just a view base class that knows about the schema of a type.\ndiff --git a/docs/intro.rst b/docs/intro.rst\nindex a5fb1414..cbdf73a3 100644\n--- a/docs/intro.rst\n+++ b/docs/intro.rst\n@@ -29,7 +29,7 @@ For administrators and integrators, Dexterity offers:\n \n * the ability to create new content types through-the-web\n * the ability to switch on/off various aspects (called "behaviors") on a per-type basis\n-* improved collaboration between integrators (who may define a type\'s schema, say) and programmers (who may provide re-usable behaviors that the administrator can plug in).\n+* improved collaboration between integrators (who may define a type\'s schema, say) and programmers (who may provide reusable behaviors that the administrator can plug in).\n \n For developers, Dexterity promises:\n \n@@ -45,13 +45,13 @@ Dexterity is an alternative to Archetypes, Plone\'s venerable content type framew\n Some of the main differences include:\n \n * Dexterity is able to leverage many technologies that come with newer versions of CMF and Zope 3. This means that the Dexterity framework contains significantly less code than Archetypes. Dexterity also has better automated test coverage.\n-* Dexterity is more modular where Archetypes is more monolithic. This promises to make it easier to support things like SQL database-backed types, alternative workflow systems, instance-specific sub-types and so on. It also means that many of the components developed for Dexterity, such as the through-the-web schema editor, the "behaviors" system, or the forms construction API (plone.autoform) are re-usable in other contexts, e.g. to build standalone forms or even to augment existing Archetypes-based types.\n+* Dexterity is more modular where Archetypes is more monolithic. This promises to make it easier to support things like SQL database-backed types, alternative workflow systems, instance-specific sub-types and so on. It also means that many of the components developed for Dexterity, such as the through-the-web schema editor, the "behaviors" system, or the forms construction API (plone.autoform) are reusable in other contexts, e.g. to build standalone forms or even to augment existing Archetypes-based types.\n * Archetypes has its own Schema implementation which is incompatible with the interface-based approached found in zope.interface and zope.schema. The latter is used throughout the Zope stack to describe components and build forms. Various techniques exist to bridge the Archetypes schema to the Zope 3 schema notation, but none are particularly attractive.\n * Archetypes uses accessor and mutator methods to get/set values. These are generated and scribbled onto a class at startup. Dexterity uses attribute notation, so whereas in Archetypes you may write context.getFirstName(), in Dexterity you would write context.first_name.\n-* Archetypes has its own implementation of fields and widgets. It is difficult to re-use these in standalone forms or templates, because they are tied to the idea of a content object. Dexterity uses the de-facto standard z3c.form library instead, which means that the widgets used for standalone forms are the same as those used for content type add- and edit forms.\n+* Archetypes has its own implementation of fields and widgets. It is difficult to reuse these in standalone forms or templates, because they are tied to the idea of a content object. Dexterity uses the de-facto standard z3c.form library instead, which means that the widgets used for standalone forms are the same as those used for content type add- and edit forms.\n * Archetypes does not support add forms. Dexterity does, via z3c.form. This means that Dexterity types do not need to use the portal_factory hack to avoid stale objects in content space, and are thus significantly faster and less error prone.\n * Archetypes requires a chunk of boilerplate in your product\'s initialize() method (and requires that your package is registered as a Zope 2 product) and elsewhere. It requires a particular sequence of initialisation calls to register content classes, run the class generator to add accessors/mutators, and set up permissions. Dexterity does away with all that boilerplate, and tries to minimise repetition.\n-* It is possible to extend the schemata of existing Archetypes types with the archetypes.schemaextender product, although this adds some performance overhead and relies on a somewhat awkward programming technique. Dexterity types were built to be extensible from the beginning, and it is possible to declaratively turn on or off aspects of a type (such as Dublin Core metadata, locking support, ratings, tagging, etc) with re-usable "behaviors".\n+* It is possible to extend the schemata of existing Archetypes types with the archetypes.schemaextender product, although this adds some performance overhead and relies on a somewhat awkward programming technique. Dexterity types were built to be extensible from the beginning, and it is possible to declaratively turn on or off aspects of a type (such as Dublin Core metadata, locking support, ratings, tagging, etc) with reusable "behaviors".\n * Dexterity is built from the ground up to support through-the-web type creation. There are products that achieve the same thing with Archetypes types, but they have to work around a number of limitations in the design of Archetypes that make them somewhat brittle or slow. Dexterity also allows types to be developed jointly through-the-web and on the filesystem. For example, a schema can be written in Python and then extended through the web.\n \n As of version 5 of Plone, Dexterity is the preferred way of creating content types.\ndiff --git a/docs/schema-driven-types.rst b/docs/schema-driven-types.rst\nindex 56cd6ccc..32c71ce8 100644\n--- a/docs/schema-driven-types.rst\n+++ b/docs/schema-driven-types.rst\n@@ -317,7 +317,7 @@ The important lines here are:\n The default ``cmf.AddPortalContent`` should be used unless you configure a custom permission.\n Custom permissions are converted later in this manual.\n - We add a *behavior*.\n- Behaviors are re-usable aspects providing semantics and/or schema fields.\n+ Behaviors are reusable aspects providing semantics and/or schema fields.\n Here, we add the ``INameFromTitle`` behavior, which will give our content object a readable id based on the ``title`` property. We\xe2\x80\x99ll cover other behaviors later.\n \n The ``Program``, in ``program.xml``, looks like this:\ndiff --git a/docs/testing/integration-tests.rst b/docs/testing/integration-tests.rst\nindex cd22cb24..1a9f3c3d 100644\n--- a/docs/testing/integration-tests.rst\n+++ b/docs/testing/integration-tests.rst\n@@ -11,7 +11,7 @@ very least.\n To help manage test setup, we\xe2\x80\x99ll make use of the Zope test runner\xe2\x80\x99s\n concept of *layers*.\n Layers allow common test setup (such as configuring a Plone site and\n-installing a product) to take place once and be re-used by multiple test\n+installing a product) to take place once and be reused by multiple test\n cases.\n Those test cases can still modify the environment, but their changes will be\n torn down and the environment reset to the layer\xe2\x80\x99s initial state between\ndiff --git a/news/55bda5c9.internal b/news/+meta.internal\nsimilarity index 100%\nrename from news/55bda5c9.internal\nrename to news/+meta.internal\ndiff --git a/news/387.bugfix b/news/387.bugfix\nnew file mode 100644\nindex 00000000..d2a9273b\n--- /dev/null\n+++ b/news/387.bugfix\n@@ -0,0 +1 @@\n+Remove a DeprecationWarning from a moved p.a.z3cform.widgets import. [@jensens]\n\\ No newline at end of file\ndiff --git a/news/6e36bcc4.internal b/news/6e36bcc4.internal\nnew file mode 100644\nindex 00000000..c08f5399\n--- /dev/null\n+++ b/news/6e36bcc4.internal\n@@ -0,0 +1,2 @@\n+Update configuration files.\n+[plone devs]\ndiff --git a/plone/app/dexterity/behaviors/configure.zcml b/plone/app/dexterity/behaviors/configure.zcml\nindex a105dbf7..e5c09ccb 100644\n--- a/plone/app/dexterity/behaviors/configure.zcml\n+++ b/plone/app/dexterity/behaviors/configure.zcml\n@@ -59,23 +59,31 @@\n for="plone.dexterity.interfaces.IDexterityContent"\n />\n \n- \n- \n+ \n+ \n+ \n+ \n \n- \n- \n+ \n+ \n \n- \n+ \n+ \n \n \n >> portal = layer[\'portal\']\n- >>> from plone.dexterity.fti import DexterityFTI\n- >>> fti = DexterityFTI(\'dinosaur\')\n- >>> portal.portal_types._setObject(\'dinosaur\', fti)\n- \'dinosaur\'\n- >>> fti.klass = \'plone.dexterity.content.Container\'\n- >>> fti.filter_content_types = False\n-\n-We can declare that it supports the "name from title" behavior defined in\n-plone.app.content (normally this would be done via Generic Setup)::\n-\n- >>> fti.behaviors = (\'plone.app.content.interfaces.INameFromTitle\',\n- ... \'plone.app.dexterity.behaviors.metadata.IBasic\')\n-\n-Now let\'s fire up the browser and confirm that new content gets renamed\n-appropriately::\n-\n- >>> from plone.app.testing import TEST_USER_ID, TEST_USER_NAME, TEST_USER_PASSWORD, setRoles\n- >>> setRoles(portal, TEST_USER_ID, [\'Manager\'])\n- >>> import transaction; transaction.commit()\n- >>> from plone.testing.z2 import Browser\n- >>> browser = Browser(layer[\'app\'])\n- >>> browser.addHeader(\'Authorization\', \'Basic %s:%s\' % (TEST_USER_NAME, TEST_USER_PASSWORD,))\n-\n- >>> browser.open(\'http://nohost/plone/++add++dinosaur\')\n- >>> browser.getControl(\'Title\').value = \'Brachiosaurus\'\n- >>> browser.getControl(\'Save\').click()\n- >>> browser.url\n- \'http://nohost/plone/brachiosaurus/view\'\n-\n-\n-Title-to-id within a Dexterity container\n-----------------------------------------\n-\n-Does it still work if we\'re adding content within a Dexterity container? Let\'s\n-check::\n-\n- >>> browser.open(\'http://nohost/plone/brachiosaurus/++add++dinosaur\')\n- >>> browser.getControl(\'Title\').value = \'Baby Brachiosaurus\'\n- >>> browser.getControl(\'Save\').click()\n- >>> browser.url\n- \'http://nohost/plone/brachiosaurus/baby-brachiosaurus/view\'\ndiff --git a/plone/app/dexterity/tests/test_constrains.py b/plone/app/dexterity/tests/test_constrains.py\nindex 446756c8..390c4a1e 100644\n--- a/plone/app/dexterity/tests/test_constrains.py\n+++ b/plone/app/dexterity/tests/test_constrains.py\n@@ -1,4 +1,3 @@\n-from plone.app.content.browser.constraintypes import IConstrainForm\n from plone.app.dexterity.behaviors import constrains\n from plone.app.dexterity.testing import DEXTERITY_FUNCTIONAL_TESTING\n from plone.app.dexterity.testing import DEXTERITY_INTEGRATION_TESTING\n@@ -11,7 +10,6 @@\n from plone.dexterity.fti import DexterityFTI\n from plone.testing.zope import Browser\n from Products.CMFCore.utils import getToolByName\n-from zope.interface.exceptions import Invalid\n \n import unittest\n \n@@ -325,20 +323,6 @@ def test_allowedContentTypesExit4(self):\n self.types_id_subset, [x.getId() for x in behavior.allowedContentTypes()]\n )\n \n- def test_formschemainvariants(self):\n- class Data:\n- allowed_types = []\n- secondary_types = []\n-\n- bad = Data()\n- bad.allowed_types = []\n- bad.secondary_types = ["1"]\n- good = Data()\n- good.allowed_types = ["1"]\n- good.secondary_types = []\n- self.assertTrue(IConstrainForm.validateInvariants(good) is None)\n- self.assertRaises(Invalid, IConstrainForm.validateInvariants, bad)\n-\n \n class FolderConstrainViewFunctionalText(unittest.TestCase):\n layer = DEXTERITY_FUNCTIONAL_TESTING\ndiff --git a/plone/app/dexterity/tests/test_doctests.py b/plone/app/dexterity/tests/test_doctests.py\nindex ecdfce99..6b361554 100644\n--- a/plone/app/dexterity/tests/test_doctests.py\n+++ b/plone/app/dexterity/tests/test_doctests.py\n@@ -8,7 +8,6 @@\n tests = (\n "discussion.txt",\n "editing.rst",\n- "namefromtitle.txt",\n "metadata.txt",\n "nextprevious.txt",\n "filename.txt",\ndiff --git a/plone/app/dexterity/tests/test_export.py b/plone/app/dexterity/tests/test_export.py\nindex ae944ad1..b8a40d03 100644\n--- a/plone/app/dexterity/tests/test_export.py\n+++ b/plone/app/dexterity/tests/test_export.py\n@@ -1,4 +1,5 @@\n """Test the @@types-export view."""\n+\n from plone.app.dexterity.testing import DEXTERITY_INTEGRATION_TESTING\n from plone.app.dexterity.tests.test_constrains import add_item_type\n from xml.dom.minidom import parseString\ndiff --git a/plone/app/dexterity/tests/test_import.py b/plone/app/dexterity/tests/test_import.py\nindex 026af16b..ce69de7b 100644\n--- a/plone/app/dexterity/tests/test_import.py\n+++ b/plone/app/dexterity/tests/test_import.py\n@@ -1,4 +1,5 @@\n """Test the types import."""\n+\n from DateTime.DateTime import DateTime\n from plone.app.dexterity.browser.import_types import ITypeProfileImport\n from plone.app.dexterity.browser.import_types import TypeProfileImport\ndiff --git a/plone/app/dexterity/tests/test_namefromtitle.py b/plone/app/dexterity/tests/test_namefromtitle.py\nnew file mode 100644\nindex 00000000..94d137b7\n--- /dev/null\n+++ b/plone/app/dexterity/tests/test_namefromtitle.py\n@@ -0,0 +1,84 @@\n+from plone.app.dexterity.testing import DEXTERITY_FUNCTIONAL_TESTING\n+from plone.app.testing import setRoles\n+from plone.app.testing import TEST_USER_ID\n+from plone.app.testing import TEST_USER_NAME\n+from plone.app.testing import TEST_USER_PASSWORD\n+from plone.dexterity.fti import DexterityFTI\n+from plone.testing.zope import Browser\n+\n+import transaction\n+import unittest\n+\n+\n+def add_dinosaur_type(portal, behavior_name):\n+ fti = DexterityFTI("dinosaur")\n+ portal.portal_types._setObject("dinosaur", fti)\n+ fti.klass = "plone.dexterity.content.Container"\n+ fti.filter_content_types = False\n+ fti.behaviors = (\n+ behavior_name,\n+ "plone.basic",\n+ )\n+ return fti\n+\n+\n+class NameFromTitleFunctionalTest(unittest.TestCase):\n+ """Test name-from-title using named behavior."""\n+\n+ layer = DEXTERITY_FUNCTIONAL_TESTING\n+ behavior_name = "plone.namefromtitle"\n+\n+ def setUp(self):\n+ app = self.layer["app"]\n+ self.portal = self.layer["portal"]\n+ self.request = self.layer["request"]\n+ setRoles(self.portal, TEST_USER_ID, ["Manager"])\n+ self.portal_url = self.portal.absolute_url()\n+\n+ # Say we have a \'Dinosaur\' content type:\n+ self.fti = add_dinosaur_type(self.portal, self.behavior_name)\n+\n+ transaction.commit()\n+ self.browser = Browser(app)\n+ self.browser.handleErrors = False\n+ self.browser.addHeader(\n+ "Authorization",\n+ "Basic {}:{}".format(\n+ TEST_USER_NAME,\n+ TEST_USER_PASSWORD,\n+ ),\n+ )\n+\n+ def test_create(self):\n+ self.browser.open(f"{self.portal_url}/++add++dinosaur")\n+ self.browser.getControl("Title").value = "Brachiosaurus"\n+ self.browser.getControl("Save").click()\n+ self.assertEqual(self.browser.url, f"{self.portal_url}/brachiosaurus/view")\n+\n+ # Does it still work if we are adding content within a container?\n+ self.browser.open(f"{self.portal_url}/brachiosaurus/++add++dinosaur")\n+ self.browser.getControl("Title").value = "Baby Brachiosaurus"\n+ self.browser.getControl("Save").click()\n+ self.assertEqual(\n+ self.browser.url,\n+ f"{self.portal_url}/brachiosaurus/baby-brachiosaurus/view",\n+ )\n+\n+\n+class PloneAppContentNameFromTitleFunctionalTest(NameFromTitleFunctionalTest):\n+ """Test name-from-title using old plone.app.content behavior interface."""\n+\n+ behavior_name = "plone.app.content.interfaces.INameFromTitle"\n+\n+\n+# We could test that you can use the new interface location as behavior name,\n+# but this fails, and this is fine: it was never supported.\n+# In all cases the named behavior is recommended.\n+#\n+# class PloneBaseNameFromTitleFunctionalTest(NameFromTitleFunctionalTest):\n+# """Test name-from-title using new plone.base behavior interface."""\n+# behavior_name = "plone.base.interfaces.INameFromTitle"\n+\n+\n+def test_suite():\n+ return unittest.defaultTestLoader.loadTestsFromName(__name__)\ndiff --git a/plone/app/dexterity/tests/test_permissions.py b/plone/app/dexterity/tests/test_permissions.py\ndeleted file mode 100644\nindex 273796cb..00000000\n--- a/plone/app/dexterity/tests/test_permissions.py\n+++ /dev/null\n@@ -1,263 +0,0 @@\n-from plone.app.content.browser.vocabulary import VocabularyView\n-from plone.app.testing import login\n-from plone.app.testing import setRoles\n-from plone.app.testing import TEST_USER_ID\n-from plone.app.testing import TEST_USER_NAME\n-from plone.app.z3cform.interfaces import IPloneFormLayer\n-from plone.app.z3cform.tests.layer import PAZ3CForm_INTEGRATION_TESTING\n-from plone.autoform.interfaces import WIDGETS_KEY\n-from plone.autoform.interfaces import WRITE_PERMISSIONS_KEY\n-from plone.dexterity.browser.add import DefaultAddForm\n-from plone.dexterity.browser.add import DefaultAddView\n-from plone.dexterity.fti import DexterityFTI\n-from z3c.form.interfaces import IFieldWidget\n-from z3c.form.util import getSpecification\n-from z3c.form.widget import FieldWidget\n-from zope import schema\n-from zope.component import provideAdapter\n-from zope.component.globalregistry import base\n-from zope.globalrequest import setRequest\n-from zope.interface import Interface\n-from zope.publisher.browser import TestRequest\n-\n-import json\n-import unittest\n-\n-\n-def add_mock_fti(portal):\n- # Fake DX Type\n- fti = DexterityFTI("dx_mock")\n- portal.portal_types._setObject("dx_mock", fti)\n- fti.klass = "plone.dexterity.content.Item"\n- fti.schema = "plone.app.dexterity.tests.test_permissions.IMockSchema"\n- fti.filter_content_types = False\n- fti.behaviors = ("plone.app.dexterity.behaviors.metadata.IBasic",)\n-\n-\n-def _custom_field_widget(field, request):\n- from plone.app.z3cform.widget import AjaxSelectWidget\n-\n- widget = FieldWidget(field, AjaxSelectWidget(request))\n- widget.vocabulary = "plone.app.vocabularies.PortalTypes"\n- return widget\n-\n-\n-class IMockSchema(Interface):\n- allowed_field = schema.Choice(vocabulary="plone.app.vocabularies.PortalTypes")\n- disallowed_field = schema.Choice(vocabulary="plone.app.vocabularies.PortalTypes")\n- default_field = schema.Choice(vocabulary="plone.app.vocabularies.PortalTypes")\n- custom_widget_field = schema.TextLine()\n- adapted_widget_field = schema.TextLine()\n-\n-\n-IMockSchema.setTaggedValue(\n- WRITE_PERMISSIONS_KEY,\n- {\n- "allowed_field": "zope2.View",\n- "disallowed_field": "zope2.ViewManagementScreens",\n- "custom_widget_field": "zope2.View",\n- "adapted_widget_field": "zope2.View",\n- },\n-)\n-IMockSchema.setTaggedValue(\n- WIDGETS_KEY,\n- {\n- "custom_widget_field": _custom_field_widget,\n- },\n-)\n-\n-\n-def _enable_custom_widget(field):\n- provideAdapter(\n- _custom_field_widget,\n- adapts=(getSpecification(field), IPloneFormLayer),\n- provides=IFieldWidget,\n- )\n-\n-\n-def _disable_custom_widget(field):\n- base.unregisterAdapter(\n- required=(\n- getSpecification(field),\n- IPloneFormLayer,\n- ),\n- provided=IFieldWidget,\n- )\n-\n-\n-class DexterityVocabularyPermissionTests(unittest.TestCase):\n- layer = PAZ3CForm_INTEGRATION_TESTING\n-\n- def setUp(self):\n- self.request = TestRequest(environ={"HTTP_ACCEPT_LANGUAGE": "en"})\n- setRequest(self.request)\n- self.portal = self.layer["portal"]\n-\n- login(self.portal, TEST_USER_NAME)\n- setRoles(self.portal, TEST_USER_ID, ["Manager"])\n-\n- add_mock_fti(self.portal)\n- self.portal.invokeFactory("dx_mock", "test_dx")\n-\n- self.portal.test_dx.manage_permission("View", ("Anonymous",), acquire=False)\n- self.portal.test_dx.manage_permission(\n- "View management screens", (), acquire=False\n- )\n- self.portal.test_dx.manage_permission(\n- "Modify portal content",\n- ("Editor", "Manager", "Site Adiminstrator"),\n- acquire=False,\n- )\n-\n- def test_vocabulary_field_allowed(self):\n- view = VocabularyView(self.portal.test_dx, self.request)\n- self.request.form.update(\n- {\n- "name": "plone.app.vocabularies.PortalTypes",\n- "field": "allowed_field",\n- }\n- )\n- data = json.loads(view())\n- self.assertEqual(\n- len(data["results"]),\n- len(self.portal.portal_types.objectIds()),\n- )\n-\n- def test_vocabulary_field_wrong_vocabulary_disallowed(self):\n- view = VocabularyView(self.portal.test_dx, self.request)\n- self.request.form.update(\n- {\n- "name": "plone.app.vocabularies.Fake",\n- "field": "allowed_field",\n- }\n- )\n- data = json.loads(view())\n- self.assertEqual(data["error"], "Vocabulary lookup not allowed")\n-\n- def test_vocabulary_field_disallowed(self):\n- view = VocabularyView(self.portal.test_dx, self.request)\n- self.request.form.update(\n- {\n- "name": "plone.app.vocabularies.PortalTypes",\n- "field": "disallowed_field",\n- }\n- )\n- data = json.loads(view())\n- self.assertEqual(data["error"], "Vocabulary lookup not allowed")\n-\n- def test_vocabulary_field_default_permission(self):\n- view = VocabularyView(self.portal.test_dx, self.request)\n- self.request.form.update(\n- {\n- "name": "plone.app.vocabularies.PortalTypes",\n- "field": "default_field",\n- }\n- )\n- # If the field is does not have a security declaration, the\n- # default edit permission is tested (Modify portal content)\n- setRoles(self.portal, TEST_USER_ID, ["Member"])\n- data = json.loads(view())\n- self.assertEqual(data["error"], "Vocabulary lookup not allowed")\n-\n- setRoles(self.portal, TEST_USER_ID, ["Editor"])\n- # Now access should be allowed, but the vocabulary does not exist\n- data = json.loads(view())\n- self.assertEqual(\n- len(data["results"]),\n- len(self.portal.portal_types.objectIds()),\n- )\n-\n- def test_vocabulary_field_default_permission_wrong_vocab(self):\n- view = VocabularyView(self.portal.test_dx, self.request)\n- self.request.form.update(\n- {\n- "name": "plone.app.vocabularies.Fake",\n- "field": "default_field",\n- }\n- )\n- setRoles(self.portal, TEST_USER_ID, ["Editor"])\n- # Now access should be allowed, but the vocabulary does not exist\n- data = json.loads(view())\n- self.assertEqual(data["error"], "Vocabulary lookup not allowed")\n-\n- def test_vocabulary_missing_field(self):\n- view = VocabularyView(self.portal.test_dx, self.request)\n- self.request.form.update(\n- {\n- "name": "plone.app.vocabularies.PortalTypes",\n- "field": "missing_field",\n- }\n- )\n- setRoles(self.portal, TEST_USER_ID, ["Member"])\n- with self.assertRaises(AttributeError):\n- view()\n-\n- def test_vocabulary_on_widget(self):\n- view = VocabularyView(self.portal.test_dx, self.request)\n- self.request.form.update(\n- {\n- "name": "plone.app.vocabularies.PortalTypes",\n- "field": "custom_widget_field",\n- }\n- )\n- data = json.loads(view())\n- self.assertEqual(\n- len(data["results"]),\n- len(self.portal.portal_types.objectIds()),\n- )\n- self.request.form["name"] = "plone.app.vocabularies.Fake"\n- data = json.loads(view())\n- self.assertEqual(data["error"], "Vocabulary lookup not allowed")\n-\n- def test_vocabulary_on_adapted_widget(self):\n- _enable_custom_widget(IMockSchema["adapted_widget_field"])\n- view = VocabularyView(self.portal.test_dx, self.request)\n- self.request.form.update(\n- {\n- "name": "plone.app.vocabularies.PortalTypes",\n- "field": "adapted_widget_field",\n- }\n- )\n- data = json.loads(view())\n- self.assertEqual(\n- len(data["results"]),\n- len(self.portal.portal_types.objectIds()),\n- )\n-\n- self.request.form["name"] = "plone.app.vocabularies.Fake"\n- data = json.loads(view())\n- self.assertEqual(data["error"], "Vocabulary lookup not allowed")\n- _disable_custom_widget(IMockSchema["adapted_widget_field"])\n-\n- def test_vocabulary_field_allowed_from_add_view(self):\n- add_view = DefaultAddView(\n- self.portal, self.request, self.portal.portal_types["dx_mock"]\n- )\n- view = VocabularyView(add_view, self.request)\n- self.request.form.update(\n- {\n- "name": "plone.app.vocabularies.PortalTypes",\n- "field": "allowed_field",\n- }\n- )\n- data = json.loads(view())\n- self.assertEqual(\n- len(data["results"]),\n- len(self.portal.portal_types.objectIds()),\n- )\n-\n- def test_vocabulary_field_allowed_from_add_form(self):\n- add_form = DefaultAddForm(self.portal, self.request)\n- add_form.portal_type = "dx_mock"\n- view = VocabularyView(add_form, self.request)\n- self.request.form.update(\n- {\n- "name": "plone.app.vocabularies.PortalTypes",\n- "field": "allowed_field",\n- }\n- )\n- data = json.loads(view())\n- self.assertEqual(\n- len(data["results"]),\n- len(self.portal.portal_types.objectIds()),\n- )\ndiff --git a/plone/app/dexterity/textindexer/behavior.py b/plone/app/dexterity/textindexer/behavior.py\nindex ed9a965f..7c7bf782 100644\n--- a/plone/app/dexterity/textindexer/behavior.py\n+++ b/plone/app/dexterity/textindexer/behavior.py\n@@ -1,6 +1,7 @@\n """IDexterityTextIndexer dexterity behavior interface for enabling\n the dexteritytextindexer\n """\n+\n from zope.interface import Interface\n \n \ndiff --git a/plone/app/dexterity/textindexer/tests/behaviors.py b/plone/app/dexterity/textindexer/tests/behaviors.py\nindex 2002367d..54a22c02 100644\n--- a/plone/app/dexterity/textindexer/tests/behaviors.py\n+++ b/plone/app/dexterity/textindexer/tests/behaviors.py\n@@ -1,5 +1,6 @@\n """Contains different behaviors needed for testing.\n """\n+\n from plone.app.dexterity import textindexer\n from plone.app.textfield import RichText\n from plone.autoform.interfaces import IFormFieldProvider\ndiff --git a/pyproject.toml b/pyproject.toml\nindex 6e0a8720..e9dc3aa6 100644\n--- a/pyproject.toml\n+++ b/pyproject.toml\n@@ -1,6 +1,9 @@\n # Generated from:\n-# https://github.com/plone/meta/tree/master/config/default\n+# https://github.com/plone/meta/tree/main/config/default\n # See the inline comments on how to expand/tweak this configuration file\n+[build-system]\n+requires = ["setuptools>=68.2"]\n+\n [tool.towncrier]\n directory = "news/"\n filename = "CHANGES.rst"\n@@ -37,12 +40,36 @@ directory = "tests"\n name = "Tests"\n showcontent = true\n \n+##\n+# Add extra configuration options in .meta.toml:\n+# [pyproject]\n+# towncrier_extra_lines = """\n+# extra_configuration\n+# """\n+##\n+\n [tool.isort]\n profile = "plone"\n \n+##\n+# Add extra configuration options in .meta.toml:\n+# [pyproject]\n+# isort_extra_lines = """\n+# extra_configuration\n+# """\n+##\n+\n [tool.black]\n target-version = ["py38"]\n \n+##\n+# Add extra configuration options in .meta.toml:\n+# [pyproject]\n+# black_extra_lines = """\n+# extra_configuration\n+# """\n+##\n+\n [tool.codespell]\n ignore-words-list = "discreet,hove"\n skip = "*.po,"\n@@ -92,7 +119,7 @@ Zope = [\n \'Products.CMFCore\', \'Products.CMFDynamicViewFTI\',\n ]\n python-dateutil = [\'dateutil\']\n-ignore-packages = [\'plone.app.relationfield\', \'plone.directives.form\', \'plone.directives.dexterity\', \'five.grok\', \'plone.app.intid\', \'plone.contentrules\', \'plone.schema\', \'z3c.relationfield\']\n+ignore-packages = [\'plone.app.content\', \'plone.app.relationfield\', \'plone.directives.form\', \'plone.directives.dexterity\', \'five.grok\', \'plone.app.intid\', \'plone.contentrules\', \'plone.schema\', \'z3c.relationfield\']\n \n ##\n # Add extra configuration options in .meta.toml:\n@@ -102,19 +129,20 @@ ignore-packages = [\'plone.app.relationfield\', \'plone.directives.form\', \'plone.di\n # "gitpython = [\'git\']",\n # "pygithub = [\'github\']",\n # ]\n-# """\n ##\n \n [tool.check-manifest]\n ignore = [\n ".editorconfig",\n+ ".flake8",\n ".meta.toml",\n ".pre-commit-config.yaml",\n- "tox.ini",\n- ".flake8",\n+ "dependabot.yml",\n "mx.ini",\n+ "tox.ini",\n \n ]\n+\n ##\n # Add extra configuration options in .meta.toml:\n # [pyproject]\n@@ -122,6 +150,11 @@ ignore = [\n # "*.map.js",\n # "*.pyc",\n # """\n+# check_manifest_extra_lines = """\n+# ignore-bad-ideas = [\n+# "some/test/file/PKG-INFO",\n+# ]\n+# """\n ##\n \n \ndiff --git a/setup.py b/setup.py\nindex d167da1f..baf48695 100644\n--- a/setup.py\n+++ b/setup.py\n@@ -3,7 +3,7 @@\n from setuptools import setup\n \n \n-version = "3.1.2.dev0"\n+version = "4.0.0.dev0"\n \n short_description = (\n "Dexterity is a content type framework for CMF applications, "\n@@ -29,16 +29,15 @@\n "Development Status :: 5 - Production/Stable",\n "Environment :: Web Environment",\n "Framework :: Plone",\n- "Framework :: Plone :: 6.0",\n+ "Framework :: Plone :: 6.1",\n "Framework :: Plone :: Core",\n "Framework :: Zope :: 5",\n "License :: OSI Approved :: GNU General Public License v2 (GPLv2)",\n "Operating System :: OS Independent",\n "Programming Language :: Python",\n- "Programming Language :: Python :: 3.8",\n- "Programming Language :: Python :: 3.9",\n "Programming Language :: Python :: 3.10",\n "Programming Language :: Python :: 3.11",\n+ "Programming Language :: Python :: 3.12",\n ],\n keywords="plone ttw dexterity schema interface",\n author="Martin Aspeli, David Glick, et al",\n@@ -49,7 +48,7 @@\n namespace_packages=["plone", "plone.app"],\n include_package_data=True,\n zip_safe=False,\n- python_requires=">=3.8",\n+ python_requires=">=3.10",\n install_requires=[\n # Dexterity\n "plone.app.textfield",\n@@ -58,15 +57,15 @@\n "plone.formwidget.namedfile",\n "plone.namedfile>=1.0.0",\n "plone.rfc822",\n- "plone.schemaeditor >1.3.3",\n+ "plone.schemaeditor>1.3.3",\n # Plone/Zope core\n "lxml",\n "plone.base",\n- "plone.app.content",\n "plone.app.uuid",\n- "plone.app.z3cform>=1.1.0",\n+ "plone.app.z3cform>=4.6.0",\n "plone.autoform>=1.1",\n "plone.contentrules",\n+ "plone.portlets",\n "plone.schema>=1.1.0",\n "plone.supermodel>=1.1",\n "plone.z3cform>=0.6.0",\ndiff --git a/tox.ini b/tox.ini\nindex d82659fb..6ac3537b 100644\n--- a/tox.ini\n+++ b/tox.ini\n@@ -1,5 +1,5 @@\n # Generated from:\n-# https://github.com/plone/meta/tree/master/config/default\n+# https://github.com/plone/meta/tree/main/config/default\n # See the inline comments on how to expand/tweak this configuration file\n [tox]\n # We need 4.4.0 for constrain_package_deps.\n@@ -32,6 +32,21 @@ commands =\n echo "Unrecognized environment name {envname}"\n false\n \n+##\n+# Add extra configuration options in .meta.toml:\n+# [tox]\n+# testenv_options = """\n+# basepython = /usr/bin/python3.8\n+# """\n+##\n+\n+[testenv:init]\n+description = Prepare environment\n+skip_install = true\n+commands =\n+ echo "Initial setup complete"\n+\n+\n [testenv:format]\n description = automatically reformat code\n skip_install = true\n@@ -56,9 +71,9 @@ description = check if the package defines all its dependencies\n skip_install = true\n deps =\n build\n- z3c.dependencychecker==2.11\n+ z3c.dependencychecker==2.14.3\n commands =\n- python -m build --sdist --no-isolation\n+ python -m build --sdist\n dependencychecker\n \n [testenv:dependencies-graph]\n@@ -77,31 +92,78 @@ description = run the distribution tests\n use_develop = true\n skip_install = false\n constrain_package_deps = true\n-set_env = ROBOT_BROWSER=headlesschrome\n+set_env =\n+ ROBOT_BROWSER=headlesschrome\n+\n+##\n+# Specify extra test environment variables in .meta.toml:\n+# [tox]\n+# test_environment_variables = """\n+# PIP_EXTRA_INDEX_URL=https://my-pypi.my-server.com/\n+# """\n+#\n+# Set constrain_package_deps .meta.toml:\n+# [tox]\n+# constrain_package_deps = "false"\n+##\n deps =\n zope.testrunner\n- -c https://dist.plone.org/release/6.0-dev/constraints.txt\n+ -c https://dist.plone.org/release/6.1-dev/constraints.txt\n+\n+##\n+# Specify additional deps in .meta.toml:\n+# [tox]\n+# test_deps_additional = "-esources/plonegovbr.portal_base[test]"\n+#\n+# Specify a custom constraints file in .meta.toml:\n+# [tox]\n+# constraints_file = "https://my-server.com/constraints.txt"\n+##\n commands =\n+ rfbrowser init\n zope-testrunner --all --test-path={toxinidir} -s plone.app.dexterity {posargs}\n extras =\n test\n \n+##\n+# Add extra configuration options in .meta.toml:\n+# [tox]\n+# test_extras = """\n+# tests\n+# widgets\n+# """\n+##\n+\n [testenv:coverage]\n description = get a test coverage report\n use_develop = true\n skip_install = false\n constrain_package_deps = true\n-set_env = ROBOT_BROWSER=headlesschrome\n+set_env =\n+ ROBOT_BROWSER=headlesschrome\n+\n+##\n+# Specify extra test environment variables in .meta.toml:\n+# [tox]\n+# test_environment_variables = """\n+# PIP_EXTRA_INDEX_URL=https://my-pypi.my-server.com/\n+# """\n+##\n deps =\n coverage\n zope.testrunner\n- -c https://dist.plone.org/release/6.0-dev/constraints.txt\n+ -c https://dist.plone.org/release/6.1-dev/constraints.txt\n+\n commands =\n- coverage run --source plone.app.dexterity {envbindir}/zope-testrunner --quiet --all --test-path={toxinidir} -s plone.app.dexterity {posargs}\n+ rfbrowser init\n+ coverage run --branch --source plone.app.dexterity {envbindir}/zope-testrunner --quiet --all --test-path={toxinidir} -s plone.app.dexterity {posargs}\n coverage report -m --format markdown\n+ coverage xml\n+ coverage html\n extras =\n test\n \n+\n [testenv:release-check]\n description = ensure that the distribution is ready to release\n skip_install = true\n@@ -109,25 +171,39 @@ deps =\n twine\n build\n towncrier\n- -c https://dist.plone.org/release/6.0-dev/constraints.txt\n+ -c https://dist.plone.org/release/6.1-dev/constraints.txt\n+\n commands =\n # fake version to not have to install the package\n # we build the change log as news entries might break\n # the README that is displayed on PyPI\n towncrier build --version=100.0.0 --yes\n- python -m build --sdist --no-isolation\n+ python -m build --sdist\n twine check dist/*\n \n [testenv:circular]\n description = ensure there are no cyclic dependencies\n use_develop = true\n skip_install = false\n+# Here we must always constrain the package deps to what is already installed,\n+# otherwise we simply get the latest from PyPI, which may not work.\n+constrain_package_deps = true\n+set_env =\n+\n+##\n+# Specify extra test environment variables in .meta.toml:\n+# [tox]\n+# test_environment_variables = """\n+# PIP_EXTRA_INDEX_URL=https://my-pypi.my-server.com/\n+# """\n+##\n allowlist_externals =\n sh\n deps =\n pipdeptree\n pipforester\n- -c https://dist.plone.org/release/6.0-dev/constraints.txt\n+ -c https://dist.plone.org/release/6.1-dev/constraints.txt\n+\n commands =\n # Generate the full dependency tree\n sh -c \'pipdeptree -j > forest.json\'\n@@ -141,6 +217,6 @@ commands =\n # Add extra configuration options in .meta.toml:\n # [tox]\n # extra_lines = """\n-# my_other_environment\n+# _your own configuration lines_\n # """\n ##\n' - -Repository: plone.app.dexterity - - -Branch: refs/heads/master -Date: 2024-05-20T20:56:34+02:00 -Author: Jens W. Klein (jensens) -Commit: https://github.com/plone/plone.app.dexterity/commit/53e4b93afc635c7816b044f80cb6f3a252e205ed - -move test to pasdiscussion - -Files changed: -M plone/app/dexterity/tests/test_doctests.py -D plone/app/dexterity/tests/discussion.txt - -b'diff --git a/plone/app/dexterity/tests/discussion.txt b/plone/app/dexterity/tests/discussion.txt\ndeleted file mode 100644\nindex 6b247cfa..00000000\n--- a/plone/app/dexterity/tests/discussion.txt\n+++ /dev/null\n@@ -1,106 +0,0 @@\n-Allow Discussion\n-================\n-\n-Test Setup\n-----------\n-\n-We create a dexterity content type that provides the allow discussion behavior::\n-\n- >>> portal = layer[\'portal\']\n- >>> from plone.dexterity.fti import DexterityFTI\n- >>> fti = DexterityFTI(\'discussiondocument\')\n- >>> fti.behaviors = (\'plone.app.dexterity.behaviors.discussion.IAllowDiscussion\',)\n- >>> portal.portal_types._setObject(\'discussiondocument\', fti)\n- \'discussiondocument\'\n-\n-Set up a test browser::\n-\n- >>> from plone.app.testing import TEST_USER_ID, TEST_USER_NAME, TEST_USER_PASSWORD, setRoles\n- >>> setRoles(portal, TEST_USER_ID, [\'Manager\'])\n- >>> import transaction; transaction.commit()\n- >>> from plone.testing.zope import Browser\n- >>> browser = Browser(layer[\'app\'])\n- >>> browser.addHeader(\'Authorization\', \'Basic %s:%s\' % (TEST_USER_NAME, TEST_USER_PASSWORD,))\n-\n-We have to make sure the request provides IDiscussonLayer because the enabled\n-method on the conversation calls conversation_view (which is only registered\n-for IDiscussionLayer).\n-\n- >>> from plone.app.discussion.interfaces import IDiscussionLayer\n- >>> from zope.interface import alsoProvides\n- >>> alsoProvides(portal.REQUEST, IDiscussionLayer)\n-\n-Add a document::\n-\n- >>> browser.open(\'http://nohost/plone/++add++discussiondocument\')\n-\n-\n-Default Allow Discussion Options\n---------------------------------\n-\n-There are three options for the allow discussion select field::\n-\n- >>> allowDiscussion = browser.getControl(\'Allow discussion\')\n- >>> allowDiscussion.options\n- [\'--NOVALUE--\', \'True\', \'False\']\n-\n-By default, no value is set for allow discussion::\n-\n- >>> browser.getControl(\'Allow discussion\').value\n- [\'--NOVALUE--\']\n- >>> browser.getControl(\'Save\').click()\n- >>> browser.url\n- \'http://nohost/plone/discussiondocument/view\'\n-\n-This means discussion is not enabled:\n-\n- >>> from plone.app.discussion.interfaces import IConversation\n- >>> conv = IConversation(portal.discussiondocument)\n- >>> conv.enabled()\n- False\n-\n-We have to globally enable discussion in order to be able to add comments::\n-\n- >>> from plone.registry.interfaces import IRegistry\n- >>> from zope.component import queryUtility\n- >>> from plone.app.discussion.interfaces import IDiscussionSettings\n- >>> registry = queryUtility(IRegistry)\n- >>> settings = registry.forInterface(IDiscussionSettings)\n- >>> settings.globally_enabled = True\n-\n-Discussion is still disabled for our content object though::\n-\n- >>> from plone.app.discussion.interfaces import IConversation\n- >>> conv = IConversation(portal.discussiondocument)\n- >>> conv.enabled()\n- False\n-\n-This is because discussion is disabled by default for the document content\n-type::\n-\n- >>> fti.allow_discussion\n- False\n-\n-If we allow discussion for the \'Document\' content type, the conversation\n-for our content object is enabled because it just uses the default setting\n-(because allow_discussion is set to None)::\n-\n- >>> fti.allow_discussion = True\n- >>> from plone.app.discussion.interfaces import IConversation\n- >>> conv = IConversation(portal.discussiondocument)\n- >>> conv.enabled()\n- True\n-\n-We can now override the default value (True) by explicitly setting allow discussion to False::\n-\n- >>> browser.open(\'http://nohost/plone/discussiondocument/edit\')\n- >>> allowDiscussion = browser.getControl(\'Allow discussion\')\n- >>> allowDiscussion.value = [\'False\']\n- >>> browser.getControl(\'Save\').click()\n-\n-Discussion on our content object is now not enabled::\n-\n- >>> from plone.app.discussion.interfaces import IConversation\n- >>> conv = IConversation(portal.discussiondocument)\n- >>> conv.enabled()\n- False\ndiff --git a/plone/app/dexterity/tests/test_doctests.py b/plone/app/dexterity/tests/test_doctests.py\nindex 6b361554..a84ddd14 100644\n--- a/plone/app/dexterity/tests/test_doctests.py\n+++ b/plone/app/dexterity/tests/test_doctests.py\n@@ -6,7 +6,6 @@\n \n \n tests = (\n- "discussion.txt",\n "editing.rst",\n "metadata.txt",\n "nextprevious.txt",\n' - -Repository: plone.app.dexterity - - -Branch: refs/heads/master -Date: 2024-06-03T11:52:14-04:00 -Author: Jens W. Klein (jensens) -Commit: https://github.com/plone/plone.app.dexterity/commit/c98e60c8256e7e7bacf43dcc54e96fc5a81c5738 - -Merge branch 'master' into pa-discussion-coreaddon - -Files changed: -M CHANGES.rst -M setup.py -D news/+meta.internal -D news/387.bugfix -D news/6e36bcc4.internal - -b'diff --git a/CHANGES.rst b/CHANGES.rst\nindex 9e3fbfd2..729d9e29 100644\n--- a/CHANGES.rst\n+++ b/CHANGES.rst\n@@ -8,6 +8,22 @@ Changelog\n \n .. towncrier release notes start\n \n+4.0.0 (2024-05-30)\n+------------------\n+\n+Bug fixes:\n+\n+\n+- Remove a DeprecationWarning from a moved p.a.z3cform.widgets import. [@jensens] (#387)\n+\n+\n+Internal:\n+\n+\n+- Update configuration files.\n+ [plone devs] (6e36bcc4)\n+\n+\n 3.2.0 (2023-11-03)\n ------------------\n \ndiff --git a/news/+meta.internal b/news/+meta.internal\ndeleted file mode 100644\nindex c08f5399..00000000\n--- a/news/+meta.internal\n+++ /dev/null\n@@ -1,2 +0,0 @@\n-Update configuration files.\n-[plone devs]\ndiff --git a/news/387.bugfix b/news/387.bugfix\ndeleted file mode 100644\nindex d2a9273b..00000000\n--- a/news/387.bugfix\n+++ /dev/null\n@@ -1 +0,0 @@\n-Remove a DeprecationWarning from a moved p.a.z3cform.widgets import. [@jensens]\n\\ No newline at end of file\ndiff --git a/news/6e36bcc4.internal b/news/6e36bcc4.internal\ndeleted file mode 100644\nindex c08f5399..00000000\n--- a/news/6e36bcc4.internal\n+++ /dev/null\n@@ -1,2 +0,0 @@\n-Update configuration files.\n-[plone devs]\ndiff --git a/setup.py b/setup.py\nindex baf48695..87a884a4 100644\n--- a/setup.py\n+++ b/setup.py\n@@ -3,7 +3,7 @@\n from setuptools import setup\n \n \n-version = "4.0.0.dev0"\n+version = "4.0.1.dev0"\n \n short_description = (\n "Dexterity is a content type framework for CMF applications, "\n' - -Repository: plone.app.dexterity - - -Branch: refs/heads/master -Date: 2024-06-13T21:34:22+02:00 +Date: 2024-08-13T18:51:41+02:00 Author: Maurits van Rees (mauritsvanrees) -Commit: https://github.com/plone/plone.app.dexterity/commit/be543ca4d6600adffaf2e1fbf45da147fafb4ff6 +Commit: https://github.com/plone/plone.app.upgrade/commit/100cf0e4c061c8c22130f06e523150f39a2f9563 + +6.1: Cleanup plone.app.discussion settings when the package is not available. -Remove plone.app.discussion from test extras. +If the site contains comments, we throw an error and stop the upgrade. +The advice then is to add the `plone.app.discussion` package. Files changed: -M setup.py +A news/211.bugfix +A plone/app/upgrade/v61/profiles/uninstall-discussion/actions.xml +A plone/app/upgrade/v61/profiles/uninstall-discussion/browserlayer.xml +A plone/app/upgrade/v61/profiles/uninstall-discussion/catalog.xml +A plone/app/upgrade/v61/profiles/uninstall-discussion/controlpanel.xml +A plone/app/upgrade/v61/profiles/uninstall-discussion/registry.xml +A plone/app/upgrade/v61/profiles/uninstall-discussion/types.xml +A plone/app/upgrade/v61/profiles/uninstall-discussion/workflows.xml +M plone/app/upgrade/v61/configure.zcml +M plone/app/upgrade/v61/final.py +M plone/app/upgrade/v61/profiles.zcml -b'diff --git a/setup.py b/setup.py\nindex 87a884a4..a490eaf0 100644\n--- a/setup.py\n+++ b/setup.py\n@@ -88,7 +88,6 @@\n "test": [\n "plone.app.robotframework",\n "plone.app.testing",\n- "plone.app.discussion",\n "plone.i18n",\n "plone.testing",\n "robotsuite",\n' +b'diff --git a/news/211.bugfix b/news/211.bugfix\nnew file mode 100644\nindex 00000000..f0d647ee\n--- /dev/null\n+++ b/news/211.bugfix\n@@ -0,0 +1,4 @@\n+6.1: Cleanup ``plone.app.discussion`` settings when the package is not available.\n+If the site contains comments, we throw an error and stop the upgrade.\n+The advice then is to add the ``plone.app.discussion`` package.\n+[maurits]\ndiff --git a/plone/app/upgrade/v61/configure.zcml b/plone/app/upgrade/v61/configure.zcml\nindex c20e0358..b941668c 100644\n--- a/plone/app/upgrade/v61/configure.zcml\n+++ b/plone/app/upgrade/v61/configure.zcml\n@@ -71,8 +71,8 @@\n >\n \n \n \n \ndiff --git a/plone/app/upgrade/v61/final.py b/plone/app/upgrade/v61/final.py\nindex f3fa8ef2..38e5446f 100644\n--- a/plone/app/upgrade/v61/final.py\n+++ b/plone/app/upgrade/v61/final.py\n@@ -1,4 +1,12 @@\n+from plone.registry.interfaces import IRegistry\n+from zope.component import getUtility\n+from Acquisition import aq_parent\n+from importlib.metadata import distribution\n+from importlib.metadata import PackageNotFoundError\n+from plone.app.upgrade.utils import loadMigrationProfile\n+from plone.browserlayer.utils import unregister_layer\n from Products.CMFCore.utils import getToolByName\n+from zope.component import getSiteManager\n \n import logging\n \n@@ -21,3 +29,77 @@ def remove_portal_properties_tool(context):\n # AttributeError: \'PropertiesTool\' object has no attribute \'__of__\'\n portal._delOb("portal_properties")\n logger.info("Removed portal_properties tool.")\n+\n+\n+def maybe_cleanup_discussion(context):\n+ """Cleanup some left-overs from plone.app.discussion.\n+\n+ But only do this when the package is not available.\n+ In Plone 6.1, the package was made into a core add-on.\n+ Meaning: it no longer gets pulled in by Products.CMFPlone,\n+ but only by the Plone package.\n+ """\n+ # First check if the GS profile was installed.\n+ profile_id = "plone.app.discussion:default"\n+ if context.getLastVersionForProfile(profile_id) == "unknown":\n+ logger.info("%s was not installed, nothing to do.", profile_id)\n+ return\n+ try:\n+ distribution("plone.app.discussion")\n+ logger.info("The plone.app.discussion package is available, so we do nothing.")\n+ return\n+ except PackageNotFoundError:\n+ logger.info("plone.app.discussion package not found, will cleanup.")\n+\n+ # First check if there are any actual discussion items in the site.\n+ catalog = getToolByName(context, "portal_catalog")\n+ brains = catalog.unrestrictedSearchResults(portal_type="Discussion Item")\n+ total = len(brains)\n+ if total:\n+ raise ValueError(\n+ f"{total} Discussion Items (comments) were found in the site, but "\n+ "plone.app.discussion is missing.\\nThis package is optional since "\n+ "Plone 6.1.\\nPlease add plone.app.discussion to your Plone installation "\n+ "if you want to keep using them."\n+ )\n+\n+ # First apply a profile. This is mostly a copy of the uninstall profile\n+ # of plone.app.discussion.\n+ loadMigrationProfile(context, "profile-plone.app.upgrade.v61:uninstall-discussion")\n+\n+ # The registry keys were registered via the IDiscussionSettings interface\n+ # which no longer exists, so we remove them one by one.\n+ registry = getUtility(IRegistry)\n+ records = registry.records\n+ to_remove = [\n+ key\n+ for key in records.keys()\n+ if "plone.app.discussion.interfaces.IDiscussionSettings" in key\n+ ]\n+ for key in to_remove:\n+ del records[key]\n+ logger.info("Removed all IDiscussionSettings registry records.")\n+\n+ # Gather the FTIs that have the plone.allowdiscussion behavior.\n+ # It can appear with the name or the interface identifier.\n+ portal_types = getToolByName(context, "portal_types")\n+ old_behaviors = {\n+ "plone.allowdiscussion",\n+ "plone.app.dexterity.behaviors.discussion.IAllowDiscussion",\n+ }\n+ ftis_to_fix = (\n+ fti\n+ for fti in portal_types.objectValues("Dexterity FTI")\n+ if set(fti.behaviors) & old_behaviors\n+ )\n+ for fti in ftis_to_fix:\n+ # Remove the behavior. Remember this is a tuple.\n+ behaviors = [\n+ behavior for behavior in fti.behaviors if behavior not in old_behaviors\n+ ]\n+ # Set the updated behaviors\n+ fti.behaviors = tuple(behaviors)\n+ logger.info("Removed plone.allowdiscussion behavior from %s", fti)\n+\n+ # Mark GS profile as not installed/activated.\n+ context.unsetLastVersionForProfile(profile_id)\ndiff --git a/plone/app/upgrade/v61/profiles.zcml b/plone/app/upgrade/v61/profiles.zcml\nindex 1dd84f37..74cac9b2 100644\n--- a/plone/app/upgrade/v61/profiles.zcml\n+++ b/plone/app/upgrade/v61/profiles.zcml\n@@ -11,4 +11,13 @@\n provides="Products.GenericSetup.interfaces.EXTENSION"\n />\n \n+ \n+ \n+\n \ndiff --git a/plone/app/upgrade/v61/profiles/uninstall-discussion/actions.xml b/plone/app/upgrade/v61/profiles/uninstall-discussion/actions.xml\nnew file mode 100644\nindex 00000000..680e5e96\n--- /dev/null\n+++ b/plone/app/upgrade/v61/profiles/uninstall-discussion/actions.xml\n@@ -0,0 +1,13 @@\n+\n+\n+ \n+ \n+ \n+\ndiff --git a/plone/app/upgrade/v61/profiles/uninstall-discussion/browserlayer.xml b/plone/app/upgrade/v61/profiles/uninstall-discussion/browserlayer.xml\nnew file mode 100644\nindex 00000000..078a4cbb\n--- /dev/null\n+++ b/plone/app/upgrade/v61/profiles/uninstall-discussion/browserlayer.xml\n@@ -0,0 +1,6 @@\n+\n+\n+ \n+\ndiff --git a/plone/app/upgrade/v61/profiles/uninstall-discussion/catalog.xml b/plone/app/upgrade/v61/profiles/uninstall-discussion/catalog.xml\nnew file mode 100644\nindex 00000000..e4584ca6\n--- /dev/null\n+++ b/plone/app/upgrade/v61/profiles/uninstall-discussion/catalog.xml\n@@ -0,0 +1,28 @@\n+\n+\n+\n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+\n+\ndiff --git a/plone/app/upgrade/v61/profiles/uninstall-discussion/controlpanel.xml b/plone/app/upgrade/v61/profiles/uninstall-discussion/controlpanel.xml\nnew file mode 100644\nindex 00000000..f76f5d75\n--- /dev/null\n+++ b/plone/app/upgrade/v61/profiles/uninstall-discussion/controlpanel.xml\n@@ -0,0 +1,14 @@\n+\n+\n+\n+ \n+\n+\ndiff --git a/plone/app/upgrade/v61/profiles/uninstall-discussion/registry.xml b/plone/app/upgrade/v61/profiles/uninstall-discussion/registry.xml\nnew file mode 100644\nindex 00000000..b5109eae\n--- /dev/null\n+++ b/plone/app/upgrade/v61/profiles/uninstall-discussion/registry.xml\n@@ -0,0 +1,8 @@\n+\n+\n+ \n+ \n+\ndiff --git a/plone/app/upgrade/v61/profiles/uninstall-discussion/types.xml b/plone/app/upgrade/v61/profiles/uninstall-discussion/types.xml\nnew file mode 100644\nindex 00000000..e854062b\n--- /dev/null\n+++ b/plone/app/upgrade/v61/profiles/uninstall-discussion/types.xml\n@@ -0,0 +1,9 @@\n+\n+\n+ \n+\ndiff --git a/plone/app/upgrade/v61/profiles/uninstall-discussion/workflows.xml b/plone/app/upgrade/v61/profiles/uninstall-discussion/workflows.xml\nnew file mode 100644\nindex 00000000..b7269a77\n--- /dev/null\n+++ b/plone/app/upgrade/v61/profiles/uninstall-discussion/workflows.xml\n@@ -0,0 +1,13 @@\n+\n+\n+ \n+ \n+\n' -Repository: plone.app.dexterity +Repository: plone.app.upgrade Branch: refs/heads/master -Date: 2024-09-02T16:35:07+02:00 -Author: Maurits van Rees (mauritsvanrees) -Commit: https://github.com/plone/plone.app.dexterity/commit/37908607d37d1dab26e4566a7bdf52831de688e9 +Date: 2024-09-02T16:35:17+02:00 +Author: Maurits van Rees (mauritsvanrees) +Commit: https://github.com/plone/plone.app.upgrade/commit/97c18165d6a88e813cf1682db1f8b3c2a7d3c4a2 -Merge pull request #371 from plone/pa-discussion-coreaddon +Merge pull request #330 from plone/pa-discussion-core-addon -move plone.allowdiscussion behavior to plone.app.discussion +6.1: Cleanup plone.app.discussion settings when package is missing Files changed: -A news/371.bugfix -M plone/app/dexterity/behaviors/configure.zcml -M plone/app/dexterity/behaviors/discussion.py -M plone/app/dexterity/tests/test_doctests.py -M setup.py -D plone/app/dexterity/tests/discussion.txt +A news/211.bugfix +A plone/app/upgrade/v61/profiles/uninstall-discussion/actions.xml +A plone/app/upgrade/v61/profiles/uninstall-discussion/browserlayer.xml +A plone/app/upgrade/v61/profiles/uninstall-discussion/catalog.xml +A plone/app/upgrade/v61/profiles/uninstall-discussion/controlpanel.xml +A plone/app/upgrade/v61/profiles/uninstall-discussion/registry.xml +A plone/app/upgrade/v61/profiles/uninstall-discussion/types.xml +A plone/app/upgrade/v61/profiles/uninstall-discussion/workflows.xml +M plone/app/upgrade/v61/configure.zcml +M plone/app/upgrade/v61/final.py +M plone/app/upgrade/v61/profiles.zcml -b'diff --git a/news/371.bugfix b/news/371.bugfix\nnew file mode 100644\nindex 00000000..5f63bea4\n--- /dev/null\n+++ b/news/371.bugfix\n@@ -0,0 +1,3 @@\n+`plone.app.discussion` is now a core addon.\n+The `plone.discussion` behavior class was moved over there.\n+[@jensens]\ndiff --git a/plone/app/dexterity/behaviors/configure.zcml b/plone/app/dexterity/behaviors/configure.zcml\nindex aee0d089..e5c09ccb 100644\n--- a/plone/app/dexterity/behaviors/configure.zcml\n+++ b/plone/app/dexterity/behaviors/configure.zcml\n@@ -113,14 +113,6 @@\n provides=".nextprevious.INextPreviousToggle"\n />\n \n- \n- \n-\n \n \n >> portal = layer[\'portal\']\n- >>> from plone.dexterity.fti import DexterityFTI\n- >>> fti = DexterityFTI(\'discussiondocument\')\n- >>> fti.behaviors = (\'plone.app.dexterity.behaviors.discussion.IAllowDiscussion\',)\n- >>> portal.portal_types._setObject(\'discussiondocument\', fti)\n- \'discussiondocument\'\n-\n-Set up a test browser::\n-\n- >>> from plone.app.testing import TEST_USER_ID, TEST_USER_NAME, TEST_USER_PASSWORD, setRoles\n- >>> setRoles(portal, TEST_USER_ID, [\'Manager\'])\n- >>> import transaction; transaction.commit()\n- >>> from plone.testing.zope import Browser\n- >>> browser = Browser(layer[\'app\'])\n- >>> browser.addHeader(\'Authorization\', \'Basic %s:%s\' % (TEST_USER_NAME, TEST_USER_PASSWORD,))\n-\n-We have to make sure the request provides IDiscussonLayer because the enabled\n-method on the conversation calls conversation_view (which is only registered\n-for IDiscussionLayer).\n-\n- >>> from plone.app.discussion.interfaces import IDiscussionLayer\n- >>> from zope.interface import alsoProvides\n- >>> alsoProvides(portal.REQUEST, IDiscussionLayer)\n-\n-Add a document::\n-\n- >>> browser.open(\'http://nohost/plone/++add++discussiondocument\')\n-\n-\n-Default Allow Discussion Options\n---------------------------------\n-\n-There are three options for the allow discussion select field::\n-\n- >>> allowDiscussion = browser.getControl(\'Allow discussion\')\n- >>> allowDiscussion.options\n- [\'--NOVALUE--\', \'True\', \'False\']\n-\n-By default, no value is set for allow discussion::\n-\n- >>> browser.getControl(\'Allow discussion\').value\n- [\'--NOVALUE--\']\n- >>> browser.getControl(\'Save\').click()\n- >>> browser.url\n- \'http://nohost/plone/discussiondocument/view\'\n-\n-This means discussion is not enabled:\n-\n- >>> from plone.app.discussion.interfaces import IConversation\n- >>> conv = IConversation(portal.discussiondocument)\n- >>> conv.enabled()\n- False\n-\n-We have to globally enable discussion in order to be able to add comments::\n-\n- >>> from plone.registry.interfaces import IRegistry\n- >>> from zope.component import queryUtility\n- >>> from plone.app.discussion.interfaces import IDiscussionSettings\n- >>> registry = queryUtility(IRegistry)\n- >>> settings = registry.forInterface(IDiscussionSettings)\n- >>> settings.globally_enabled = True\n-\n-Discussion is still disabled for our content object though::\n-\n- >>> from plone.app.discussion.interfaces import IConversation\n- >>> conv = IConversation(portal.discussiondocument)\n- >>> conv.enabled()\n- False\n-\n-This is because discussion is disabled by default for the document content\n-type::\n-\n- >>> fti.allow_discussion\n- False\n-\n-If we allow discussion for the \'Document\' content type, the conversation\n-for our content object is enabled because it just uses the default setting\n-(because allow_discussion is set to None)::\n-\n- >>> fti.allow_discussion = True\n- >>> from plone.app.discussion.interfaces import IConversation\n- >>> conv = IConversation(portal.discussiondocument)\n- >>> conv.enabled()\n- True\n-\n-We can now override the default value (True) by explicitly setting allow discussion to False::\n-\n- >>> browser.open(\'http://nohost/plone/discussiondocument/edit\')\n- >>> allowDiscussion = browser.getControl(\'Allow discussion\')\n- >>> allowDiscussion.value = [\'False\']\n- >>> browser.getControl(\'Save\').click()\n-\n-Discussion on our content object is now not enabled::\n-\n- >>> from plone.app.discussion.interfaces import IConversation\n- >>> conv = IConversation(portal.discussiondocument)\n- >>> conv.enabled()\n- False\ndiff --git a/plone/app/dexterity/tests/test_doctests.py b/plone/app/dexterity/tests/test_doctests.py\nindex 6b361554..a84ddd14 100644\n--- a/plone/app/dexterity/tests/test_doctests.py\n+++ b/plone/app/dexterity/tests/test_doctests.py\n@@ -6,7 +6,6 @@\n \n \n tests = (\n- "discussion.txt",\n "editing.rst",\n "metadata.txt",\n "nextprevious.txt",\ndiff --git a/setup.py b/setup.py\nindex 87a884a4..a490eaf0 100644\n--- a/setup.py\n+++ b/setup.py\n@@ -88,7 +88,6 @@\n "test": [\n "plone.app.robotframework",\n "plone.app.testing",\n- "plone.app.discussion",\n "plone.i18n",\n "plone.testing",\n "robotsuite",\n' +b'diff --git a/news/211.bugfix b/news/211.bugfix\nnew file mode 100644\nindex 00000000..f0d647ee\n--- /dev/null\n+++ b/news/211.bugfix\n@@ -0,0 +1,4 @@\n+6.1: Cleanup ``plone.app.discussion`` settings when the package is not available.\n+If the site contains comments, we throw an error and stop the upgrade.\n+The advice then is to add the ``plone.app.discussion`` package.\n+[maurits]\ndiff --git a/plone/app/upgrade/v61/configure.zcml b/plone/app/upgrade/v61/configure.zcml\nindex c20e0358..b941668c 100644\n--- a/plone/app/upgrade/v61/configure.zcml\n+++ b/plone/app/upgrade/v61/configure.zcml\n@@ -71,8 +71,8 @@\n >\n \n \n \n \ndiff --git a/plone/app/upgrade/v61/final.py b/plone/app/upgrade/v61/final.py\nindex f3fa8ef2..38e5446f 100644\n--- a/plone/app/upgrade/v61/final.py\n+++ b/plone/app/upgrade/v61/final.py\n@@ -1,4 +1,12 @@\n+from plone.registry.interfaces import IRegistry\n+from zope.component import getUtility\n+from Acquisition import aq_parent\n+from importlib.metadata import distribution\n+from importlib.metadata import PackageNotFoundError\n+from plone.app.upgrade.utils import loadMigrationProfile\n+from plone.browserlayer.utils import unregister_layer\n from Products.CMFCore.utils import getToolByName\n+from zope.component import getSiteManager\n \n import logging\n \n@@ -21,3 +29,77 @@ def remove_portal_properties_tool(context):\n # AttributeError: \'PropertiesTool\' object has no attribute \'__of__\'\n portal._delOb("portal_properties")\n logger.info("Removed portal_properties tool.")\n+\n+\n+def maybe_cleanup_discussion(context):\n+ """Cleanup some left-overs from plone.app.discussion.\n+\n+ But only do this when the package is not available.\n+ In Plone 6.1, the package was made into a core add-on.\n+ Meaning: it no longer gets pulled in by Products.CMFPlone,\n+ but only by the Plone package.\n+ """\n+ # First check if the GS profile was installed.\n+ profile_id = "plone.app.discussion:default"\n+ if context.getLastVersionForProfile(profile_id) == "unknown":\n+ logger.info("%s was not installed, nothing to do.", profile_id)\n+ return\n+ try:\n+ distribution("plone.app.discussion")\n+ logger.info("The plone.app.discussion package is available, so we do nothing.")\n+ return\n+ except PackageNotFoundError:\n+ logger.info("plone.app.discussion package not found, will cleanup.")\n+\n+ # First check if there are any actual discussion items in the site.\n+ catalog = getToolByName(context, "portal_catalog")\n+ brains = catalog.unrestrictedSearchResults(portal_type="Discussion Item")\n+ total = len(brains)\n+ if total:\n+ raise ValueError(\n+ f"{total} Discussion Items (comments) were found in the site, but "\n+ "plone.app.discussion is missing.\\nThis package is optional since "\n+ "Plone 6.1.\\nPlease add plone.app.discussion to your Plone installation "\n+ "if you want to keep using them."\n+ )\n+\n+ # First apply a profile. This is mostly a copy of the uninstall profile\n+ # of plone.app.discussion.\n+ loadMigrationProfile(context, "profile-plone.app.upgrade.v61:uninstall-discussion")\n+\n+ # The registry keys were registered via the IDiscussionSettings interface\n+ # which no longer exists, so we remove them one by one.\n+ registry = getUtility(IRegistry)\n+ records = registry.records\n+ to_remove = [\n+ key\n+ for key in records.keys()\n+ if "plone.app.discussion.interfaces.IDiscussionSettings" in key\n+ ]\n+ for key in to_remove:\n+ del records[key]\n+ logger.info("Removed all IDiscussionSettings registry records.")\n+\n+ # Gather the FTIs that have the plone.allowdiscussion behavior.\n+ # It can appear with the name or the interface identifier.\n+ portal_types = getToolByName(context, "portal_types")\n+ old_behaviors = {\n+ "plone.allowdiscussion",\n+ "plone.app.dexterity.behaviors.discussion.IAllowDiscussion",\n+ }\n+ ftis_to_fix = (\n+ fti\n+ for fti in portal_types.objectValues("Dexterity FTI")\n+ if set(fti.behaviors) & old_behaviors\n+ )\n+ for fti in ftis_to_fix:\n+ # Remove the behavior. Remember this is a tuple.\n+ behaviors = [\n+ behavior for behavior in fti.behaviors if behavior not in old_behaviors\n+ ]\n+ # Set the updated behaviors\n+ fti.behaviors = tuple(behaviors)\n+ logger.info("Removed plone.allowdiscussion behavior from %s", fti)\n+\n+ # Mark GS profile as not installed/activated.\n+ context.unsetLastVersionForProfile(profile_id)\ndiff --git a/plone/app/upgrade/v61/profiles.zcml b/plone/app/upgrade/v61/profiles.zcml\nindex 1dd84f37..74cac9b2 100644\n--- a/plone/app/upgrade/v61/profiles.zcml\n+++ b/plone/app/upgrade/v61/profiles.zcml\n@@ -11,4 +11,13 @@\n provides="Products.GenericSetup.interfaces.EXTENSION"\n />\n \n+ \n+ \n+\n \ndiff --git a/plone/app/upgrade/v61/profiles/uninstall-discussion/actions.xml b/plone/app/upgrade/v61/profiles/uninstall-discussion/actions.xml\nnew file mode 100644\nindex 00000000..680e5e96\n--- /dev/null\n+++ b/plone/app/upgrade/v61/profiles/uninstall-discussion/actions.xml\n@@ -0,0 +1,13 @@\n+\n+\n+ \n+ \n+ \n+\ndiff --git a/plone/app/upgrade/v61/profiles/uninstall-discussion/browserlayer.xml b/plone/app/upgrade/v61/profiles/uninstall-discussion/browserlayer.xml\nnew file mode 100644\nindex 00000000..078a4cbb\n--- /dev/null\n+++ b/plone/app/upgrade/v61/profiles/uninstall-discussion/browserlayer.xml\n@@ -0,0 +1,6 @@\n+\n+\n+ \n+\ndiff --git a/plone/app/upgrade/v61/profiles/uninstall-discussion/catalog.xml b/plone/app/upgrade/v61/profiles/uninstall-discussion/catalog.xml\nnew file mode 100644\nindex 00000000..e4584ca6\n--- /dev/null\n+++ b/plone/app/upgrade/v61/profiles/uninstall-discussion/catalog.xml\n@@ -0,0 +1,28 @@\n+\n+\n+\n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+\n+\ndiff --git a/plone/app/upgrade/v61/profiles/uninstall-discussion/controlpanel.xml b/plone/app/upgrade/v61/profiles/uninstall-discussion/controlpanel.xml\nnew file mode 100644\nindex 00000000..f76f5d75\n--- /dev/null\n+++ b/plone/app/upgrade/v61/profiles/uninstall-discussion/controlpanel.xml\n@@ -0,0 +1,14 @@\n+\n+\n+\n+ \n+\n+\ndiff --git a/plone/app/upgrade/v61/profiles/uninstall-discussion/registry.xml b/plone/app/upgrade/v61/profiles/uninstall-discussion/registry.xml\nnew file mode 100644\nindex 00000000..b5109eae\n--- /dev/null\n+++ b/plone/app/upgrade/v61/profiles/uninstall-discussion/registry.xml\n@@ -0,0 +1,8 @@\n+\n+\n+ \n+ \n+\ndiff --git a/plone/app/upgrade/v61/profiles/uninstall-discussion/types.xml b/plone/app/upgrade/v61/profiles/uninstall-discussion/types.xml\nnew file mode 100644\nindex 00000000..e854062b\n--- /dev/null\n+++ b/plone/app/upgrade/v61/profiles/uninstall-discussion/types.xml\n@@ -0,0 +1,9 @@\n+\n+\n+ \n+\ndiff --git a/plone/app/upgrade/v61/profiles/uninstall-discussion/workflows.xml b/plone/app/upgrade/v61/profiles/uninstall-discussion/workflows.xml\nnew file mode 100644\nindex 00000000..b7269a77\n--- /dev/null\n+++ b/plone/app/upgrade/v61/profiles/uninstall-discussion/workflows.xml\n@@ -0,0 +1,13 @@\n+\n+\n+ \n+ \n+\n'