diff --git a/last_commit.txt b/last_commit.txt index 13fe7a3b87..4e2c2bcf5e 100644 --- a/last_commit.txt +++ b/last_commit.txt @@ -1,427 +1,198 @@ -Repository: Products.CMFPlone +Repository: plone.app.dexterity Branch: refs/heads/master -Date: 2024-05-15T15:33:14+02:00 +Date: 2023-06-06T18:14:52+02:00 Author: Jens W. Klein (jensens) -Commit: https://github.com/plone/Products.CMFPlone/commit/6709e3a0630ea21837f28b5581255e154952183f +Commit: https://github.com/plone/plone.app.dexterity/commit/bb8f2c8a892f00364006e8ffef95f98a417ab981 -rip out plone.app.discussion +move plone.allowdiscussion behavior to plone.app.discussion Files changed: -M Products/CMFPlone/CatalogTool.py -M Products/CMFPlone/factory.py -M Products/CMFPlone/profiles/default/types.xml -M Products/CMFPlone/profiles/default/workflows.xml -M Products/CMFPlone/profiles/dependencies/metadata.xml -M Products/CMFPlone/tests/testMigrationTool.py -M Products/CMFPlone/tests/testPortalCreation.py -M Products/CMFPlone/tests/testSecurity.py -M setup.py -D Products/CMFPlone/profiles/default/types/Discussion_Item.xml - -b'diff --git a/Products/CMFPlone/CatalogTool.py b/Products/CMFPlone/CatalogTool.py\nindex 1b45f330a1..723d71e8b9 100644\n--- a/Products/CMFPlone/CatalogTool.py\n+++ b/Products/CMFPlone/CatalogTool.py\n@@ -10,7 +10,6 @@\n from BTrees.Length import Length\n from DateTime import DateTime\n from OFS.interfaces import IOrderedContainer\n-from plone.app.discussion.interfaces import DISCUSSION_ANNOTATION_KEY\n from plone.base.interfaces import INonStructuralFolder\n from plone.base.interfaces import IPloneCatalogTool\n from plone.base.utils import base_hasattr\n@@ -46,6 +45,13 @@\n \n \n logger = logging.getLogger("Plone")\n+try:\n+ from plone.app.discussion.interfaces import DISCUSSION_ANNOTATION_KEY\n+except ImportError: # pragma: no cover\n+ DISCUSSION_ANNOTATION_KEY = None\n+\n+\n+logger = logging.getLogger(\'Plone\')\n \n _marker = object()\n \n@@ -455,9 +461,13 @@ def indexObject(obj, path):\n ):\n try:\n self.reindexObject(obj, idxs=idxs)\n- # index conversions from plone.app.discussion\n+ # index conversations from plone.app.discussion\n annotions = IAnnotations(obj)\n- if DISCUSSION_ANNOTATION_KEY in annotions:\n+ if (\n+ DISCUSSION_ANNOTATION_KEY is not None\n+ and DISCUSSION_ANNOTATION_KEY in annotions\n+\n+ ):\n conversation = annotions[DISCUSSION_ANNOTATION_KEY]\n conversation = conversation.__of__(obj)\n for comment in conversation.getComments():\ndiff --git a/Products/CMFPlone/factory.py b/Products/CMFPlone/factory.py\nindex e536921af7..6473f9c4f5 100644\n--- a/Products/CMFPlone/factory.py\n+++ b/Products/CMFPlone/factory.py\n@@ -62,7 +62,6 @@ def getNonInstallableProducts(self):\n "borg.localrole",\n "plone.app.caching",\n "plone.app.dexterity",\n- "plone.app.discussion",\n "plone.app.event",\n "plone.app.intid",\n "plone.app.linkintegrity",\n@@ -106,7 +105,6 @@ def getNonInstallableProfiles(self):\n "plone.protect:default",\n "plone.app.contenttypes:default",\n "plone.app.dexterity:default",\n- "plone.app.discussion:default",\n "plone.app.event:default",\n "plone.app.linkintegrity:default",\n "plone.app.registry:default",\ndiff --git a/Products/CMFPlone/profiles/default/types.xml b/Products/CMFPlone/profiles/default/types.xml\nindex ea78dfcba6..55bf830c30 100644\n--- a/Products/CMFPlone/profiles/default/types.xml\n+++ b/Products/CMFPlone/profiles/default/types.xml\n@@ -3,9 +3,6 @@\n name="portal_types"\n >\n Controls the available content types in your portal\n- \n \ndiff --git a/Products/CMFPlone/profiles/default/types/Discussion_Item.xml b/Products/CMFPlone/profiles/default/types/Discussion_Item.xml\ndeleted file mode 100644\nindex d3c321ac3e..0000000000\n--- a/Products/CMFPlone/profiles/default/types/Discussion_Item.xml\n+++ /dev/null\n@@ -1,49 +0,0 @@\n-\n-\n- \n- Discussion Items are documents which reply to other content.\n- They should *not* be addable through the standard \'folder_factories\' interface.\n- string:${portal_url}/discussionitem_icon.png\n- Discussion Item\n- \n- \n- \n- True\n- True\n- \n- False\n- \n- \n- \n- \n- \n- \n- \n- \n-\ndiff --git a/Products/CMFPlone/profiles/default/workflows.xml b/Products/CMFPlone/profiles/default/workflows.xml\nindex d9b00d3aae..e1efd4185f 100644\n--- a/Products/CMFPlone/profiles/default/workflows.xml\n+++ b/Products/CMFPlone/profiles/default/workflows.xml\n@@ -38,7 +38,6 @@\n \n \n \n- \n \n \n \ndiff --git a/Products/CMFPlone/profiles/dependencies/metadata.xml b/Products/CMFPlone/profiles/dependencies/metadata.xml\nindex 191b32119e..a00e76d0ac 100644\n--- a/Products/CMFPlone/profiles/dependencies/metadata.xml\n+++ b/Products/CMFPlone/profiles/dependencies/metadata.xml\n@@ -6,7 +6,6 @@\n profile-Products.PortalTransforms:PortalTransforms\n profile-Products.CMFEditions:CMFEditions\n profile-Products.PlonePAS:PlonePAS\n- profile-plone.app.discussion:default\n profile-plone.app.linkintegrity:default\n profile-plone.app.registry:default\n profile-plone.app.theming:default\ndiff --git a/Products/CMFPlone/tests/testMigrationTool.py b/Products/CMFPlone/tests/testMigrationTool.py\nindex 342a0856d5..142be2c5ef 100644\n--- a/Products/CMFPlone/tests/testMigrationTool.py\n+++ b/Products/CMFPlone/tests/testMigrationTool.py\n@@ -177,14 +177,13 @@ def test_upgrade_all(self):\n \n # real ones:\n cmfeditions = Addon(profile_id="Products.CMFEditions:CMFEditions")\n- discussion = Addon(profile_id="plone.app.discussion:default")\n # real one with failing check_module:\n dexterity = Addon(\n profile_id="plone.app.dexterity:default", check_module="no.such.module"\n )\n # non-existing one:\n foo = Addon(profile_id="foo")\n- addonlist = AddonList([cmfeditions, discussion, dexterity, foo])\n+ addonlist = AddonList([cmfeditions, dexterity, foo])\n # Calling it should give no errors.\n addonlist.upgrade_all(self.portal)\n \n@@ -196,9 +195,6 @@ def test_upgrade_all(self):\n # Now mess with the profile versions.\n setup.setLastVersionForProfile(cmfeditions.profile_id, "2.0")\n setup.setLastVersionForProfile(dexterity.profile_id, "0.1")\n- # \'unknown\' needs special handling, otherwise the version will\n- # become a tuple (\'unknown\',):\n- setup._profile_upgrade_versions[discussion.profile_id] = "unknown"\n \n # Run the upgrade again.\n addonlist.upgrade_all(self.portal)\n@@ -228,28 +224,23 @@ def test_plone_addonlist_upgrade_all(self):\n # Several addons did not get fully upgraded in the past, which\n # is why this list was created.\n cmfeditions_id = "Products.CMFEditions:CMFEditions"\n- discussion_id = "plone.app.discussion:default"\n querystring_id = "plone.app.querystring:default"\n # Note the current versions.\n setup = getToolByName(self.portal, "portal_setup")\n getversion = setup.getLastVersionForProfile\n cmfeditions_version = getversion(cmfeditions_id)\n- discussion_version = getversion(discussion_id)\n querystring_version = getversion(querystring_id)\n # Check that they are not unknown\n self.assertNotEqual(cmfeditions_version, "unknown")\n- self.assertNotEqual(discussion_version, "unknown")\n self.assertNotEqual(querystring_version, "unknown")\n # So let\'s mess with some profile versions. We get some older\n # versions that really exist.\n setversion = setup.setLastVersionForProfile\n setversion(cmfeditions_id, "2.0")\n- setversion(discussion_id, "100")\n setversion(querystring_id, "7")\n # Check that it worked, that the profile versions really are\n # different.\n self.assertNotEqual(cmfeditions_version, getversion(cmfeditions_id))\n- self.assertNotEqual(discussion_version, getversion(discussion_id))\n self.assertNotEqual(querystring_version, getversion(querystring_id))\n \n # Run the upgrade.\n@@ -258,5 +249,4 @@ def test_plone_addonlist_upgrade_all(self):\n # Check that it worked, that the profiles are now at their\n # original versions.\n self.assertEqual(cmfeditions_version, getversion(cmfeditions_id))\n- self.assertEqual(discussion_version, getversion(discussion_id))\n self.assertEqual(querystring_version, getversion(querystring_id))\ndiff --git a/Products/CMFPlone/tests/testPortalCreation.py b/Products/CMFPlone/tests/testPortalCreation.py\nindex ea0e56fc1e..208e60c3eb 100644\n--- a/Products/CMFPlone/tests/testPortalCreation.py\n+++ b/Products/CMFPlone/tests/testPortalCreation.py\n@@ -608,13 +608,6 @@ def testMemberHasViewGroupsPermission(self):\n ][0]\n self.assertTrue(member_has_permission["selected"])\n \n- def testDiscussionItemWorkflow(self):\n- # By default the discussion item has the comment_one_state_workflow\n- self.assertEqual(\n- self.workflow.getChainForPortalType("Discussion Item"),\n- ("comment_one_state_workflow",),\n- )\n-\n def testFolderHasFolderListingView(self):\n # Folder type should allow \'folder_listing\'\n self.assertTrue("listing_view" in self.types.Folder.view_methods)\ndiff --git a/Products/CMFPlone/tests/testSecurity.py b/Products/CMFPlone/tests/testSecurity.py\nindex 2a895be962..379fca60f3 100644\n--- a/Products/CMFPlone/tests/testSecurity.py\n+++ b/Products/CMFPlone/tests/testSecurity.py\n@@ -71,7 +71,7 @@ def test_gtbn_funcglobals(self):\n self.fail("getToolByName should block access to non CMF tools")\n \n def test_widget_traversal_1(self):\n- res = self.publish("/plone/@@discussion-settings/++widget++moderator_email")\n+ res = self.publish("/plone/@@mail-controlpanel/++widget++email_from_address")\n self.assertEqual(302, res.status)\n self.assertTrue(\n res.headers["location"].startswith(\n@@ -81,8 +81,7 @@ def test_widget_traversal_1(self):\n \n def test_widget_traversal_2(self):\n res = self.publish(\n- "/plone/@@discussion-settings/++widget++captcha/terms/field/interface/setTaggedValue?tag=cake&value=lovely"\n- )\n+ "/plone/@@mail-controlpanel/++widget++email_from_address/terms/field/interface/setTaggedValue?tag=cake&value=lovely")\n self.assertEqual(404, res.status)\n # self.assertTrue(res.headers[\'location\'].startswith(\n # \'http://nohost/plone/acl_users/credentials_cookie_auth/require_login\'))\ndiff --git a/setup.py b/setup.py\nindex 23eda3836d..ac11203d1d 100644\n--- a/setup.py\n+++ b/setup.py\n@@ -61,7 +61,6 @@\n "plone.app.contenttypes",\n "plone.app.customerize",\n "plone.app.dexterity",\n- "plone.app.discussion",\n "plone.app.i18n",\n "plone.app.layout >= 2.5.15",\n "plone.app.linkintegrity >=1.0.3",\n' - -Repository: Products.CMFPlone - - -Branch: refs/heads/master -Date: 2024-05-15T15:33:14+02:00 -Author: Jens W. Klein (jensens) -Commit: https://github.com/plone/Products.CMFPlone/commit/017c8d190b1b7472fce4122ca293443818ed5b8a - -do not enable behavior plone.allowdiscussion - -Files changed: -M Products/CMFPlone/profiles/default/types/Plone_Site.xml - -b'diff --git a/Products/CMFPlone/profiles/default/types/Plone_Site.xml b/Products/CMFPlone/profiles/default/types/Plone_Site.xml\nindex f6b324d8e1..b7cdf1c072 100644\n--- a/Products/CMFPlone/profiles/default/types/Plone_Site.xml\n+++ b/Products/CMFPlone/profiles/default/types/Plone_Site.xml\n@@ -38,7 +38,6 @@\n \n \n \n- \n \n \n \n' - -Repository: Products.CMFPlone - - -Branch: refs/heads/master -Date: 2024-05-15T15:33:14+02:00 -Author: Jens W. Klein (jensens) -Commit: https://github.com/plone/Products.CMFPlone/commit/c937837e483c5f45b07c850664c31432ebb878e8 - -sort permissions in test - -Files changed: -M Products/CMFPlone/tests/testSiteAdminRole.py - -b'diff --git a/Products/CMFPlone/tests/testSiteAdminRole.py b/Products/CMFPlone/tests/testSiteAdminRole.py\nindex 836c9c2992..fb7069916f 100644\n--- a/Products/CMFPlone/tests/testSiteAdminRole.py\n+++ b/Products/CMFPlone/tests/testSiteAdminRole.py\n@@ -18,8 +18,8 @@ def testExpectedPermissions(self):\n "Access contents information": 1,\n "Access inactive portal content": 1,\n "Add Accelerated HTTP Cache Managers": 0,\n- "Add BTreeFolder2s": 0,\n "Add Browser Id Manager": 0,\n+ "Add BTreeFolder2s": 0,\n "Add CMF Action Icons Tools": 0,\n "Add CMF Caching Policy Managers": 0,\n "Add CMF Calendar Tools": 0,\n@@ -52,15 +52,21 @@ def testExpectedPermissions(self):\n "Add Placeful Workflow Tools": 0,\n "Add Plone Language Tools": 0,\n "Add Plone Tools": 0,\n+ "Add plone.app.customerizes": 0,\n "Add PlonePAS Tools": 0,\n "Add Pluggable Index": 0,\n "Add Plugin Registrys": 0,\n+ "Add portal content": 1,\n+ "Add portal events": 1,\n+ "Add portal folders": 1,\n+ "Add portal member": 1,\n "Add PortalTransforms Tools": 0,\n "Add Python Scripts": 0,\n "Add RAM Cache Managers": 0,\n- "Add ReStructuredText Documents": 0,\n "Add Repositories": 0,\n "Add ResourceRegistries Tools": 0,\n+ "Add ReStructuredText Documents": 0,\n+ "Add secure MailHost objects": 0,\n "Add Session Data Manager": 0,\n "Add Site Roots": 0,\n "Add Temporary Folder": 0,\n@@ -72,36 +78,30 @@ def testExpectedPermissions(self):\n "Add Workflow Policy": 0,\n "Add ZCatalogs": 0,\n "Add ZODB Mount Points": 0,\n- "Add plone.app.customerizes": 0,\n- "Add portal content": 1,\n- "Add portal events": 1,\n- "Add portal folders": 1,\n- "Add portal member": 1,\n- "Add secure MailHost objects": 0,\n "Allow sendto": 1,\n- "CMFEditions: Access previous versions": 1,\n- "CMFEditions: Apply version control": 1,\n- "CMFEditions: Checkout to location": 1,\n- "CMFEditions: Manage versioning policies": 1,\n- "CMFEditions: Purge version": 1,\n- "CMFEditions: Revert to previous versions": 1,\n- "CMFEditions: Save new version": 1,\n+ "Change bindings": 0,\n "Change Browser Id Manager": 0,\n+ "Change cache managers": 0,\n+ "Change cache settings": 0,\n+ "Change configuration": 0,\n+ "Change Database Methods": 0,\n "Change DTML Documents": 0,\n "Change DTML Methods": 0,\n- "Change Database Methods": 0,\n "Change External Methods": 0,\n "Change Images and Files": 0,\n- "Change Page Templates": 0,\n- "Change Python Scripts": 0,\n- "Change Session Data Manager": 0,\n- "Change bindings": 0,\n- "Change cache managers": 0,\n- "Change cache settings": 0,\n- "Change configuration": 0,\n "Change local roles": 1,\n+ "Change Page Templates": 0,\n "Change permissions": 0,\n "Change proxy roles": 0,\n+ "Change Python Scripts": 0,\n+ "Change Session Data Manager": 0,\n+ "CMFEditions: Access previous versions": 1,\n+ "CMFEditions: Apply version control": 1,\n+ "CMFEditions: Checkout to location": 1,\n+ "CMFEditions: Manage versioning policies": 1,\n+ "CMFEditions: Purge version": 1,\n+ "CMFEditions: Revert to previous versions": 1,\n+ "CMFEditions: Save new version": 1,\n "Content rules: Manage rules": 1,\n "Copy or Move": 1,\n "Create Transient Objects": 0,\n@@ -109,8 +109,8 @@ def testExpectedPermissions(self):\n "Delete Groups": 0,\n "Delete objects": 1,\n "Edit ReStructuredText": 0,\n- "FTP access": 1,\n "Five: Add TTW View Template": 0,\n+ "FTP access": 1,\n "Import/Export objects": 0,\n "List folder contents": 1,\n "List portal members": 1,\n@@ -121,29 +121,32 @@ def testExpectedPermissions(self):\n "Manage Access Rules": 0,\n "Manage Five local sites": 0,\n "Manage Groups": 0,\n+ "Manage portal": 0,\n+ "Manage properties": 1,\n+ "Manage repositories": 0,\n "Manage Site": 0,\n "Manage Transient Object Container": 0,\n+ "Manage users": 0,\n "Manage Vocabulary": 0,\n "Manage WebDAV Locks": 0,\n "Manage ZCatalog Entries": 0,\n "Manage ZCatalogIndex Entries": 0,\n- "Manage portal": 0,\n- "Manage properties": 1,\n- "Manage repositories": 0,\n- "Manage users": 0,\n "Modify Cookie Crumblers": 0,\n "Modify portal content": 1,\n "Modify view template": 1,\n "Open/Close Database Connections": 0,\n "Plone Site Setup: Overview": 1,\n+ "plone.portlet.collection: Add collection portlet": 1,\n+ "plone.portlet.static: Add static portlet": 1,\n "Portlets: Manage own portlets": 1,\n "Portlets: Manage portlets": 1,\n "Portlets: View dashboard": 1,\n "Query Vocabulary": 0,\n+ "Reply to item": 0,\n "Request review": 1,\n "Review portal content": 1,\n- "Search ZCatalog": 1,\n "Search for principals": 0,\n+ "Search ZCatalog": 1,\n "Set Group Ownership": 0,\n "Set own password": 1,\n "Set own properties": 1,\n@@ -156,15 +159,12 @@ def testExpectedPermissions(self):\n "Undo changes": 1,\n "Use mailhost services": 1,\n "Use version control": 1,\n- "Reply to item": 0,\n- "View": 1,\n "View Groups": 1,\n "View management screens": 0,\n+ "View": 1,\n+ "WebDAV access": 1,\n "WebDAV Lock items": 1,\n "WebDAV Unlock items": 1,\n- "WebDAV access": 1,\n- "plone.portlet.collection: Add collection portlet": 1,\n- "plone.portlet.static: Add static portlet": 1,\n }\n try:\n import plone.app.iterate\n' - -Repository: Products.CMFPlone - - -Branch: refs/heads/master -Date: 2024-05-15T15:33:14+02:00 -Author: Jens W. Klein (jensens) -Commit: https://github.com/plone/Products.CMFPlone/commit/b51b016c3d45cd90026ba1805491f0c96f85c21f - -move check for 'Reply to item' permission to plone.app.discussion. - -Files changed: -M Products/CMFPlone/tests/testSiteAdminRole.py - -b'diff --git a/Products/CMFPlone/tests/testSiteAdminRole.py b/Products/CMFPlone/tests/testSiteAdminRole.py\nindex fb7069916f..ca0d7013b7 100644\n--- a/Products/CMFPlone/tests/testSiteAdminRole.py\n+++ b/Products/CMFPlone/tests/testSiteAdminRole.py\n@@ -142,7 +142,6 @@ def testExpectedPermissions(self):\n "Portlets: Manage portlets": 1,\n "Portlets: View dashboard": 1,\n "Query Vocabulary": 0,\n- "Reply to item": 0,\n "Request review": 1,\n "Review portal content": 1,\n "Search for principals": 0,\n' - -Repository: Products.CMFPlone - - -Branch: refs/heads/master -Date: 2024-05-15T15:47:24+02:00 -Author: Jens W. Klein (jensens) -Commit: https://github.com/plone/Products.CMFPlone/commit/d39797361dcefe681d3856fb6e7a5dbbf273343d - -switch test to use plone.app.event instead of plone.app.discusision as crash-test dummy - -Files changed: -M Products/CMFPlone/tests/testMigrationTool.py - -b'diff --git a/Products/CMFPlone/tests/testMigrationTool.py b/Products/CMFPlone/tests/testMigrationTool.py\nindex 142be2c5ef..d241051379 100644\n--- a/Products/CMFPlone/tests/testMigrationTool.py\n+++ b/Products/CMFPlone/tests/testMigrationTool.py\n@@ -177,13 +177,14 @@ def test_upgrade_all(self):\n \n # real ones:\n cmfeditions = Addon(profile_id="Products.CMFEditions:CMFEditions")\n+ pae = Addon(profile_id="plone.app.event:default")\n # real one with failing check_module:\n dexterity = Addon(\n profile_id="plone.app.dexterity:default", check_module="no.such.module"\n )\n # non-existing one:\n foo = Addon(profile_id="foo")\n- addonlist = AddonList([cmfeditions, dexterity, foo])\n+ addonlist = AddonList([cmfeditions, pae, dexterity, foo])\n # Calling it should give no errors.\n addonlist.upgrade_all(self.portal)\n \n@@ -195,6 +196,9 @@ def test_upgrade_all(self):\n # Now mess with the profile versions.\n setup.setLastVersionForProfile(cmfeditions.profile_id, "2.0")\n setup.setLastVersionForProfile(dexterity.profile_id, "0.1")\n+ # \'unknown\' needs special handling, otherwise the version will\n+ # become a tuple (\'unknown\',):\n+ setup._profile_upgrade_versions[pae.profile_id] = "unknown"\n \n # Run the upgrade again.\n addonlist.upgrade_all(self.portal)\n@@ -204,10 +208,10 @@ def test_upgrade_all(self):\n self.assertEqual(\n setup.getLastVersionForProfile(cmfeditions.profile_id), cmfeditions_version\n )\n- # We had set discussion to unknown, so it will not have been\n+ # We had set pae to unknown, so it will not have been\n # upgraded:\n self.assertEqual(\n- setup.getLastVersionForProfile(discussion.profile_id), "unknown"\n+ setup.getLastVersionForProfile(pae.profile_id), "unknown"\n )\n # We had given dexterity a failing check_module, so it will\n # not have been upgraded:\n@@ -224,23 +228,28 @@ def test_plone_addonlist_upgrade_all(self):\n # Several addons did not get fully upgraded in the past, which\n # is why this list was created.\n cmfeditions_id = "Products.CMFEditions:CMFEditions"\n+ pae_id = "plone.app.event:default"\n querystring_id = "plone.app.querystring:default"\n # Note the current versions.\n setup = getToolByName(self.portal, "portal_setup")\n getversion = setup.getLastVersionForProfile\n cmfeditions_version = getversion(cmfeditions_id)\n+ pae_version = getversion(pae_id)\n querystring_version = getversion(querystring_id)\n # Check that they are not unknown\n self.assertNotEqual(cmfeditions_version, "unknown")\n+ self.assertNotEqual(pae_version, "unknown")\n self.assertNotEqual(querystring_version, "unknown")\n # So let\'s mess with some profile versions. We get some older\n # versions that really exist.\n setversion = setup.setLastVersionForProfile\n setversion(cmfeditions_id, "2.0")\n+ setversion(pae_id, "100")\n setversion(querystring_id, "7")\n # Check that it worked, that the profile versions really are\n # different.\n self.assertNotEqual(cmfeditions_version, getversion(cmfeditions_id))\n+ self.assertNotEqual(pae_version, getversion(pae_id))\n self.assertNotEqual(querystring_version, getversion(querystring_id))\n \n # Run the upgrade.\n@@ -249,4 +258,5 @@ def test_plone_addonlist_upgrade_all(self):\n # Check that it worked, that the profiles are now at their\n # original versions.\n self.assertEqual(cmfeditions_version, getversion(cmfeditions_id))\n+ self.assertEqual(pae_version, getversion(pae_id))\n self.assertEqual(querystring_version, getversion(querystring_id))\n' - -Repository: Products.CMFPlone - - -Branch: refs/heads/master -Date: 2024-05-15T15:52:19+02:00 -Author: Jens W. Klein (jensens) -Commit: https://github.com/plone/Products.CMFPlone/commit/44b6e9f1727a7d911814f30d8fc561070e683a09 - -do not ttest 3 times, skip pad - -Files changed: -M Products/CMFPlone/tests/testMigrationTool.py - -b'diff --git a/Products/CMFPlone/tests/testMigrationTool.py b/Products/CMFPlone/tests/testMigrationTool.py\nindex d241051379..f8895a40e0 100644\n--- a/Products/CMFPlone/tests/testMigrationTool.py\n+++ b/Products/CMFPlone/tests/testMigrationTool.py\n@@ -228,28 +228,23 @@ def test_plone_addonlist_upgrade_all(self):\n # Several addons did not get fully upgraded in the past, which\n # is why this list was created.\n cmfeditions_id = "Products.CMFEditions:CMFEditions"\n- pae_id = "plone.app.event:default"\n querystring_id = "plone.app.querystring:default"\n # Note the current versions.\n setup = getToolByName(self.portal, "portal_setup")\n getversion = setup.getLastVersionForProfile\n cmfeditions_version = getversion(cmfeditions_id)\n- pae_version = getversion(pae_id)\n querystring_version = getversion(querystring_id)\n # Check that they are not unknown\n self.assertNotEqual(cmfeditions_version, "unknown")\n- self.assertNotEqual(pae_version, "unknown")\n self.assertNotEqual(querystring_version, "unknown")\n # So let\'s mess with some profile versions. We get some older\n # versions that really exist.\n setversion = setup.setLastVersionForProfile\n setversion(cmfeditions_id, "2.0")\n- setversion(pae_id, "100")\n setversion(querystring_id, "7")\n # Check that it worked, that the profile versions really are\n # different.\n self.assertNotEqual(cmfeditions_version, getversion(cmfeditions_id))\n- self.assertNotEqual(pae_version, getversion(pae_id))\n self.assertNotEqual(querystring_version, getversion(querystring_id))\n \n # Run the upgrade.\n@@ -258,5 +253,4 @@ def test_plone_addonlist_upgrade_all(self):\n # Check that it worked, that the profiles are now at their\n # original versions.\n self.assertEqual(cmfeditions_version, getversion(cmfeditions_id))\n- self.assertEqual(pae_version, getversion(pae_id))\n self.assertEqual(querystring_version, getversion(querystring_id))\n' - -Repository: Products.CMFPlone +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/Products.CMFPlone/commit/8c485391b2c370b50687f1fe7a18813b610d14b5 - -Merge branch 'master' into pa-discussion-core-addon - -Files changed: -A news/3949.bugfix -A news/3960.bugfix -M Products/CMFPlone/browser/navigation.py -M Products/CMFPlone/browser/navtree.py -M Products/CMFPlone/browser/search.py -M Products/CMFPlone/browser/syndication/adapters.py -M Products/CMFPlone/browser/syndication/templates/search-rss.pt -M Products/CMFPlone/browser/syndication/views.py -M Products/CMFPlone/controlpanel/tests/test_controlpanel_browser_redirection.py -M Products/CMFPlone/tests/testNavTree.py -M Products/CMFPlone/tests/testSecurity.py -M Products/CMFPlone/tests/testSecurityDeclarations.py -D Products/CMFPlone/skins/plone_scripts/getFolderContents.py -D Products/CMFPlone/skins/plone_scripts/queryCatalog.py -D Products/CMFPlone/tests/testQueryCatalog.py - -b'diff --git a/Products/CMFPlone/browser/navigation.py b/Products/CMFPlone/browser/navigation.py\nindex e6799e20b7..39266987f2 100644\n--- a/Products/CMFPlone/browser/navigation.py\n+++ b/Products/CMFPlone/browser/navigation.py\n@@ -2,10 +2,10 @@\n from Acquisition import aq_inner\n from plone.app.layout.navigation.interfaces import INavtreeStrategy\n from plone.app.layout.navigation.navtree import buildFolderTree\n-from plone.app.layout.navigation.root import getNavigationRoot\n from plone.base.defaultpage import check_default_page_via_view\n from plone.base.interfaces import IHideFromBreadcrumbs\n from plone.base.interfaces import INavigationSchema\n+from plone.base.navigationroot import get_navigation_root\n from plone.base.utils import pretty_title_or_id\n from plone.base.utils import safe_callable\n from plone.registry.interfaces import IRegistry\n@@ -79,7 +79,7 @@ def _getNavQuery(self):\n else:\n query = {}\n \n- query["path"] = {"query": getNavigationRoot(self.context), "depth": 1}\n+ query["path"] = {"query": get_navigation_root(self.context), "depth": 1}\n query["portal_type"] = [t for t in navigation_settings.displayed_types]\n query["sort_on"] = navigation_settings.sort_tabs_on\n if navigation_settings.sort_tabs_reversed:\n@@ -189,7 +189,7 @@ def breadcrumbs(self):\n dec_result = [(len(r.getPath()), r) for r in rawresult]\n dec_result.sort()\n \n- rootPath = getNavigationRoot(context)\n+ rootPath = get_navigation_root(context)\n \n # Build result dict\n result = []\n@@ -234,7 +234,7 @@ def breadcrumbs(self):\n if IHideFromBreadcrumbs.providedBy(context):\n return base\n \n- rootPath = getNavigationRoot(context)\n+ rootPath = get_navigation_root(context)\n itemPath = "/".join(context.getPhysicalPath())\n \n # don\'t show default pages in breadcrumbs or pages above the navigation\ndiff --git a/Products/CMFPlone/browser/navtree.py b/Products/CMFPlone/browser/navtree.py\nindex d161ad83c7..75e7c79ffb 100644\n--- a/Products/CMFPlone/browser/navtree.py\n+++ b/Products/CMFPlone/browser/navtree.py\n@@ -7,8 +7,8 @@\n from plone.app.layout.navigation.interfaces import INavigationQueryBuilder\n from plone.app.layout.navigation.interfaces import INavtreeStrategy\n from plone.app.layout.navigation.navtree import NavtreeStrategyBase\n-from plone.app.layout.navigation.root import getNavigationRoot\n from plone.base.interfaces import INavigationSchema\n+from plone.base.navigationroot import get_navigation_root\n from plone.i18n.normalizer.interfaces import IIDNormalizer\n from plone.registry.interfaces import IRegistry\n from Products.CMFCore.utils import getToolByName\n@@ -44,7 +44,7 @@ def __init__(self, context):\n \n # Construct the path query\n \n- rootPath = getNavigationRoot(context)\n+ rootPath = get_navigation_root(context)\n currentPath = "/".join(context.getPhysicalPath())\n \n # If we are above the navigation root, a navtree query would return\n@@ -115,7 +115,7 @@ def __init__(self, context, view=None):\n )\n \n self.showAllParents = True\n- self.rootPath = getNavigationRoot(context)\n+ self.rootPath = get_navigation_root(context)\n \n membership = getToolByName(context, "portal_membership")\n self.memberId = membership.getAuthenticatedMember().getId()\n@@ -196,7 +196,7 @@ def __init__(self, context, view=None):\n if view is not None:\n self.rootPath = view.navigationTreeRootPath()\n else:\n- self.rootPath = getNavigationRoot(context)\n+ self.rootPath = get_navigation_root(context)\n \n def subtreeFilter(self, node):\n sitemapDecision = SitemapNavtreeStrategy.subtreeFilter(self, node)\ndiff --git a/Products/CMFPlone/browser/search.py b/Products/CMFPlone/browser/search.py\nindex 84503505dd..ae4d6cbe27 100644\n--- a/Products/CMFPlone/browser/search.py\n+++ b/Products/CMFPlone/browser/search.py\n@@ -1,12 +1,12 @@\n from DateTime import DateTime\n from plone.app.contentlisting.interfaces import IContentListing\n-from plone.app.layout.navigation.interfaces import INavigationRoot\n from plone.base.batch import Batch\n+from plone.base.interfaces import INavigationRoot\n from plone.base.interfaces import ISearchSchema\n from plone.base.interfaces.siteroot import IPloneSiteRoot\n+from plone.base.navigationroot import get_navigation_root\n from plone.registry.interfaces import IRegistry\n from Products.CMFCore.utils import getToolByName\n-from Products.CMFPlone.browser.navtree import getNavigationRoot\n from Products.ZCTextIndex.ParseTree import ParseError\n from zope.cachedescriptors.property import Lazy as lazy_property\n from zope.component import getMultiAdapter\n@@ -147,7 +147,7 @@ def _filter_query(self, query):\n query["show_inactive"] = False\n # respect navigation root if we\'re not at the site root.\n if "path" not in query and not IPloneSiteRoot.providedBy(self.context):\n- query["path"] = getNavigationRoot(self.context)\n+ query["path"] = get_navigation_root(self.context)\n \n if "sort_order" in query and not query["sort_order"]:\n del query["sort_order"]\ndiff --git a/Products/CMFPlone/browser/syndication/adapters.py b/Products/CMFPlone/browser/syndication/adapters.py\nindex 4d51bb1507..2a97b9d69d 100644\n--- a/Products/CMFPlone/browser/syndication/adapters.py\n+++ b/Products/CMFPlone/browser/syndication/adapters.py\n@@ -1,10 +1,13 @@\n from DateTime import DateTime\n from OFS.interfaces import IItem\n from plone.app.contenttypes.behaviors.leadimage import ILeadImageBehavior\n+from plone.base.batch import Batch\n+from plone.base.interfaces import IPloneSiteRoot\n from plone.base.interfaces.syndication import IFeed\n from plone.base.interfaces.syndication import IFeedItem\n from plone.base.interfaces.syndication import IFeedSettings\n from plone.base.interfaces.syndication import ISearchFeed\n+from plone.base.navigationroot import get_navigation_root_object\n from plone.dexterity.interfaces import IDexterityContent\n from plone.namedfile.interfaces import INamedField\n from plone.registry.interfaces import IRegistry\n@@ -154,21 +157,27 @@ def language(self):\n \n class CollectionFeed(FolderFeed):\n def _brains(self):\n+ # call the collection query method as defined in\n+ # plone.app.contenttypes.interfaces.ICollection\n+ # usually implemented at plone.aapp.contenttypes.item.collection\n return self.context.queryCatalog(batch=False)[: self.limit]\n \n \n @implementer(ISearchFeed)\n class SearchFeed(FolderFeed):\n def _brains(self):\n- max_items = self.limit\n request = self.context.REQUEST\n+ navroot = get_navigation_root_object(self.context, IPloneSiteRoot(self.context))\n+ catalog = getToolByName(self.context, "portal_catalog")\n+ query = {\n+ "path": {"query": navroot.absolute_url_path(), "depth": 1},\n+ "sort_order": "reverse",\n+ "sort_on": request.get("sort_on", "effective"),\n+ }\n+ result = catalog(**query)\n start = int(request.get("b_start", 0))\n- end = int(request.get("b_end", start + max_items))\n- request.set("sort_order", "reverse")\n- request.set("sort_on", request.get("sort_on", "effective"))\n- return self.context.queryCatalog(\n- show_all=1, use_types_blacklist=True, use_navigation_root=True\n- )[start:end]\n+ end = int(request.get("b_end", start + self.limit))\n+ return Batch(result, start, end)\n \n \n @adapter(IItem, IFeed)\ndiff --git a/Products/CMFPlone/browser/syndication/templates/search-rss.pt b/Products/CMFPlone/browser/syndication/templates/search-rss.pt\nindex 513e757697..cd21ca32ff 100644\n--- a/Products/CMFPlone/browser/syndication/templates/search-rss.pt\n+++ b/Products/CMFPlone/browser/syndication/templates/search-rss.pt\n@@ -8,11 +8,11 @@\n xmlns:tal="http://xml.zope.org/namespaces/tal"\n tal:define="syn context/@@syndication-util;">\n \n-\n \n- The title\n- http://url.to.portal\n+ The title\n+ http://url.to.portal\n \n \n \n@@ -29,7 +29,7 @@\n \n \n \n- Title\n+ Title\n Identifier\n Description\n -Commit: https://github.com/plone/Products.CMFPlone/commit/6d1c641f03c3ebb842b6918ecdfca9e7db9bce24 - -add change log entry +Commit: https://github.com/plone/plone.app.dexterity/commit/3778d627d8ea5b75843a0db06cff168a51b574a7 -Files changed: -A news/3782.bugfix - -b'diff --git a/news/3782.bugfix b/news/3782.bugfix\nnew file mode 100644\nindex 0000000000..959bc31b2b\n--- /dev/null\n+++ b/news/3782.bugfix\n@@ -0,0 +1,2 @@\n+Turn plone.app.discussion in a core-addon.\n+[@jensens]\n' - -Repository: Products.CMFPlone - - -Branch: refs/heads/master -Date: 2024-05-22T13:48:26+02:00 -Author: Jens W. Klein (jensens) -Commit: https://github.com/plone/Products.CMFPlone/commit/d4e6a0d3d0af56909dabcaf36cfa59ab27900c79 +Update plone/app/dexterity/behaviors/discussion.py -no offensive wording +Co-authored-by: David Glick <david@glicksoftware.com> Files changed: -M Products/CMFPlone/PloneTool.py -M Products/CMFPlone/controlpanel/browser/types.py -M Products/CMFPlone/controlpanel/tests/test_controlpanel_browser_navigation.py -M Products/CMFPlone/controlpanel/tests/test_controlpanel_browser_search.py -M Products/CMFPlone/profiles/default/types/TempFolder.xml -M Products/CMFPlone/tests/testPloneTool.py -M Products/CMFPlone/tests/testSearch.py +M plone/app/dexterity/behaviors/discussion.py -b'diff --git a/Products/CMFPlone/PloneTool.py b/Products/CMFPlone/PloneTool.py\nindex 886d93ff63..21fdaf37c9 100644\n--- a/Products/CMFPlone/PloneTool.py\n+++ b/Products/CMFPlone/PloneTool.py\n@@ -758,7 +758,7 @@ def acquireLocalRoles(self, obj, status=1, REQUEST=None):\n # If status is 1, allow acquisition of local roles (regular\n # behaviour).\n # If it\'s 0, prohibit it (it will allow some kind of local role\n- # blacklisting).\n+ # denylisting).\n mt = getToolByName(self, "portal_membership")\n if not mt.checkPermission(ModifyPortalContent, obj):\n raise Unauthorized\n@@ -937,7 +937,7 @@ def getUserFriendlyTypes(self, typesList=None):\n typesList = []\n registry = getUtility(IRegistry)\n search_settings = registry.forInterface(ISearchSchema, prefix="plone")\n- blacklistedTypes = search_settings.types_not_searched\n+ denylistedTypes = search_settings.types_not_searched\n \n ttool = getToolByName(self, "portal_types")\n tool_types = ttool.keys()\n@@ -946,7 +946,7 @@ def getUserFriendlyTypes(self, typesList=None):\n else:\n types = tool_types\n \n- friendlyTypes = set(types) - set(blacklistedTypes)\n+ friendlyTypes = set(types) - set(denylistedTypes)\n return list(friendlyTypes)\n \n @security.public\ndiff --git a/Products/CMFPlone/controlpanel/browser/types.py b/Products/CMFPlone/controlpanel/browser/types.py\nindex 2e4361594b..09f38ef9b0 100644\n--- a/Products/CMFPlone/controlpanel/browser/types.py\n+++ b/Products/CMFPlone/controlpanel/browser/types.py\n@@ -169,12 +169,12 @@ def __call__(self):\n \n searchable = form.get("searchable", False)\n site_settings = registry.forInterface(ISearchSchema, prefix="plone")\n- blacklisted = [i for i in site_settings.types_not_searched]\n- if searchable and type_id in blacklisted:\n- blacklisted.remove(type_id)\n- elif not searchable and type_id not in blacklisted:\n- blacklisted.append(type_id)\n- site_settings.types_not_searched = tuple(blacklisted)\n+ denylisted = [i for i in site_settings.types_not_searched]\n+ if searchable and type_id in denylisted:\n+ denylisted.remove(type_id)\n+ elif not searchable and type_id not in denylisted:\n+ denylisted.append(type_id)\n+ site_settings.types_not_searched = tuple(denylisted)\n \n default_page_type = form.get("default_page_type", False)\n types_settings = registry.forInterface(ITypesSchema, prefix="plone")\n@@ -307,8 +307,8 @@ def current_versioning_policy(self):\n def is_searchable(self):\n registry = getUtility(IRegistry)\n settings = registry.forInterface(ISearchSchema, prefix="plone")\n- blacklisted = settings.types_not_searched\n- return self.type_id not in blacklisted\n+ denylisted = settings.types_not_searched\n+ return self.type_id not in denylisted\n \n def is_default_page_type(self):\n registry = getUtility(IRegistry)\ndiff --git a/Products/CMFPlone/controlpanel/tests/test_controlpanel_browser_navigation.py b/Products/CMFPlone/controlpanel/tests/test_controlpanel_browser_navigation.py\nindex bfc28b52f4..0865585b51 100644\n--- a/Products/CMFPlone/controlpanel/tests/test_controlpanel_browser_navigation.py\n+++ b/Products/CMFPlone/controlpanel/tests/test_controlpanel_browser_navigation.py\n@@ -80,7 +80,6 @@ def test_displayed_types(self):\n settings = registry.forInterface(INavigationSchema, prefix="plone")\n self.browser.open("%s/@@navigation-controlpanel" % self.portal_url)\n self.browser.getControl("Collection", index=0).selected = True\n- self.browser.getControl("Comment").selected = True\n self.browser.getControl("Event").selected = True\n self.browser.getControl("File").selected = True\n self.browser.getControl("Folder").selected = True\n@@ -91,7 +90,6 @@ def test_displayed_types(self):\n self.browser.getControl("Save").click()\n \n self.assertTrue("Collection" in settings.displayed_types)\n- self.assertTrue("Discussion Item" in settings.displayed_types)\n self.assertTrue("Event" in settings.displayed_types)\n self.assertTrue("File" in settings.displayed_types)\n self.assertTrue("Folder" in settings.displayed_types)\ndiff --git a/Products/CMFPlone/controlpanel/tests/test_controlpanel_browser_search.py b/Products/CMFPlone/controlpanel/tests/test_controlpanel_browser_search.py\nindex f2cc9bf507..f0b6c31daa 100644\n--- a/Products/CMFPlone/controlpanel/tests/test_controlpanel_browser_search.py\n+++ b/Products/CMFPlone/controlpanel/tests/test_controlpanel_browser_search.py\n@@ -58,12 +58,10 @@ def test_enable_livesearch(self):\n def test_types_not_searched(self):\n self.browser.open("%s/@@search-controlpanel" % self.portal_url)\n self.browser.getControl(name="form.widgets.types_not_searched:list").value = [\n- "Discussion Item",\n "News Item",\n ]\n self.browser.getControl("Save").click()\n \n registry = getUtility(IRegistry)\n settings = registry.forInterface(ISearchSchema, prefix="plone")\n- self.assertTrue("Discussion Item" in settings.types_not_searched)\n self.assertTrue("News Item" in settings.types_not_searched)\ndiff --git a/Products/CMFPlone/profiles/default/types/TempFolder.xml b/Products/CMFPlone/profiles/default/types/TempFolder.xml\nindex aa3e58c625..3fd51aa7e3 100644\n--- a/Products/CMFPlone/profiles/default/types/TempFolder.xml\n+++ b/Products/CMFPlone/profiles/default/types/TempFolder.xml\n@@ -18,7 +18,6 @@\n False\n False\n \n- \n \n \n \ndiff --git a/Products/CMFPlone/tests/testPloneTool.py b/Products/CMFPlone/tests/testPloneTool.py\nindex 712dbb04aa..3e3be2c3ea 100644\n--- a/Products/CMFPlone/tests/testPloneTool.py\n+++ b/Products/CMFPlone/tests/testPloneTool.py\n@@ -134,17 +134,17 @@ def testGetUserFriendlyTypes(self):\n types = set(ttool.keys())\n registry = getUtility(IRegistry)\n search_settings = registry.forInterface(ISearchSchema, prefix="plone")\n- blacklistedTypes = search_settings.types_not_searched\n+ denylistedTypes = search_settings.types_not_searched\n \n- # \'ChangeSet\' is blacklisted, but not in the types by default,\n+ # \'ChangeSet\' is denylisted, but not in the types by default,\n # so we filter that out.\n- blacklistedTypes = {t for t in blacklistedTypes if t in types}\n+ denylistedTypes = {t for t in denylistedTypes if t in types}\n # No black listed types should be returned.\n self.assertEqual(\n- [t for t in self.utils.getUserFriendlyTypes() if t in blacklistedTypes], []\n+ [t for t in self.utils.getUserFriendlyTypes() if t in denylistedTypes], []\n )\n self.assertEqual(\n- len(self.utils.getUserFriendlyTypes()), len(types) - len(blacklistedTypes)\n+ len(self.utils.getUserFriendlyTypes()), len(types) - len(denylistedTypes)\n )\n # Non-existing types should be filtered out.\n self.assertEqual(self.utils.getUserFriendlyTypes(["File"]), ["File"])\ndiff --git a/Products/CMFPlone/tests/testSearch.py b/Products/CMFPlone/tests/testSearch.py\nindex e1d00b8567..7c8eb6b483 100644\n--- a/Products/CMFPlone/tests/testSearch.py\n+++ b/Products/CMFPlone/tests/testSearch.py\n@@ -135,8 +135,8 @@ def crumbs(item):\n title = crumbs(second_level_folder.third_level_document)[0]["Title"]\n self.assertEqual(title, "First Level Folder")\n \n- def test_blacklisted_types_in_results(self):\n- """Make sure we don\'t break types\' blacklisting in the new search\n+ def test_Denylisted_types_in_results(self):\n+ """Make sure we don\'t break types\' Denylisting in the new search\n results view.\n """\n portal = self.layer["portal"]\n@@ -154,7 +154,7 @@ def test_blacklisted_types_in_results(self):\n res = portal.restrictedTraverse("@@search").results(query=q, batch=False)\n self.assertFalse(\n "my-page1" in [r.getId() for r in res],\n- \'Blacklisted type "Document" has been found in search results.\',\n+ \'Denylisted type "Document" has been found in search results.\',\n )\n \n def test_default_search_order_relevance(self):\n' +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: Products.CMFPlone +Repository: plone.app.dexterity Branch: refs/heads/master -Date: 2024-05-23T11:50:31+02:00 +Date: 2023-07-13T14:55:09+02:00 Author: Jens W. Klein (jensens) -Commit: https://github.com/plone/Products.CMFPlone/commit/4de205c6c8ec3cdbd8b122049896264a6fcac36e +Commit: https://github.com/plone/plone.app.dexterity/commit/bed36a6aeffbf90594613c8b35cfdccd2472201c -Merge branch 'master' into pa-discussion-core-addon +Merge branch 'master' into pa-discussion-coreaddon Files changed: -A news/3756.bugfix -A news/3962.bugfix -M Products/CMFPlone/browser/navtree.py -M Products/CMFPlone/controlpanel/browser/error_log_form.py +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/Products/CMFPlone/browser/navtree.py b/Products/CMFPlone/browser/navtree.py\nindex 75e7c79ffb..36a59e22bb 100644\n--- a/Products/CMFPlone/browser/navtree.py\n+++ b/Products/CMFPlone/browser/navtree.py\n@@ -9,12 +9,12 @@\n from plone.app.layout.navigation.navtree import NavtreeStrategyBase\n from plone.base.interfaces import INavigationSchema\n from plone.base.navigationroot import get_navigation_root\n+from plone.base.utils import pretty_title_or_id\n+from plone.base.utils import safe_callable\n from plone.i18n.normalizer.interfaces import IIDNormalizer\n from plone.registry.interfaces import IRegistry\n from Products.CMFCore.utils import getToolByName\n-from Products.CMFPlone import utils\n from zope.component import getUtility\n-from zope.component import queryUtility\n from zope.interface import implementer\n \n \n@@ -32,12 +32,12 @@ class NavtreeQueryBuilder:\n """Build a navtree query based on the settings in navtree_properties"""\n \n def __init__(self, context):\n- registry = getUtility(IRegistry)\n- navigation_settings = registry.forInterface(INavigationSchema, prefix="plone")\n+ self.registry = getUtility(IRegistry)\n+ navigation_settings = self.registry.forInterface(INavigationSchema, prefix="plone")\n \n # Acquire a custom nav query if available\n customQuery = getattr(context, "getCustomNavQuery", None)\n- if customQuery is not None and utils.safe_callable(customQuery):\n+ if customQuery is not None and safe_callable(customQuery):\n query = customQuery()\n else:\n query = {}\n@@ -62,7 +62,7 @@ def __init__(self, context):\n # seem to work with EPI.\n \n # Only list the applicable types\n- query["portal_type"] = utils.typesToList(context)\n+ query["portal_type"] = self.registry.get("plone.displayed_types", ())\n \n # Apply the desired sort\n sortAttribute = navigation_settings.sort_tabs_on\n@@ -87,10 +87,9 @@ class SitemapQueryBuilder(NavtreeQueryBuilder):\n """Build a folder tree query suitable for a sitemap"""\n \n def __init__(self, context):\n- NavtreeQueryBuilder.__init__(self, context)\n+ super().__init__(context)\n portal_url = getToolByName(context, "portal_url")\n- registry = getUtility(IRegistry)\n- sitemap_depth = registry.get("plone.sitemap_depth", 3)\n+ sitemap_depth = self.registry.get("plone.sitemap_depth", 3)\n self.query["path"] = {\n "query": portal_url.getPortalPath(),\n "depth": sitemap_depth,\n@@ -149,7 +148,7 @@ def decoratorFactory(self, node):\n if isFolderish and (portalType is None or portalType not in self.parentTypesNQ):\n showChildren = True\n \n- newNode["Title"] = utils.pretty_title_or_id(context, item)\n+ newNode["Title"] = pretty_title_or_id(context, item)\n newNode["id"] = item.getId\n newNode["UID"] = item.UID\n newNode["absolute_url"] = itemUrl\n@@ -169,7 +168,7 @@ def decoratorFactory(self, node):\n newNode["getRemoteUrl"] and newNode["Creator"] != self.memberId\n )\n \n- idnormalizer = queryUtility(IIDNormalizer)\n+ idnormalizer = getUtility(IIDNormalizer)\n newNode["normalized_portal_type"] = idnormalizer.normalize(portalType)\n newNode["normalized_review_state"] = idnormalizer.normalize(\n newNode["review_state"]\ndiff --git a/Products/CMFPlone/controlpanel/browser/error_log_form.py b/Products/CMFPlone/controlpanel/browser/error_log_form.py\nindex b91169ff4c..9c61401dcf 100644\n--- a/Products/CMFPlone/controlpanel/browser/error_log_form.py\n+++ b/Products/CMFPlone/controlpanel/browser/error_log_form.py\n@@ -1,14 +1,14 @@\n from DateTime import DateTime\n from plone.base import PloneMessageFactory as _\n+from Products.CMFCore.utils import getToolByName\n from Products.CMFPlone.utils import safe_nativestring\n from Products.Five import BrowserView\n \n-import plone.api as api\n-\n \n class ErrorLogUpdate(BrowserView):\n def __call__(self):\n- member = api.user.get_current()\n+ portal_membership = getToolByName(self.context, "portal_membership")\n+ member = portal_membership.getAuthenticatedMember()\n \n if getattr(self.request, "form.button.search", None) is not None:\n search = self.request.form.get("search_entry")\ndiff --git a/news/3756.bugfix b/news/3756.bugfix\nnew file mode 100644\nindex 0000000000..4f3c022774\n--- /dev/null\n+++ b/news/3756.bugfix\n@@ -0,0 +1,2 @@\n+Fix deprecation warnings in "navtree" code + some micro optimizations\n+[jensens]\ndiff --git a/news/3962.bugfix b/news/3962.bugfix\nnew file mode 100644\nindex 0000000000..d49decfcd7\n--- /dev/null\n+++ b/news/3962.bugfix\n@@ -0,0 +1 @@\n+Products.CMFPlone must not depend on plone.api [@jensens]\ndiff --git a/setup.py b/setup.py\nindex ac11203d1d..d6a543abca 100644\n--- a/setup.py\n+++ b/setup.py\n@@ -53,7 +53,6 @@\n "borg.localrole",\n "five.customerize",\n "lxml",\n- "plone.api >= 1.4.4",\n "plone.app.content",\n "plone.app.contentlisting",\n "plone.app.contentmenu >= 2.0.1",\n' - -Repository: Products.CMFPlone - - -Branch: refs/heads/master -Date: 2024-05-30T16:26:22+02:00 -Author: Jens W. Klein (jensens) -Commit: https://github.com/plone/Products.CMFPlone/commit/110d688c49a48497e4d60c3aa5558164328a2620 - -zcml:condition for plone.app.discussion - -Files changed: -M Products/CMFPlone/configure.zcml - -b'diff --git a/Products/CMFPlone/configure.zcml b/Products/CMFPlone/configure.zcml\nindex c1a3ba041d..bc83b75869 100644\n--- a/Products/CMFPlone/configure.zcml\n+++ b/Products/CMFPlone/configure.zcml\n@@ -44,12 +44,10 @@\n \n \n \n- \n \n \n \n \n- \n \n \n \n@@ -65,12 +63,22 @@\n \n \n \n- \n+ \n+ \n+ \n \n \n+\n+\n \n \n \n' - -Repository: Products.CMFPlone - +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' -Branch: refs/heads/master -Date: 2024-05-30T16:35:11+02:00 -Author: Jens W. Klein (jensens) -Commit: https://github.com/plone/Products.CMFPlone/commit/bc2b937fc54f51a66459d46a7966325c27c33823 - -name shift here?? - -Files changed: -M Products/CMFPlone/controlpanel/tests/test_controlpanel_browser_navigation.py - -b'diff --git a/Products/CMFPlone/controlpanel/tests/test_controlpanel_browser_navigation.py b/Products/CMFPlone/controlpanel/tests/test_controlpanel_browser_navigation.py\nindex 0865585b51..ec6159b0c6 100644\n--- a/Products/CMFPlone/controlpanel/tests/test_controlpanel_browser_navigation.py\n+++ b/Products/CMFPlone/controlpanel/tests/test_controlpanel_browser_navigation.py\n@@ -109,7 +109,7 @@ def test_workflow_settings(self):\n self.browser.getControl(\n "Internally published [internally_published]"\n ).selected = True # noqa\n- self.browser.getControl("Pending [pending]").selected = True\n+ self.browser.getControl("Pending review [pending]").selected = True\n self.browser.getControl("Private [private]").selected = True\n self.browser.getControl("Public draft [visible]").selected = True\n self.browser.getControl("Published [published]").selected = True\n' - -Repository: Products.CMFPlone +Repository: plone.app.dexterity Branch: refs/heads/master -Date: 2024-05-31T18:34:08+02:00 +Date: 2024-05-20T20:45:51+02:00 Author: Jens W. Klein (jensens) -Commit: https://github.com/plone/Products.CMFPlone/commit/fc9cbecb290b9d74cb1e84a2821f0269d185e003 +Commit: https://github.com/plone/plone.app.dexterity/commit/c5d845819c9c3379ea9b18aa693f80db021fcac5 -allow discussion settings will be tested in pa.discussion +better phraseing and changelog, - SyntaxError Files changed: -M Products/CMFPlone/tests/robot/test_controlpanel_types.robot - -b'diff --git a/Products/CMFPlone/tests/robot/test_controlpanel_types.robot b/Products/CMFPlone/tests/robot/test_controlpanel_types.robot\nindex 56fed5385a..9a8da1d5c4 100644\n--- a/Products/CMFPlone/tests/robot/test_controlpanel_types.robot\n+++ b/Products/CMFPlone/tests/robot/test_controlpanel_types.robot\n@@ -21,8 +21,6 @@ Scenario: Allow comments for Link Type\n When I select \'Link\' in types dropdown\n and Allow discussion\n Then Wait until page contains Content Settings\n- When I add new Link \'my_link\'\n- Then Link \'my_link\' should have comments enabled\n \n Scenario: Change default workflow\n Given a logged-in site administrator\n@@ -74,11 +72,6 @@ I add new Link \'${id}\'\n \n # --- THEN -------------------------------------------------------------------\n \n-Link \'${id}\' should have comments enabled\n- Go to ${PLONE_URL}/${id}\n- Wait until page contains ${id}\n- Page should contain element xpath=//div[@id="commenting"]\n-\n Link \'${id}\' should have Single State Workflow enabled\n Go to ${PLONE_URL}/${id}\n Wait until page contains ${id}\n' - -Repository: Products.CMFPlone - - -Branch: refs/heads/master -Date: 2024-06-03T11:51:49-04:00 -Author: Jens W. Klein (jensens) -Commit: https://github.com/plone/Products.CMFPlone/commit/5281c5d7daddc4bb2f4bff246ea4704344fd4a9c +A news/371.bugfix +M plone/app/dexterity/behaviors/discussion.py -Merge branch 'master' into pa-discussion-core-addon +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' -Files changed: -A news/3964.bugfix -A news/3965.bugfix -A news/3966.bugfix -A news/3967.bugfix -M Products/CMFPlone/controlpanel/browser/maintenance.pt -M Products/CMFPlone/controlpanel/browser/maintenance.py -M Products/CMFPlone/controlpanel/browser/usergroups_groupdetails.pt -M Products/CMFPlone/controlpanel/browser/usergroups_groupmembership.pt -M Products/CMFPlone/controlpanel/browser/usergroups_groupsoverview.pt -M Products/CMFPlone/controlpanel/browser/usergroups_usermembership.pt -M Products/CMFPlone/controlpanel/browser/usergroups_usersoverview.pt -M Products/CMFPlone/controlpanel/tests/test_controlpanel_browser_maintenance.py -M Products/CMFPlone/tests/csrf.txt -M Products/CMFPlone/tests/testPortalCreation.py - -b'diff --git a/Products/CMFPlone/controlpanel/browser/maintenance.pt b/Products/CMFPlone/controlpanel/browser/maintenance.pt\nindex 5440bff8fe..6123e04c33 100644\n--- a/Products/CMFPlone/controlpanel/browser/maintenance.pt\n+++ b/Products/CMFPlone/controlpanel/browser/maintenance.pt\n@@ -45,7 +45,8 @@\n \n
\n \n-
\n \n-
\n \n- \n+ \n \n \n \ndiff --git a/Products/CMFPlone/controlpanel/browser/usergroups_groupmembership.pt b/Products/CMFPlone/controlpanel/browser/usergroups_groupmembership.pt\nindex 6ff5ab4d2b..a8b643edd9 100644\n--- a/Products/CMFPlone/controlpanel/browser/usergroups_groupmembership.pt\n+++ b/Products/CMFPlone/controlpanel/browser/usergroups_groupmembership.pt\n@@ -13,7 +13,7 @@\n errors python:request.get(\'errors\', {});\n portal_roles view/portal_roles;">\n \n-
\n+ \n \n \n

\n \n \n-

\n+ \n \n \n \ndiff --git a/Products/CMFPlone/controlpanel/browser/usergroups_groupsoverview.pt b/Products/CMFPlone/controlpanel/browser/usergroups_groupsoverview.pt\nindex 8fa939eafc..8dbeadaa8b 100644\n--- a/Products/CMFPlone/controlpanel/browser/usergroups_groupsoverview.pt\n+++ b/Products/CMFPlone/controlpanel/browser/usergroups_groupsoverview.pt\n@@ -21,7 +21,7 @@\n batchformkeys python:[\'searchstring\',\'_authenticator\'];\n portal_url context/portal_url;">\n \n-
\n+ \n
\n

Groups

\n@@ -207,7 +207,7 @@\n \n \n \n-
\n+ \n \n \n \ndiff --git a/Products/CMFPlone/controlpanel/browser/usergroups_usermembership.pt b/Products/CMFPlone/controlpanel/browser/usergroups_usermembership.pt\nindex 22fc535621..337456d5b7 100644\n--- a/Products/CMFPlone/controlpanel/browser/usergroups_usermembership.pt\n+++ b/Products/CMFPlone/controlpanel/browser/usergroups_usermembership.pt\n@@ -31,7 +31,7 @@\n batch python:Batch(view.searchResults, b_size, int(b_start));\n batchformkeys python:[\'searchstring\',\'_authenticator\', \'userid\'];">\n \n-
\n+ \n \n \n \ndiff --git a/Products/CMFPlone/controlpanel/browser/usergroups_usersoverview.pt b/Products/CMFPlone/controlpanel/browser/usergroups_usersoverview.pt\nindex 6bf0192991..4ba04fb288 100644\n--- a/Products/CMFPlone/controlpanel/browser/usergroups_usersoverview.pt\n+++ b/Products/CMFPlone/controlpanel/browser/usergroups_usersoverview.pt\n@@ -17,7 +17,7 @@\n portal_roles view/portal_roles;\n portal_url context/portal_url;">\n \n-
\n+ \n \n
\n \n@@ -225,7 +225,7 @@\n \n \n \n-
\n+ \n \n \n \ndiff --git a/Products/CMFPlone/controlpanel/tests/test_controlpanel_browser_maintenance.py b/Products/CMFPlone/controlpanel/tests/test_controlpanel_browser_maintenance.py\nindex 0216c1077c..9bf825009f 100644\n--- a/Products/CMFPlone/controlpanel/tests/test_controlpanel_browser_maintenance.py\n+++ b/Products/CMFPlone/controlpanel/tests/test_controlpanel_browser_maintenance.py\n@@ -1,14 +1,12 @@\n-from pkg_resources import get_distribution\n from plone.app.testing import TEST_USER_NAME\n from plone.app.testing import TEST_USER_PASSWORD\n from plone.testing.zope import Browser\n from plone.testing.zope import login\n+from Products.CMFPlone.controlpanel.browser.maintenance import LIFETIME\n from Products.CMFPlone.testing import PRODUCTS_CMFPLONE_FUNCTIONAL_TESTING\n \n import unittest\n-\n-\n-has_zope4 = get_distribution("Zope2").version.startswith("4")\n+import zExceptions\n \n \n class MaintenanceControlPanelFunctionalTest(unittest.TestCase):\n@@ -84,3 +82,18 @@ def pack(self, t=None, days=0):\n self.assertTrue(self.browser.url.endswith("maintenance-controlpanel"))\n self.assertTrue("Packed the database." in self.browser.contents)\n db.pack = original_pack\n+\n+ @unittest.skipIf(not LIFETIME, "Lifetime for shutdown not available")\n+ def test_maintenance_shutdown_CSRF_protection(self):\n+ login(self.app["acl_users"], "app")\n+ import transaction\n+\n+ transaction.commit()\n+ self.browser.handleErrors = False\n+ self.browser.addHeader("Authorization", f"Basic app:{TEST_USER_PASSWORD}")\n+ self.browser.open("/@@overview-controlpanel")\n+ self.browser.getLink("Maintenance").click()\n+ self.browser.getControl(name="_authenticator", index=0).value = "invalid!"\n+ self.browser.getControl(name="_authenticator", index=1).value = "invalid!"\n+ with self.asserRaises(zExceptions.Forbidden):\n+ self.browser.getControl("Shut down").click()\ndiff --git a/Products/CMFPlone/tests/csrf.txt b/Products/CMFPlone/tests/csrf.txt\nindex f03dbbb376..b18c93b87d 100644\n--- a/Products/CMFPlone/tests/csrf.txt\n+++ b/Products/CMFPlone/tests/csrf.txt\n@@ -417,23 +417,3 @@ Traceback (most recent call last):\n ...\n zExceptions.Forbidden: Form authenticator is invalid.\n \n-Exceptions to the rule is the "Maintenance" configlet, which is tested\n-separately. The "Maintenance" configlet has some special security\n-limitations, which is why we need to log in as the portal owner first:\n-\n- >>> app_browser = Browser(app)\n- >>> app_browser.handleErrors = False\n- >>> app.acl_users.userFolderAddUser(\'app\', TEST_USER_PASSWORD, [\'Manager\'], [])\n- >>> from plone.testing import zope\n- >>> zope.logout()\n- >>> zope.login(app[\'acl_users\'], \'app\')\n- >>> import transaction; transaction.commit()\n- >>> app_browser.addHeader(\'Authorization\', f\'Basic app:{TEST_USER_PASSWORD}\')\n- >>> app_browser.open(\'http://nohost/plone/@@overview-controlpanel\')\n- >>> app_browser.getLink(\'Maintenance\').click()\n- >>> app_browser.getControl(name=\'_authenticator\', index=0).value = \'invalid!\'\n- >>> app_browser.getControl(name=\'_authenticator\', index=1).value = \'invalid!\'\n- >>> app_browser.getControl(\'Shut down\').click()\n- Traceback (most recent call last):\n- ...\n- zExceptions.Forbidden: Form authenticator is invalid.\ndiff --git a/Products/CMFPlone/tests/testPortalCreation.py b/Products/CMFPlone/tests/testPortalCreation.py\nindex 208e60c3eb..5fa0b55ae4 100644\n--- a/Products/CMFPlone/tests/testPortalCreation.py\n+++ b/Products/CMFPlone/tests/testPortalCreation.py\n@@ -198,7 +198,6 @@ def testUnfriendlyTypesProperty(self):\n registry = getUtility(IRegistry)\n settings = registry.forInterface(ISearchSchema, prefix="plone")\n self.assertTrue("plone.types_not_searched" in registry)\n- self.assertTrue("Plone Site" in settings.types_not_searched)\n \n def testDefaultSortOrderProperty(self):\n # We should have an sort_on property\ndiff --git a/news/3964.bugfix b/news/3964.bugfix\nnew file mode 100644\nindex 0000000000..08994471a3\n--- /dev/null\n+++ b/news/3964.bugfix\n@@ -0,0 +1,2 @@\n+Removes duplicate `
` in controlpanel templates\n+[@szakitibi]\ndiff --git a/news/3965.bugfix b/news/3965.bugfix\nnew file mode 100644\nindex 0000000000..de4d0d6091\n--- /dev/null\n+++ b/news/3965.bugfix\n@@ -0,0 +1,2 @@\n+Do not test types_not_searched for a element that is not part of the underlying vocabulary.\n+[@jensens]\ndiff --git a/news/3966.bugfix b/news/3966.bugfix\nnew file mode 100644\nindex 0000000000..1c962a9e34\n--- /dev/null\n+++ b/news/3966.bugfix\n@@ -0,0 +1 @@\n+Remove unused leftover reference to the Zope2 package from test. [@jensens]\ndiff --git a/news/3967.bugfix b/news/3967.bugfix\nnew file mode 100644\nindex 0000000000..8cbd922fef\n--- /dev/null\n+++ b/news/3967.bugfix\n@@ -0,0 +1,3 @@\n+Fix: Traceback in maintenance control panel on shutdown if feature is not available.\n+Hide button if action is not possible.\n+[@jensens]\n' - -Repository: Products.CMFPlone +Repository: plone.app.dexterity Branch: refs/heads/master -Date: 2024-06-10T16:42:46+02:00 +Date: 2024-05-20T20:46:44+02:00 Author: Jens W. Klein (jensens) -Commit: https://github.com/plone/Products.CMFPlone/commit/cde4f1a750950eff02ee7dc0d4797a7b10ea2abf +Commit: https://github.com/plone/plone.app.dexterity/commit/72fcbbe1a71c532b6248df5db9d71ec3bad923f5 -move test with comments to pone.app.discussion +Merge branch 'master' into pa-discussion-coreaddon Files changed: -M Products/CMFPlone/tests/robot/test_controlpanel_types.robot +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/Products/CMFPlone/tests/robot/test_controlpanel_types.robot b/Products/CMFPlone/tests/robot/test_controlpanel_types.robot\nindex 9a8da1d5c4..b611a93109 100644\n--- a/Products/CMFPlone/tests/robot/test_controlpanel_types.robot\n+++ b/Products/CMFPlone/tests/robot/test_controlpanel_types.robot\n@@ -13,15 +13,6 @@ Test Teardown Run keywords Plone Test Teardown\n \n \n *** Test Cases ***************************************************************\n-\n-Scenario: Allow comments for Link Type\n- Given a logged-in manager\n- and Globally enabled comments\n- and the types control panel\n- When I select 'Link' in types dropdown\n- and Allow discussion\n- Then Wait until page contains Content Settings\n-\n Scenario: Change default workflow\n Given a logged-in site administrator\n and the types control panel\n@@ -34,32 +25,12 @@ Scenario: Change default workflow\n *** Keywords *****************************************************************\n \n # --- GIVEN ------------------------------------------------------------------\n-\n-a logged-in manager\n- Enable autologin as Manager\n-\n the types control panel\n Go to ${PLONE_URL}/@@content-controlpanel\n Wait until page contains Content Settings\n \n-Globally enabled comments\n- Go to ${PLONE_URL}/@@discussion-settings\n- Wait until page contains Discussion settings\n- Select checkbox name=form.widgets.globally_enabled:list\n- Click button Save\n-\n-\n \n # --- WHEN -------------------------------------------------------------------\n-\n-I select '${content_type}' in types dropdown\n- Select from list by label name=type_id ${content_type}\n- Wait until page contains Globally addable\n-\n-Allow discussion\n- Select checkbox name=allow_discussion:boolean\n- Click Button Save\n-\n I select '${workflow}' workflow\n Select from list by label name=new_workflow ${workflow}\n Click Button Save\n" +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: Products.CMFPlone +Repository: plone.app.dexterity Branch: refs/heads/master -Date: 2024-06-10T10:43:26-04:00 +Date: 2024-05-20T20:56:34+02:00 Author: Jens W. Klein (jensens) -Commit: https://github.com/plone/Products.CMFPlone/commit/2cb388aba0e9afa8ca28727f726ae57790bd3a0d +Commit: https://github.com/plone/plone.app.dexterity/commit/53e4b93afc635c7816b044f80cb6f3a252e205ed -Merge branch 'master' into pa-discussion-core-addon +move test to pasdiscussion Files changed: -A news/3952.bugfix -M Products/CMFPlone/controlpanel/browser/usergroups_usermembership.py +M plone/app/dexterity/tests/test_doctests.py +D plone/app/dexterity/tests/discussion.txt -b"diff --git a/Products/CMFPlone/controlpanel/browser/usergroups_usermembership.py b/Products/CMFPlone/controlpanel/browser/usergroups_usermembership.py\nindex 57ea21eeed..755c807856 100644\n--- a/Products/CMFPlone/controlpanel/browser/usergroups_usermembership.py\n+++ b/Products/CMFPlone/controlpanel/browser/usergroups_usermembership.py\n@@ -63,7 +63,7 @@ def getGroups(self):\n for m in self.gtool.getGroupsForPrincipal(self.member)\n ]\n groupResults.sort(\n- key=lambda x: x is not None and normalizeString(x.getGroupTitleOrName())\n+ key=lambda x: normalizeString(x.getGroupTitleOrName()) if x else ''\n )\n return [i for i in groupResults if i]\n \ndiff --git a/news/3952.bugfix b/news/3952.bugfix\nnew file mode 100644\nindex 0000000000..a63571e60a\n--- /dev/null\n+++ b/news/3952.bugfix\n@@ -0,0 +1,2 @@\n+Fix TypeError in getGroups sorting\n+[@rohnsha0]\n\\ No newline at end of file\n" +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: Products.CMFPlone +Repository: plone.app.dexterity Branch: refs/heads/master -Date: 2024-07-09T05:18:50-04:00 +Date: 2024-06-03T11:52:14-04:00 Author: Jens W. Klein (jensens) -Commit: https://github.com/plone/Products.CMFPlone/commit/cb2e823f50961cd179a47347147ab1f9d5a3ab9b +Commit: https://github.com/plone/plone.app.dexterity/commit/c98e60c8256e7e7bacf43dcc54e96fc5a81c5738 -Merge branch 'master' into pa-discussion-core-addon +Merge branch 'master' into pa-discussion-coreaddon Files changed: -M .github/ISSUE_TEMPLATE/PLIP.md -M CHANGES.md -M Products/CMFPlone/RegistrationTool.py -M Products/CMFPlone/browser/admin.py -M Products/CMFPlone/browser/author.py -M Products/CMFPlone/browser/configure.zcml -M Products/CMFPlone/browser/templates/author.pt -M Products/CMFPlone/browser/templates/plone-upgrade.pt -M Products/CMFPlone/controlpanel/browser/resourceregistry.pt -M Products/CMFPlone/controlpanel/browser/tinymce.py -M Products/CMFPlone/interfaces/__init__.py -M Products/CMFPlone/patterns/settings.py -M Products/CMFPlone/patterns/tinymce.py -M Products/CMFPlone/tests/testEmailLogin.py -M Products/CMFPlone/tests/testMigrationTool.py -M Products/CMFPlone/tests/testResourceRegistries.py -M Products/CMFPlone/tests/test_patternsettings.py +M CHANGES.rst M setup.py -D news/3756.bugfix -D news/3945.bugfix -D news/3946.bugfix -D news/3949.bugfix -D news/3952.bugfix -D news/3960.bugfix -D news/3962.bugfix -D news/3964.bugfix -D news/3965.bugfix -D news/3966.bugfix -D news/3967.bugfix -D news/6103.internal +D news/+meta.internal +D news/387.bugfix +D news/6e36bcc4.internal -b'diff --git a/.github/ISSUE_TEMPLATE/PLIP.md b/.github/ISSUE_TEMPLATE/PLIP.md\nindex 271a1c9345..e570a1162f 100644\n--- a/.github/ISSUE_TEMPLATE/PLIP.md\n+++ b/.github/ISSUE_TEMPLATE/PLIP.md\n@@ -2,8 +2,9 @@\n name: "\\U0001F680 PLIP"\n about: Plone Improvement Proposal\n title: \'\'\n-labels: \'\'\n+labels: \'03 type: feature (plip)\'\n assignees: \'\'\n+projects: \'plone/47\'\n \n ---\n \n@@ -11,9 +12,7 @@ assignees: \'\'\n \n \n \n+## 6.1.0a4.dev0 (2024-07-08)\n+\n+\n+### Bug fixes:\n+\n+- Mockup TinyMCE settings: Remove unused loadingBaseUrl option. 3765-1\n+- Mockup TinyMCE settings: Remove deprecated AtD plugin settings. 3765-2\n+- Mockup TinyMCE settings: Remove unused AtD related views. 3765-3\n+- Mockup TinyMCE settings: Remove unused ITinyMCESpellCheckerSchema and ITinyMCESpellCheckerForm. 3765-4\n+- Fix deprecation warnings in "navtree" code + some micro optimizations\n+ [jensens] #3756\n+- Use details element for collapsibles in the resource registry.\n+ Makes it possible to toggle elements even with broken or missing javascript.\n+ Also properly connect form labels with their inputs.\n+ Fixes #3942\n+- Import INavigationRoot from plone.base, removes DeprecationWarning.\n+ [@jensens] #3945\n+- Use `context` instead of `here` in templates.\n+ Call `@@main_template` (with prefix `@@`) to optimize lookup.\n+ [@jensens] #3946\n+- Reduce DeprecationWarnings. [@jensens] #3949\n+- Fix TypeError in getGroups sorting\n+ [@rohnsha0] #3952\n+- Remove queryCatalog and getFolderContents skins script.\n+ This includes a refactoring in the search RSS and updates to soe tests\n+ [@jensens] #3960\n+- Products.CMFPlone must not depend on plone.api [@jensens] #3962\n+- Removes duplicate `
` in controlpanel templates\n+ [@szakitibi] #3964\n+- Do not test types_not_searched for a element that is not part of the underlying vocabulary.\n+ [@jensens] #3965\n+- Remove unused leftover reference to the Zope2 package from test. [@jensens] #3966\n+- Fix: Traceback in maintenance control panel on shutdown if feature is not available.\n+ Hide button if action is not possible.\n+ [@jensens] #3967\n+- Fixed RegistrationTool to take user email with `__+__@abc.com`.\n+ [@rohnsha0] #3968\n+- Plone upgrade page: show error when upgrade is needed but no upgrades are available.\n+ Especially show a note when the `plone.app.upgrade` package is not available.\n+ [maurits] #3975\n+- Plone upgrade page: show list of previously installed packages that are currently missing.\n+ For example: `plone.app.discussion` may be missing in Plone 6.1, unless you explicitly add it, or depend on the `Plone` package.\n+ [maurits] #3975\n+\n+\n+### Internal:\n+\n+- Resourceregistry controlpanel: zprettify template.\n+ [thet] #3942\n+- Automatically set the label to `03 type: feature (plip)` for PLIPs. @stevepiercy #3982\n+- Automatically add a PLIP issue to the PLIP project board. @stevepiercy #3984\n+- Updated metadata version to 6103.\n+ [maurits] #6103\n+\n ## 6.1.0a3 (2024-04-26)\n \n \ndiff --git a/Products/CMFPlone/RegistrationTool.py b/Products/CMFPlone/RegistrationTool.py\nindex b3a1931cea..905fc35b6c 100644\n--- a/Products/CMFPlone/RegistrationTool.py\n+++ b/Products/CMFPlone/RegistrationTool.py\n@@ -107,7 +107,7 @@ class RegistrationTool(PloneBaseTool, BaseTool):\n plone_tool = 1\n md5key = None\n _v_md5base = None\n- default_member_id_pattern = r"^\\w[\\w\\.\\-@]+\\w$"\n+ default_member_id_pattern = r"^\\w[\\w\\.\\-@+]+\\w$"\n _ALLOWED_MEMBER_ID_PATTERN = re.compile(default_member_id_pattern)\n \n def __init__(self):\ndiff --git a/Products/CMFPlone/browser/admin.py b/Products/CMFPlone/browser/admin.py\nindex 6c96868982..e6273a812a 100644\n--- a/Products/CMFPlone/browser/admin.py\n+++ b/Products/CMFPlone/browser/admin.py\n@@ -1,6 +1,9 @@\n from AccessControl import getSecurityManager\n from AccessControl.Permissions import view as View\n from collections import OrderedDict\n+from functools import cached_property\n+from importlib.metadata import distribution\n+from importlib.metadata import PackageNotFoundError\n from OFS.interfaces import IApplication\n from plone.base.interfaces import INonInstallable\n from plone.base.interfaces import IPloneSiteRoot\n@@ -35,14 +38,18 @@\n from ZPublisher.BaseRequest import DefaultPublishTraverse\n \n import logging\n-import pkg_resources\n \n \n try:\n- pkg_resources.get_distribution("plone.volto")\n+ distribution("plone.volto")\n HAS_VOLTO = True\n-except pkg_resources.DistributionNotFound:\n+except PackageNotFoundError:\n HAS_VOLTO = False\n+try:\n+ distribution("plone.app.upgrade")\n+ HAS_UPGRADE = True\n+except PackageNotFoundError:\n+ HAS_UPGRADE = False\n LOGGER = logging.getLogger("Products.CMFPlone")\n \n \n@@ -313,10 +320,43 @@ def __call__(self):\n \n \n class Upgrade(BrowserView):\n+ has_upgrade = HAS_UPGRADE\n+\n def upgrades(self):\n pm = getattr(self.context, "portal_migration")\n return pm.listUpgrades()\n \n+ @cached_property\n+ def missing_packages(self):\n+ """Get list of missing packages that were installed in GS.\n+\n+ Main use case:\n+\n+ * Create a Product.CMFPlone 6.0 site.\n+ * Upgrade the code to Products.CMFPlone 6.1.\n+ * Now the plone.app.discussion package is missing.\n+ This will give problems, because its GS profile was installed\n+ by default in 6.0.\n+\n+ Beware of false positives. For example when upgrading from Plone 5.2,\n+ CMFFormController will be missing, but we have code in plone.app.upgrade\n+ to properly clean this up. So we should not bother the admin with this.\n+ """\n+ setup = getattr(self.context, "portal_setup")\n+ installed = sorted(\n+ set([x.split(":")[0] for x in setup._profile_upgrade_versions.keys()])\n+ )\n+ ignore = ["Products.CMFFormController"]\n+ missing = []\n+ for package in installed:\n+ if package in ignore:\n+ continue\n+ try:\n+ distribution(package)\n+ except PackageNotFoundError:\n+ missing.append(package)\n+ return missing\n+\n def versions(self):\n pm = getattr(self.context, "portal_migration")\n result = {}\ndiff --git a/Products/CMFPlone/browser/author.py b/Products/CMFPlone/browser/author.py\nindex ed13dd4e98..644099ea71 100644\n--- a/Products/CMFPlone/browser/author.py\n+++ b/Products/CMFPlone/browser/author.py\n@@ -20,6 +20,7 @@\n from zope.component import getUtility\n from zope.interface import implementer\n from zope.publisher.interfaces import IPublishTraverse\n+from ZTUtils import make_query\n \n import logging\n \n@@ -137,6 +138,9 @@ def publishTraverse(self, request, name):\n self.username = name\n return self\n \n+ def makeQuery(self, **kw):\n+ return make_query(**kw)\n+\n @property\n def is_anonymous(self):\n return self.portal_state.anonymous()\ndiff --git a/Products/CMFPlone/browser/configure.zcml b/Products/CMFPlone/browser/configure.zcml\nindex 36941da36b..23ecdcfdd3 100644\n--- a/Products/CMFPlone/browser/configure.zcml\n+++ b/Products/CMFPlone/browser/configure.zcml\n@@ -224,15 +224,6 @@\n permission="zope2.View"\n />\n \n- \n- \n-\n \n \n \n

\n- \n+ \n All content created by\n …\n \ndiff --git a/Products/CMFPlone/browser/templates/plone-upgrade.pt b/Products/CMFPlone/browser/templates/plone-upgrade.pt\nindex 9b9cdecc7d..8752241612 100644\n--- a/Products/CMFPlone/browser/templates/plone-upgrade.pt\n+++ b/Products/CMFPlone/browser/templates/plone-upgrade.pt\n@@ -21,6 +21,7 @@\n \n \n \n

\n@@ -45,6 +46,12 @@\n \n
\n
\n+
\n+

The following packages were previously installed, but are currently missing. This may be a problem.

\n+
    \n+
  • ${package}
  • \n+
\n+
\n

\n Your site is up to date.\n

\n@@ -101,7 +108,7 @@\n

\n \n
\n- \n+ \n \n \n@@ -144,9 +151,20 @@\n
\n
\n \n+

\n+ \n+ No upgrade steps are available.\n+ \n+ \n+ The plone.app.upgrade package is missing.\n+ \n+

\n+\n \n \n \ndiff --git a/Products/CMFPlone/controlpanel/browser/resourceregistry.pt b/Products/CMFPlone/controlpanel/browser/resourceregistry.pt\nindex 4279bdb02b..2385c90f98 100644\n--- a/Products/CMFPlone/controlpanel/browser/resourceregistry.pt\n+++ b/Products/CMFPlone/controlpanel/browser/resourceregistry.pt\n@@ -1,169 +1,278 @@\n-\n-\n-\n-\n-\n-\n-
\n+ xml:lang="en"\n+ i18n:domain="plone"\n+>\n+ \n \n-

Resource Registry

\n+ \n+
\n+

Resource Registry

\n \n-
\n+
\n Configure Plone JavaScript/CSS resource bundles.\n-
\n-
\n+ \n+
\n \n-
\n-
\n- \n- Javascript disabled or error(s) occurred\n- \n+
\n+
\n+ \n+ Javascript disabled or error(s) occurred\n+ \n If this message persists, a Javascript Error occurred within the\n resources below.\n- \n-
\n- \n+ \n-
\n- \n+ \n+ \n-
\n-
\n- \n+
\n+
\n+ \n-
\n-
\n- \n-
\n-

\n- \n-

\n-
\n-
\n-
\n- \n+ \n+
\n+ \n+
\n+ \n+
\n+ \n+ \n+ \n+ \n+

${python:bundle["name"]}

\n+

Add new bundle

\n+
\n+
\n+ \n
\n- \n- \n+ \n
\n
\n- \n- \n+ \n
\n
\n- \n- \n+ \n
\n
\n- \n-
\n
\n- \n- \n+ \n
\n
\n- \n- \n+ \n
\n
\n- \n-
\n
\n- \n-
\n
\n- \n- \n- \n+ \n+ \n+ \n
\n
\n-
\n+ \n+ \n
\n-
\n-
\n-
\n-
\n-

Additional Resources

\n-

After the above resources, the following might get loaded:

\n-
\n-
Theme CSS and JavaScript
\n-
\n+\n+
\n+

Additional Resources

\n+

After the above resources, the following might get loaded:

\n+
\n+
Theme CSS and JavaScript
\n+
\n The activated Plone-Theme usually provides one CSS bundle and sometimes a javascript bundle.\n-
\n-
Custom CSS
\n-
\n+
\n+
Custom CSS
\n+
\n At last a custom CSS is loaded, if non-empty.\n It can be used to override the previous loaded CSS.\n It is provided for tinkerers and those in need of urgent through-the-web changes.\n- Hint: Edit the Custom CSS in the Theming-Control-Panel.\n-
\n-
\n-
\n-
\n- \n-\n+ Hint:\n+ Edit the Custom CSS in the Theming-Control-Panel.\n+ \n+ \n+
\n+
\n \n-\n-\n+ \n+\n+ \n \n+ \n+\ndiff --git a/Products/CMFPlone/controlpanel/browser/tinymce.py b/Products/CMFPlone/controlpanel/browser/tinymce.py\nindex a7cd3e97ee..aa829445e2 100644\n--- a/Products/CMFPlone/controlpanel/browser/tinymce.py\n+++ b/Products/CMFPlone/controlpanel/browser/tinymce.py\n@@ -5,7 +5,6 @@\n from plone.base.interfaces import ITinyMCEPluginSchema\n from plone.base.interfaces import ITinyMCEResourceTypesSchema\n from plone.base.interfaces import ITinyMCESchema\n-from plone.base.interfaces import ITinyMCESpellCheckerSchema\n from z3c.form import field\n from z3c.form import group\n from z3c.form.browser.checkbox import CheckBoxFieldWidget\n@@ -16,11 +15,6 @@ class TinyMCEPluginForm(group.GroupForm):\n fields = field.Fields(ITinyMCEPluginSchema)\n \n \n-class TinyMCESpellCheckerForm(group.GroupForm):\n- label = _("Spell Checker")\n- fields = field.Fields(ITinyMCESpellCheckerSchema)\n-\n-\n class TinyMCEResourceTypesForm(group.GroupForm):\n label = _("Resource Types")\n fields = field.Fields(ITinyMCEResourceTypesSchema)\n@@ -39,7 +33,6 @@ class TinyMCEControlPanelForm(controlpanel.RegistryEditForm):\n fields = field.Fields(ITinyMCELayoutSchema)\n groups = (\n TinyMCEPluginForm,\n- TinyMCESpellCheckerForm,\n TinyMCEResourceTypesForm,\n TinyMCEAdvancedForm,\n )\ndiff --git a/Products/CMFPlone/interfaces/__init__.py b/Products/CMFPlone/interfaces/__init__.py\nindex f00871004f..afb3e1df15 100644\n--- a/Products/CMFPlone/interfaces/__init__.py\n+++ b/Products/CMFPlone/interfaces/__init__.py\n@@ -31,7 +31,6 @@\n ITinyMCEPluginSchema="plone.base.interfaces.controlpanel:ITinyMCEPluginSchema",\n ITinyMCEResourceTypesSchema="plone.base.interfaces.controlpanel:ITinyMCEResourceTypesSchema",\n ITinyMCESchema="plone.base.interfaces.controlpanel:ITinyMCESchema",\n- ITinyMCESpellCheckerSchema="plone.base.interfaces.controlpanel:ITinyMCESpellCheckerSchema",\n ITypesSchema="plone.base.interfaces.controlpanel:ITypesSchema",\n IUserGroupsSettingsSchema="plone.base.interfaces.controlpanel:IUserGroupsSettingsSchema",\n IConfigurationChangedEvent="plone.base.interfaces.events:IConfigurationChangedEvent",\ndiff --git a/Products/CMFPlone/patterns/settings.py b/Products/CMFPlone/patterns/settings.py\nindex 84c9f663a2..5573868983 100644\n--- a/Products/CMFPlone/patterns/settings.py\n+++ b/Products/CMFPlone/patterns/settings.py\n@@ -156,9 +156,6 @@ def tinymce(self):\n "pictureVariants": self.picture_variants,\n "imageCaptioningEnabled": self.image_captioning,\n "linkAttribute": "UID",\n- # This is for loading the languages on tinymce\n- "loadingBaseUrl": "{}/++plone++static/components/tinymce-builded/"\n- "js/tinymce".format(portal_url),\n "relatedItems": related_items_config,\n "prependToScalePart": "/@@images/image/",\n "prependToUrl": "{}/resolveuid/".format(site_path.rstrip("/")),\ndiff --git a/Products/CMFPlone/patterns/tinymce.py b/Products/CMFPlone/patterns/tinymce.py\nindex 27d569ef83..62fca9c63d 100644\n--- a/Products/CMFPlone/patterns/tinymce.py\n+++ b/Products/CMFPlone/patterns/tinymce.py\n@@ -139,31 +139,6 @@ def get_tiny_config(self):\n "plonelink ploneimage inserttable |" " cell row column deletetable"\n )\n \n- if settings.libraries_spellchecker_choice == "AtD":\n- mtool = getToolByName(self.context, "portal_membership")\n- member = mtool.getAuthenticatedMember()\n- member_id = member.getId()\n- if member_id:\n- if "compat3x" not in tiny_config["plugins"]:\n- tiny_config["plugins"].append("compat3x")\n- tiny_config["external_plugins"][\n- "AtD"\n- ] = "{}/++plone++static/tinymce-AtD-plugin/" "editor_plugin.js".format(\n- self.nav_root_url\n- )\n- # None when Anonymous User\n- tiny_config["atd_rpc_id"] = "plone-" + member_id\n- tiny_config["atd_rpc_url"] = self.nav_root_url\n- tiny_config["atd_show_types"] = ",".join(\n- settings.libraries_atd_show_types\n- )\n- tiny_config["atd_ignore_strings"] = ",".join(\n- settings.libraries_atd_ignore_strings\n- )\n- toolbar_additions.append("AtD")\n- elif settings.libraries_spellchecker_choice == "AtD":\n- tiny_config["browser_spellcheck"] = True\n-\n if toolbar_additions:\n tiny_config["toolbar"] += " | {}".format(" ".join(toolbar_additions))\n \ndiff --git a/Products/CMFPlone/tests/testEmailLogin.py b/Products/CMFPlone/tests/testEmailLogin.py\nindex 87dc5ecaf3..3179e27507 100644\n--- a/Products/CMFPlone/tests/testEmailLogin.py\n+++ b/Products/CMFPlone/tests/testEmailLogin.py\n@@ -79,12 +79,9 @@ def testEmailMemberIdsAllowed(self):\n # Strange, but valid as id:\n self.assertTrue(pattern.match("no.address@example"))\n self.assertTrue(registration.isMemberIdAllowed("no.address@example"))\n- # http://dev.plone.org/ticket/11616 mentions some non-standard\n- # email addresses.\n- # A plus sign in the id gives problems in some parts of the\n- # UI, so we do not allow it.\n- self.assertFalse(pattern.match("user+test@example.org"))\n- self.assertFalse(registration.isMemberIdAllowed("user+test@example.org"))\n+ # testing if it breaks anything (according to https://github.com/plone/Products.CMFPlone/issues/3968)\n+ self.assertTrue(pattern.match("user+test@example.org")) \n+ self.assertTrue(registration.isMemberIdAllowed("user+test@example.org"))\n # An apostrophe also sounds like a bad idea to use in an id,\n # though this is a valid email address:\n self.assertFalse(pattern.match("o\'hara@example.org"))\ndiff --git a/Products/CMFPlone/tests/testMigrationTool.py b/Products/CMFPlone/tests/testMigrationTool.py\nindex f8895a40e0..bd697372d5 100644\n--- a/Products/CMFPlone/tests/testMigrationTool.py\n+++ b/Products/CMFPlone/tests/testMigrationTool.py\n@@ -254,3 +254,27 @@ def test_plone_addonlist_upgrade_all(self):\n # original versions.\n self.assertEqual(cmfeditions_version, getversion(cmfeditions_id))\n self.assertEqual(querystring_version, getversion(querystring_id))\n+\n+\n+class TestPloneUpgradePage(PloneTestCase.PloneTestCase):\n+ def afterSetUp(self):\n+ self.migration = getToolByName(self.portal, "portal_migration")\n+ self.setup = getToolByName(self.portal, "portal_setup")\n+\n+ def test_upgrades(self):\n+ self.setRoles(["Manager"])\n+ view = self.portal.restrictedTraverse("@@plone-upgrade")\n+ self.assertEqual(view.upgrades(), [])\n+ self.setup.setLastVersionForProfile(_DEFAULT_PROFILE, START_PROFILE)\n+ self.assertGreater(len(view.upgrades()), 0)\n+\n+ def test_missing_packages(self):\n+ self.setRoles(["Manager"])\n+ view = self.portal.restrictedTraverse("@@plone-upgrade")\n+ self.assertEqual(view.missing_packages, [])\n+\n+ # Fake a missing package that was installed.\n+ self.setup.setLastVersionForProfile("my.dummy.package:default", 1)\n+ # Delete the cached property.\n+ del view.missing_packages\n+ self.assertEqual(view.missing_packages, ["my.dummy.package"])\ndiff --git a/Products/CMFPlone/tests/testResourceRegistries.py b/Products/CMFPlone/tests/testResourceRegistries.py\nindex 29ae11ae00..12021ceb0a 100644\n--- a/Products/CMFPlone/tests/testResourceRegistries.py\n+++ b/Products/CMFPlone/tests/testResourceRegistries.py\n@@ -489,7 +489,7 @@ def test_add_resource(self):\n add_form.getControl("add").click()\n \n self.assertIn(\n- \'

\',\n+ \'

my-resource

\',\n self.browser.contents,\n )\n \n@@ -507,6 +507,6 @@ def test_update_resource(self):\n form.getControl("update").click()\n \n self.assertIn(\n- \'

\',\n+ \'

new-resource-name

\',\n self.browser.contents,\n )\ndiff --git a/Products/CMFPlone/tests/test_patternsettings.py b/Products/CMFPlone/tests/test_patternsettings.py\nindex e485b2bff3..b447b96e51 100644\n--- a/Products/CMFPlone/tests/test_patternsettings.py\n+++ b/Products/CMFPlone/tests/test_patternsettings.py\n@@ -22,15 +22,6 @@ def get_conf(self):\n )\n return json.loads(adapter.tinymce()["data-pat-tinymce"])\n \n- def test_atd_included(self):\n- registry = getUtility(IRegistry)\n- settings = registry.forInterface(ITinyMCESchema, prefix="plone")\n- settings.libraries_spellchecker_choice = "AtD"\n- login(self.layer["portal"], TEST_USER_NAME)\n- conf = self.get_conf()\n- self.assertTrue("compat3x" in conf["tiny"]["plugins"])\n- self.assertTrue("AtD" in conf["tiny"]["external_plugins"])\n-\n def test_style_formats(self):\n conf = self.get_conf()\n self.assertEqual(len(conf["tiny"]["style_formats"]), 5)\ndiff --git a/news/3756.bugfix b/news/3756.bugfix\ndeleted file mode 100644\nindex 4f3c022774..0000000000\n--- a/news/3756.bugfix\n+++ /dev/null\n@@ -1,2 +0,0 @@\n-Fix deprecation warnings in "navtree" code + some micro optimizations\n-[jensens]\ndiff --git a/news/3945.bugfix b/news/3945.bugfix\ndeleted file mode 100644\nindex d6e4076422..0000000000\n--- a/news/3945.bugfix\n+++ /dev/null\n@@ -1,2 +0,0 @@\n-Import INavigationRoot from plone.base, removes DeprecationWarning.\n-[@jensens]\n\\ No newline at end of file\ndiff --git a/news/3946.bugfix b/news/3946.bugfix\ndeleted file mode 100644\nindex 1853b16830..0000000000\n--- a/news/3946.bugfix\n+++ /dev/null\n@@ -1,3 +0,0 @@\n-Use `context` instead of `here` in templates.\n-Call `@@main_template` (with prefix `@@`) to optimize lookup.\n-[@jensens]\n\\ No newline at end of file\ndiff --git a/news/3949.bugfix b/news/3949.bugfix\ndeleted file mode 100644\nindex bf15b1c268..0000000000\n--- a/news/3949.bugfix\n+++ /dev/null\n@@ -1 +0,0 @@\n-Reduce DeprecationWarnings. [@jensens]\ndiff --git a/news/3952.bugfix b/news/3952.bugfix\ndeleted file mode 100644\nindex a63571e60a..0000000000\n--- a/news/3952.bugfix\n+++ /dev/null\n@@ -1,2 +0,0 @@\n-Fix TypeError in getGroups sorting\n-[@rohnsha0]\n\\ No newline at end of file\ndiff --git a/news/3960.bugfix b/news/3960.bugfix\ndeleted file mode 100644\nindex b1fe215568..0000000000\n--- a/news/3960.bugfix\n+++ /dev/null\n@@ -1,3 +0,0 @@\n-Remove queryCatalog and getFolderContents skins script.\n-This includes a refactoring in the search RSS and updates to soe tests\n-[@jensens]\ndiff --git a/news/3962.bugfix b/news/3962.bugfix\ndeleted file mode 100644\nindex d49decfcd7..0000000000\n--- a/news/3962.bugfix\n+++ /dev/null\n@@ -1 +0,0 @@\n-Products.CMFPlone must not depend on plone.api [@jensens]\ndiff --git a/news/3964.bugfix b/news/3964.bugfix\ndeleted file mode 100644\nindex 08994471a3..0000000000\n--- a/news/3964.bugfix\n+++ /dev/null\n@@ -1,2 +0,0 @@\n-Removes duplicate `
` in controlpanel templates\n-[@szakitibi]\ndiff --git a/news/3965.bugfix b/news/3965.bugfix\ndeleted file mode 100644\nindex de4d0d6091..0000000000\n--- a/news/3965.bugfix\n+++ /dev/null\n@@ -1,2 +0,0 @@\n-Do not test types_not_searched for a element that is not part of the underlying vocabulary.\n-[@jensens]\ndiff --git a/news/3966.bugfix b/news/3966.bugfix\ndeleted file mode 100644\nindex 1c962a9e34..0000000000\n--- a/news/3966.bugfix\n+++ /dev/null\n@@ -1 +0,0 @@\n-Remove unused leftover reference to the Zope2 package from test. [@jensens]\ndiff --git a/news/3967.bugfix b/news/3967.bugfix\ndeleted file mode 100644\nindex 8cbd922fef..0000000000\n--- a/news/3967.bugfix\n+++ /dev/null\n@@ -1,3 +0,0 @@\n-Fix: Traceback in maintenance control panel on shutdown if feature is not available.\n-Hide button if action is not possible.\n-[@jensens]\ndiff --git a/news/6103.internal b/news/6103.internal\ndeleted file mode 100644\nindex efbd58ca7e..0000000000\n--- a/news/6103.internal\n+++ /dev/null\n@@ -1,2 +0,0 @@\n-Updated metadata version to 6103.\n-[maurits]\ndiff --git a/setup.py b/setup.py\nindex d6a543abca..61c25104b5 100644\n--- a/setup.py\n+++ b/setup.py\n@@ -2,7 +2,7 @@\n from setuptools import setup\n \n \n-version = "6.1.0a4.dev0"\n+version = "6.1.0a4.dev1"\n \n \n setup(\n' +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: Products.CMFPlone +Repository: plone.app.dexterity Branch: refs/heads/master -Date: 2024-08-02T19:10:12+05:30 -Author: Rohan Shaw (rohnsha0) <86848116+rohnsha0@users.noreply.github.com> -Commit: https://github.com/plone/Products.CMFPlone/commit/8b4537b532f3d4e0fbfde6e1137d1b87d668c9fc +Date: 2024-06-13T21:34:22+02:00 +Author: Maurits van Rees (mauritsvanrees) +Commit: https://github.com/plone/plone.app.dexterity/commit/be543ca4d6600adffaf2e1fbf45da147fafb4ff6 -Merge branch 'master' into pa-discussion-core-addon +Remove plone.app.discussion from test extras. Files changed: -A news/6104.internal -M .github/ISSUE_TEMPLATE/PLIP.md -M CHANGES.md -M Products/CMFPlone/PloneTool.py -M Products/CMFPlone/__init__.py -M Products/CMFPlone/browser/author.py -M Products/CMFPlone/browser/templates/ajax_main_template.pt -M Products/CMFPlone/browser/templates/main_template.pt -M Products/CMFPlone/exportimport/configure.zcml -M Products/CMFPlone/interfaces/__init__.py -M Products/CMFPlone/profiles/default/componentregistry.xml -M Products/CMFPlone/profiles/default/metadata.xml -M Products/CMFPlone/profiles/default/toolset.xml -M Products/CMFPlone/tests/testInterfaces.py -M Products/CMFPlone/tests/testNavigationView.py -M Products/CMFPlone/tests/testPortalCreation.py -M Products/CMFPlone/tests/testWebDAV.py -M Products/CMFPlone/tests/test_defaultpage.py -M Products/CMFPlone/tests/test_zmi.py M setup.py -D Products/CMFPlone/PropertiesTool.py -D Products/CMFPlone/exportimport/propertiestool.py -D Products/CMFPlone/exportimport/tests/testPropertiesTool.py -D Products/CMFPlone/profiles/default/propertiestool.xml -D Products/CMFPlone/www/addPropertySheet.zpt -b'diff --git a/.github/ISSUE_TEMPLATE/PLIP.md b/.github/ISSUE_TEMPLATE/PLIP.md\nindex e570a1162f..df9efd7ad5 100644\n--- a/.github/ISSUE_TEMPLATE/PLIP.md\n+++ b/.github/ISSUE_TEMPLATE/PLIP.md\n@@ -12,7 +12,7 @@ projects: \'plone/47\'\n \n \n \n+## 6.1.0a4 (2024-08-01)\n+\n+\n+### Breaking changes:\n+\n+- Remove `propertiestool` import step and usage of `portal_properties`.\n+ Remove `site_properties` from `main_template.pt` and ajax template.\n+ Remove `PropertiesTool` module.\n+ [maurits] #125\n+\n+\n+### Internal:\n+\n+- Update the link to the PLIPs page for Plone 6 Documentation. @stevepiercy #3988\n+\n ## 6.1.0a4.dev0 (2024-07-08)\n \n \ndiff --git a/Products/CMFPlone/PloneTool.py b/Products/CMFPlone/PloneTool.py\nindex 21fdaf37c9..0c549327a2 100644\n--- a/Products/CMFPlone/PloneTool.py\n+++ b/Products/CMFPlone/PloneTool.py\n@@ -623,9 +623,8 @@ def browserDefault(self, obj):\n # 3. If the object has a property default_page set and this gives a list\n # of, or single, object id, and that object is is found in the\n # folder or is the name of a skin template, return that id\n- # 4. If the property default_page is set in site_properties and that\n- # property contains a list of ids of which one id is found in the\n- # folder, return that id\n+ # 4. Look up the property plone.default_page in the registry for\n+ # magic ids and test these.\n # 5. If the object implements IBrowserDefault, try to get the selected\n # layout.\n # 6. If the type has a \'folderlisting\' action and no default page is\n@@ -928,7 +927,7 @@ def getUserFriendlyTypes(self, typesList=None):\n # and selection purposes.\n #\n # This is the list of types available in the portal, minus those\n- # defined in the types_not_searched property in site_properties, if it\n+ # defined in the types_not_searched property in the registry, if it\n # exists.\n #\n # If typesList is given, this is used as the base list; else all types\ndiff --git a/Products/CMFPlone/PropertiesTool.py b/Products/CMFPlone/PropertiesTool.py\ndeleted file mode 100644\nindex 4cc6feb44e..0000000000\n--- a/Products/CMFPlone/PropertiesTool.py\n+++ /dev/null\n@@ -1,144 +0,0 @@\n-from AccessControl import ClassSecurityInfo\n-from AccessControl.class_init import InitializeClass\n-from Acquisition import aq_inner\n-from Acquisition import aq_parent\n-from App.special_dtml import DTMLFile\n-from OFS.Folder import Folder\n-from OFS.PropertyManager import PropertyManager\n-from OFS.SimpleItem import SimpleItem\n-from plone.base.interfaces import IPropertiesTool\n-from plone.base.interfaces import ISimpleItemWithProperties\n-from Products.CMFCore.interfaces import ISiteRoot\n-from Products.CMFCore.permissions import ManagePortal\n-from Products.CMFCore.utils import UniqueObject\n-from Products.CMFPlone.PloneBaseTool import PloneBaseTool\n-from Products.CMFPlone.utils import WWW_DIR\n-from Products.MailHost.interfaces import IMailHost\n-from Products.PageTemplates.PageTemplateFile import PageTemplateFile\n-from zope.component import getUtility\n-from zope.component import queryUtility\n-from zope.deprecation import deprecate\n-from zope.interface import implementer\n-\n-\n-@implementer(IPropertiesTool)\n-class PropertiesTool(PloneBaseTool, Folder, UniqueObject):\n- """Plone properties tool"""\n-\n- id = "portal_properties"\n- toolicon = "skins/plone_images/topic_icon.png"\n-\n- meta_type = "Plone Properties Tool"\n- meta_types = (\n- {"name": "Plone Property Sheet", "action": "manage_addPropertySheetForm"},\n- )\n-\n- manage_options = (\n- (Folder.manage_options[0],)\n- + ({"label": "Overview", "action": "manage_overview"},)\n- + SimpleItem.manage_options\n- )\n-\n- manage_addPropertySheetForm = PageTemplateFile("www/addPropertySheet", globals())\n-\n- security = ClassSecurityInfo()\n-\n- security.declareProtected(ManagePortal, "manage_overview")\n- manage_overview = DTMLFile("explainPropertiesTool", WWW_DIR)\n-\n- def all_meta_types(self, interfaces=None):\n- return self.meta_types\n-\n- security.declareProtected(ManagePortal, "addPropertySheet")\n-\n- def addPropertySheet(self, id, title="", propertysheet=None):\n- # Add a new PropertySheet.\n- o = SimpleItemWithProperties(id, title)\n-\n- # copy the propertysheet values onto the new instance\n- if propertysheet is not None:\n- if not hasattr(propertysheet, "propertyIds"):\n- raise TypeError("propertysheet needs to be a PropertyManager")\n-\n- for property in propertysheet.propertyMap():\n- pid = property.get("id")\n- ptype = property.get("type")\n- pvalue = propertysheet.getProperty(pid)\n- if not hasattr(o, pid):\n- o._setProperty(pid, pvalue, ptype)\n-\n- self._setObject(id, o)\n-\n- security.declareProtected(ManagePortal, "manage_addPropertySheet")\n-\n- def manage_addPropertySheet(self, id, title="", propertysheet=None, REQUEST=None):\n- """Add a instance of a Property Sheet if handed a\n- propertysheet put the properties into new propertysheet.\n- """\n- self.addPropertySheet(id, title, propertysheet)\n-\n- if REQUEST is not None:\n- return self.manage_main()\n-\n- #\n- # \'portal_properties\' interface methods\n- #\n- security.declareProtected(ManagePortal, "editProperties")\n-\n- def editProperties(self, props):\n- # Change portal settings.\n- aq_parent(aq_inner(self)).manage_changeProperties(props)\n- if hasattr(self, "propertysheets"):\n- ps = self.propertysheets\n- if hasattr(ps, "props"):\n- ps.props.manage_changeProperties(props)\n-\n- def title(self):\n- site = queryUtility(ISiteRoot)\n- if site is None:\n- # fallback\n- return aq_parent(aq_inner(self)).title\n- return site.title\n-\n- def smtp_server(self):\n- return getUtility(IMailHost).smtp_host\n-\n- @deprecate(\n- "The portal portal_properties tool will be removed in Plone 6.1. "\n- "Use the portal_registry instead. "\n- "Check https://github.com/plone/Products.CMFPlone/issues/125 "\n- "for more details."\n- )\n- def hasProperty(self, id):\n- return super().hasProperty(id)\n-\n-\n-InitializeClass(PropertiesTool)\n-\n-\n-@implementer(ISimpleItemWithProperties)\n-class SimpleItemWithProperties(PropertyManager, SimpleItem):\n- """\n- A common base class for objects with configurable\n- properties in a fixed schema.\n- """\n-\n- def __init__(self, id, title=""):\n- self.id = id\n- self.title = title\n-\n- meta_type = "Plone Property Sheet"\n-\n- manage_options = PropertyManager.manage_options + SimpleItem.manage_options\n-\n- @deprecate(\n- "The portal portal_properties tool will be removed in Plone 6.1. "\n- "Use the portal_registry instead. "\n- "Check https://github.com/plone/Products.CMFPlone/issues/125 "\n- "for more details."\n- )\n- def hasProperty(self, id):\n- return super().hasProperty(id)\n-\n-\n-InitializeClass(SimpleItemWithProperties)\ndiff --git a/Products/CMFPlone/__init__.py b/Products/CMFPlone/__init__.py\nindex ff52822e9e..f9fab8c26f 100644\n--- a/Products/CMFPlone/__init__.py\n+++ b/Products/CMFPlone/__init__.py\n@@ -153,7 +153,6 @@ def initialize(context):\n from Products.CMFPlone import patches # noqa\n from Products.CMFPlone import PloneControlPanel\n from Products.CMFPlone import PloneTool\n- from Products.CMFPlone import PropertiesTool\n from Products.CMFPlone import RegistrationTool\n from Products.CMFPlone import SkinsTool\n from Products.CMFPlone import TranslationServiceTool\n@@ -166,7 +165,6 @@ def initialize(context):\n PloneTool.PloneTool,\n WorkflowTool.WorkflowTool,\n CachingPolicyManager.CachingPolicyManager,\n- PropertiesTool.PropertiesTool,\n MigrationTool.MigrationTool,\n PloneControlPanel.PloneControlPanel,\n RegistrationTool.RegistrationTool,\ndiff --git a/Products/CMFPlone/browser/author.py b/Products/CMFPlone/browser/author.py\nindex 644099ea71..2b0fef236c 100644\n--- a/Products/CMFPlone/browser/author.py\n+++ b/Products/CMFPlone/browser/author.py\n@@ -5,7 +5,6 @@\n from plone.base.interfaces.controlpanel import IMailSchema\n from plone.base.utils import pretty_title_or_id\n from plone.registry.interfaces import IRegistry\n-from Products.CMFCore.interfaces import IPropertiesTool\n from Products.CMFCore.utils import getToolByName\n from Products.Five.browser import BrowserView\n from Products.MailHost.interfaces import IMailHost\n@@ -201,11 +200,7 @@ def home_folder(self, username):\n return self.membership_tool.getHomeFolder(id=username)\n \n def __call__(self):\n- self.portal_properties = getUtility(IPropertiesTool)\n-\n self.portal_catalog = getToolByName(self.context, "portal_catalog")\n-\n- # XXX: getUtility call does not work.\n self.membership_tool = getToolByName(self.context, "portal_membership")\n \n self.portal_state = getMultiAdapter(\ndiff --git a/Products/CMFPlone/browser/templates/ajax_main_template.pt b/Products/CMFPlone/browser/templates/ajax_main_template.pt\nindex 81eab03a4a..f4efc3b8e7 100644\n--- a/Products/CMFPlone/browser/templates/ajax_main_template.pt\n+++ b/Products/CMFPlone/browser/templates/ajax_main_template.pt\n@@ -15,7 +15,6 @@\n dummy python: plone_layout.mark_view(view);\n portal_url python:portal_state.portal_url();\n checkPermission python:context.restrictedTraverse(\'portal_membership\').checkPermission;\n- site_properties python:context.restrictedTraverse(\'portal_properties\').site_properties;\n ajax_include_head python:request.get(\'ajax_include_head\', False);\n ajax_load python:False;"\n i18n:domain="plone"\ndiff --git a/Products/CMFPlone/browser/templates/main_template.pt b/Products/CMFPlone/browser/templates/main_template.pt\nindex 9bffd9a9bb..efe9eac05e 100644\n--- a/Products/CMFPlone/browser/templates/main_template.pt\n+++ b/Products/CMFPlone/browser/templates/main_template.pt\n@@ -15,7 +15,6 @@\n dummy python: plone_layout.mark_view(view);\n portal_url python:portal_state.portal_url();\n checkPermission python:context.restrictedTraverse(\'portal_membership\').checkPermission;\n- site_properties python:context.restrictedTraverse(\'portal_properties\').site_properties;\n ajax_include_head python:request.get(\'ajax_include_head\', False);\n ajax_load python:False;"\n i18n:domain="plone"\ndiff --git a/Products/CMFPlone/exportimport/configure.zcml b/Products/CMFPlone/exportimport/configure.zcml\nindex ca451845e8..b3fe625468 100644\n--- a/Products/CMFPlone/exportimport/configure.zcml\n+++ b/Products/CMFPlone/exportimport/configure.zcml\n@@ -54,19 +54,9 @@\n \n \n \n- \n \n \n \n- \n- \n- \n-\n \n \n- \n-\n \n \n- \n-\n- \n-\n \n \n- \n-\n \n-\n- Site wide properties\n- True\n-\n-"""\n-\n-_PROPERTIESTOOL_XML = b"""\\\n-\n-\n- \n- Site wide properties\n- True\n- \n-\n-"""\n-\n-\n-class PropertySheetXMLAdapterTests(BodyAdapterTestCase):\n- def _getTargetClass(self):\n- from Products.CMFPlone.exportimport.propertiestool import (\n- SimpleItemWithPropertiesXMLAdapter,\n- )\n-\n- return SimpleItemWithPropertiesXMLAdapter\n-\n- def _populate(self, obj):\n- obj.manage_changeProperties(title="Site wide properties")\n- obj.manage_addProperty("displayPublicationDateInByline", True, "boolean")\n-\n- def setUp(self):\n- from plone.base.interfaces import ISimpleItemWithProperties\n- from Products.GenericSetup.interfaces import IBody\n- from Products.GenericSetup.interfaces import ISetupEnviron\n-\n- provideAdapter(\n- self._getTargetClass(), (ISimpleItemWithProperties, ISetupEnviron), IBody\n- )\n-\n- self._obj = SimpleItemWithProperties("site_properties")\n- self._BODY = _PROPERTYSHEET_XML\n-\n-\n-class PropertiesToolXMLAdapterTests(BodyAdapterTestCase):\n- def _getTargetClass(self):\n- from Products.CMFPlone.exportimport.propertiestool import (\n- PlonePropertiesToolXMLAdapter,\n- )\n-\n- return PlonePropertiesToolXMLAdapter\n-\n- def _populate(self, obj):\n- obj._setObject("site_properties", SimpleItemWithProperties("site_properties"))\n- obj.site_properties.manage_changeProperties(title="Site wide properties")\n- obj.site_properties.manage_addProperty(\n- "displayPublicationDateInByline", True, "boolean"\n- )\n-\n- def setUp(self):\n- from plone.base.interfaces import IPropertiesTool\n- from plone.base.interfaces import ISimpleItemWithProperties\n- from Products.CMFPlone.exportimport.propertiestool import (\n- SimpleItemWithPropertiesXMLAdapter,\n- )\n- from Products.GenericSetup.interfaces import IBody\n- from Products.GenericSetup.interfaces import ISetupEnviron\n-\n- provideAdapter(self._getTargetClass(), (IPropertiesTool, ISetupEnviron), IBody)\n- provideAdapter(\n- SimpleItemWithPropertiesXMLAdapter,\n- (ISimpleItemWithProperties, ISetupEnviron),\n- IBody,\n- )\n-\n- self._obj = PropertiesTool()\n- self._BODY = _PROPERTIESTOOL_XML\n-\n-\n-def test_suite():\n- from unittest import makeSuite\n- from unittest import TestSuite\n-\n- suite = TestSuite()\n- suite.addTest(makeSuite(PropertySheetXMLAdapterTests))\n- suite.addTest(makeSuite(PropertiesToolXMLAdapterTests))\n- return suite\ndiff --git a/Products/CMFPlone/interfaces/__init__.py b/Products/CMFPlone/interfaces/__init__.py\nindex afb3e1df15..b275ed2efd 100644\n--- a/Products/CMFPlone/interfaces/__init__.py\n+++ b/Products/CMFPlone/interfaces/__init__.py\n@@ -51,8 +51,6 @@\n IPasswordResetToolView="plone.base.interfaces.password_reset:IPasswordResetToolView",\n IPWResetTool="plone.base.interfaces.password_reset:IPWResetTool",\n IPatternsSettings="plone.base.interfaces.patterns:IPatternsSettings",\n- IPropertiesTool="plone.base.interfaces.properties:IPropertiesTool",\n- ISimpleItemWithProperties="plone.base.interfaces.properties:ISimpleItemWithProperties",\n IBundleRegistry="plone.base.interfaces.resources:IBundleRegistry",\n IResourceRegistry="plone.base.interfaces.resources:IResourceRegistry",\n IMigratingPloneSiteRoot="plone.base.interfaces.siteroot:IMigratingPloneSiteRoot",\ndiff --git a/Products/CMFPlone/profiles/default/componentregistry.xml b/Products/CMFPlone/profiles/default/componentregistry.xml\nindex f35d005476..f1195c4eb2 100644\n--- a/Products/CMFPlone/profiles/default/componentregistry.xml\n+++ b/Products/CMFPlone/profiles/default/componentregistry.xml\n@@ -68,9 +68,6 @@\n \n- \n \ndiff --git a/Products/CMFPlone/profiles/default/metadata.xml b/Products/CMFPlone/profiles/default/metadata.xml\nindex d6d72c30c5..74dbe99aa0 100644\n--- a/Products/CMFPlone/profiles/default/metadata.xml\n+++ b/Products/CMFPlone/profiles/default/metadata.xml\n@@ -1,4 +1,4 @@\n \n \n- 6103\n+ 6104\n \ndiff --git a/Products/CMFPlone/profiles/default/propertiestool.xml b/Products/CMFPlone/profiles/default/propertiestool.xml\ndeleted file mode 100644\nindex d90844dacd..0000000000\n--- a/Products/CMFPlone/profiles/default/propertiestool.xml\n+++ /dev/null\n@@ -1,15 +0,0 @@\n-\n-\n- \n- NavigationTree properties\n- \n- \n- Site wide properties\n- \n-\ndiff --git a/Products/CMFPlone/profiles/default/toolset.xml b/Products/CMFPlone/profiles/default/toolset.xml\nindex dfbff83fe0..1a225138d1 100644\n--- a/Products/CMFPlone/profiles/default/toolset.xml\n+++ b/Products/CMFPlone/profiles/default/toolset.xml\n@@ -42,9 +42,6 @@\n \n- \n \ndiff --git a/Products/CMFPlone/tests/testInterfaces.py b/Products/CMFPlone/tests/testInterfaces.py\nindex a44348e62f..1766742b07 100644\n--- a/Products/CMFPlone/tests/testInterfaces.py\n+++ b/Products/CMFPlone/tests/testInterfaces.py\n@@ -6,8 +6,6 @@\n from Products.CMFPlone.PloneControlPanel import PloneControlPanel\n from Products.CMFPlone.PloneTool import PloneTool\n from Products.CMFPlone.Portal import PloneSite\n-from Products.CMFPlone.PropertiesTool import PropertiesTool\n-from Products.CMFPlone.PropertiesTool import SimpleItemWithProperties\n from Products.CMFPlone.RegistrationTool import RegistrationTool\n from Products.CMFPlone.SkinsTool import SkinsTool\n from Products.CMFPlone.TypesTool import TypesTool\n@@ -300,8 +298,6 @@ def _testStuff(self):\n (PloneConfiglet, ()),\n (PloneTool, ()),\n (PloneSite, ()),\n- (PropertiesTool, ()),\n- (SimpleItemWithProperties, ()),\n (RegistrationTool, ()),\n (SkinsTool, ()),\n (TypesTool, ()),\ndiff --git a/Products/CMFPlone/tests/testNavigationView.py b/Products/CMFPlone/tests/testNavigationView.py\nindex fa8d525a6b..aeddd0ba26 100644\n--- a/Products/CMFPlone/tests/testNavigationView.py\n+++ b/Products/CMFPlone/tests/testNavigationView.py\n@@ -121,29 +121,6 @@ def path(x):\n self.assertEqual(subfolder221map["item"].getPath(), path(subfolder221))\n self.assertEqual(len(subfolder221map["children"]), 0)\n \n- def testSitemapUnchangedWithTopLevel(self):\n- # Test that setting topLevel does not alter the sitemap\n- ntp = self.portal.portal_properties.navtree_properties\n- for topLevel in range(0, 5):\n- ntp.manage_changeProperties(topLevel=topLevel)\n- view = self.view_class(self.portal, self.request)\n- sitemap = view.siteMap()\n- self.assertEqual(\n- sitemap["children"][-1]["item"].getPath(), "/plone/folder2"\n- )\n-\n- def testSitemapUnchangedWithBottomLevel(self):\n- # Test that setting bottomLevel does not alter the sitemap\n- ntp = self.portal.portal_properties.navtree_properties\n- for bottomLevel in range(0, 5):\n- ntp.manage_changeProperties(bottomLevel=bottomLevel)\n- view = self.view_class(self.portal, self.request)\n- sitemap = view.siteMap()\n- self.assertEqual(\n- sitemap["children"][-1]["item"].getPath(), "/plone/folder2"\n- )\n- self.assertTrue(len(sitemap["children"][-1]["children"]) > 0)\n-\n def testSitemapWithNavigationRoot(self):\n self.navigation_settings.root = "/folder2"\n view = self.view_class(self.portal, self.request)\n@@ -270,7 +247,7 @@ def testTabInfo(self):\n self.assertTrue("review_state" in tab and tab["review_state"])\n \n def testDisableFolderTabs(self):\n- # Setting the site_property disable_folder_sections should remove\n+ # Setting the registry setting generate_tabs to False should remove\n # all folder based tabs\n self.navigation_settings.generate_tabs = False\n view = self.view_class(self.portal, self.request)\ndiff --git a/Products/CMFPlone/tests/testPortalCreation.py b/Products/CMFPlone/tests/testPortalCreation.py\nindex 5fa0b55ae4..57d7959e3c 100644\n--- a/Products/CMFPlone/tests/testPortalCreation.py\n+++ b/Products/CMFPlone/tests/testPortalCreation.py\n@@ -42,7 +42,6 @@ def afterSetUp(self):\n self.types = self.portal.portal_types\n self.cp = self.portal.portal_controlpanel\n self.actions = self.portal.portal_actions\n- self.properties = self.portal.portal_properties\n self.memberdata = self.portal.portal_memberdata\n self.catalog = self.portal.portal_catalog\n self.groups = self.portal.portal_groups\n@@ -146,35 +145,11 @@ def testNoPortalNavigationTool(self):\n # portal_navigation should have been removed\n self.assertFalse("portal_navigation" in self.portal)\n \n- def testNoFormProperties(self):\n- # form_properties should have been removed\n- self.assertFalse("form_properties" in self.properties)\n-\n- def testNoNavigationProperties(self):\n- # navigation_properties should have been removed\n- self.assertFalse("navigation_properties" in self.properties)\n-\n def testFormToolTipsProperty(self):\n # formtooltips should have been removed\n self.assertFalse(self.memberdata.hasProperty("formtooltips"))\n \n def testNavTreeProperties(self):\n- # navtree_properties should contain the new properties\n- self.assertFalse(\n- self.properties.navtree_properties.hasProperty("parentMetaTypesNotToQuery")\n- )\n- self.assertFalse(self.properties.navtree_properties.hasProperty("sitemapDepth"))\n- self.assertFalse(\n- self.properties.navtree_properties.hasProperty("showAllParents")\n- )\n- self.assertFalse(\n- self.properties.navtree_properties.hasProperty("metaTypesNotToList")\n- ) # noqa\n- self.assertFalse(\n- self.properties.navtree_properties.hasProperty("sortAttribute")\n- )\n- self.assertFalse(self.properties.navtree_properties.hasProperty("sortOrder"))\n-\n registry = getUtility(IRegistry)\n self.assertTrue("plone.workflow_states_to_show" in registry)\n self.assertTrue("plone.filter_on_workflow" in registry)\n@@ -939,9 +914,6 @@ def testFinalStepsWithMembersFolderDeleted(self):\n \n \n class TestManagementPageCharset(PloneTestCase.PloneTestCase):\n- def afterSetUp(self):\n- self.properties = self.portal.portal_properties\n-\n def testManagementPageCharset(self):\n manage_charset = getattr(self.portal, "management_page_charset", None)\n self.assertTrue(manage_charset)\ndiff --git a/Products/CMFPlone/tests/testWebDAV.py b/Products/CMFPlone/tests/testWebDAV.py\nindex 2105ae4390..e3938407ef 100644\n--- a/Products/CMFPlone/tests/testWebDAV.py\n+++ b/Products/CMFPlone/tests/testWebDAV.py\n@@ -18,17 +18,6 @@\n """\n \n \n-class TestDAVProperties(PloneTestCase.PloneTestCase):\n- def testPropertiesToolTitle(self):\n- ptool = getToolByName(self.portal, "portal_properties")\n- psets = dict(ptool.propertysheets.items())\n- self.assertTrue("webdav" in psets.keys())\n- default = psets["webdav"]\n- items = dict(default.propertyItems())\n- self.assertTrue("displayname" in items.keys())\n- self.assertEqual(items["displayname"], ptool.title)\n-\n-\n class TestPUTObjects(PloneTestCase.PloneTestCase):\n # PUT objects into Plone including special cases like index_html.\n # Confirms fix for http://dev.plone.org/plone/ticket/1375\ndiff --git a/Products/CMFPlone/tests/test_defaultpage.py b/Products/CMFPlone/tests/test_defaultpage.py\nindex 3be7eafd7d..ac633b5d6b 100644\n--- a/Products/CMFPlone/tests/test_defaultpage.py\n+++ b/Products/CMFPlone/tests/test_defaultpage.py\n@@ -106,8 +106,8 @@ def test_get_default_page_step_3_2(self):\n self.assertEqual("d1", get_default_page(self.folder))\n \n def test_get_default_page_step_4(self):\n- # 4. Else, look up the property default_page in site_properties for\n- # magic ids and test these\n+ # 4. Else, look up the property plone.default_page in the registry for\n+ # magic ids and test these\n registry = getUtility(IRegistry)\n registry["plone.default_page"] = ["d1"]\n self.folder.invokeFactory("Document", "d1", title="Doc 1")\ndiff --git a/Products/CMFPlone/tests/test_zmi.py b/Products/CMFPlone/tests/test_zmi.py\nindex 3ec5997246..8be5e75e98 100644\n--- a/Products/CMFPlone/tests/test_zmi.py\n+++ b/Products/CMFPlone/tests/test_zmi.py\n@@ -121,11 +121,6 @@ def test_portal_modifier(self):\n view = self.portal.restrictedTraverse(url)\n self.assertTrue(view(), msg=f"{url} is broken")\n \n- def test_portal_properties(self):\n- url = "portal_properties/manage_main"\n- view = self.portal.restrictedTraverse(url)\n- self.assertTrue(view(), msg=f"{url} is broken")\n-\n def test_portal_purgepolicy(self):\n url = "portal_purgepolicy/manage_propertiesForm"\n view = self.portal.restrictedTraverse(url)\ndiff --git a/Products/CMFPlone/www/addPropertySheet.zpt b/Products/CMFPlone/www/addPropertySheet.zpt\ndeleted file mode 100644\nindex 2d01f3bcda..0000000000\n--- a/Products/CMFPlone/www/addPropertySheet.zpt\n+++ /dev/null\n@@ -1,39 +0,0 @@\n-\n-\n-\n-\n-\n-Zope\n-\n-\n-\n-\n-\n-
\n- Add Propertysheet\n-
\n-\n-
\n-
\n- Enter an ID and title to add a new PropertySheet.\n-
\n-
\n- Id\n-
\n- \n-\n-
\n- Title\n-
\n- \n-\n-
\n- \n-
\n-
\n-\n- \n-\ndiff --git a/news/6104.internal b/news/6104.internal\nnew file mode 100644\nindex 0000000000..05e73167c4\n--- /dev/null\n+++ b/news/6104.internal\n@@ -0,0 +1,2 @@\n+Updated metadata version to 6104.\n+[maurits]\ndiff --git a/setup.py b/setup.py\nindex 61c25104b5..eba4785df4 100644\n--- a/setup.py\n+++ b/setup.py\n@@ -2,7 +2,7 @@\n from setuptools import setup\n \n \n-version = "6.1.0a4.dev1"\n+version = "6.1.0a5.dev0"\n \n \n setup(\n' +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' -Repository: Products.CMFPlone +Repository: plone.app.dexterity Branch: refs/heads/master -Date: 2024-08-13T18:52:20+02:00 +Date: 2024-09-02T16:35:07+02:00 Author: Maurits van Rees (mauritsvanrees) -Commit: https://github.com/plone/Products.CMFPlone/commit/67133211feb496fd2912911b1dffafb141ac7744 - -Merge branch 'master' into pa-discussion-core-addon - -Files changed: -A news/3860.bugfix -A news/3998.bugfix -M Products/CMFPlone/browser/search.py -M Products/CMFPlone/browser/templates/search.pt -M Products/CMFPlone/utils.py - -b'diff --git a/Products/CMFPlone/browser/search.py b/Products/CMFPlone/browser/search.py\nindex ae4d6cbe27..505f493748 100644\n--- a/Products/CMFPlone/browser/search.py\n+++ b/Products/CMFPlone/browser/search.py\n@@ -14,6 +14,7 @@\n from zope.component import queryUtility\n from zope.i18nmessageid import MessageFactory\n from zope.publisher.browser import BrowserView\n+from zope.schema.interfaces import IVocabularyFactory\n from ZTUtils import make_query\n \n import json\n@@ -183,7 +184,28 @@ def filter_types(self, types):\n plone_utils = getToolByName(self.context, "plone_utils")\n if not isinstance(types, list):\n types = [types]\n- return plone_utils.getUserFriendlyTypes(types)\n+\n+ # We want to have the configured types to be exposed in the search sorted for humans (by translated Title).\n+ # Those are stored in the Plone registry. They are called here UserFriendlyTypes.\n+ #\n+ # Confusingly, on the other hand we have the ReallyUserFriendlyTypes vocabulary from\n+ # plone.app.vocabularies, which is already sorted accordingly and contain all possible types,\n+ # except "Temp Folder", "Plone Site" and deprecated types.\n+\n+ # fetch the sorted ReallyUserFriendlyTypes vocabulary\n+ vocab_factory = queryUtility(\n+ IVocabularyFactory, "plone.app.vocabularies.ReallyUserFriendlyTypes"\n+ )\n+ vocab = vocab_factory(self.context)\n+\n+ # get the configured values from the registry and pass the input types to be reduced to possible values\n+ user_friendly_types = plone_utils.getUserFriendlyTypes(types)\n+\n+ # Filter the sorted ReallyUserFriendlyTypes down to the configured values from the registry,\n+ # but keep the order.\n+ sorted_types = [term.value for term in vocab if term.value in user_friendly_types]\n+\n+ return sorted_types\n \n def types_list(self):\n # only show those types that have any content\ndiff --git a/Products/CMFPlone/browser/templates/search.pt b/Products/CMFPlone/browser/templates/search.pt\nindex 3e0d611acf..5e89454238 100644\n--- a/Products/CMFPlone/browser/templates/search.pt\n+++ b/Products/CMFPlone/browser/templates/search.pt\n@@ -87,7 +87,7 @@\n \n
\n \n-