From ce210133803ff561e4793ab2dd5318295423328f Mon Sep 17 00:00:00 2001 From: davisagli Date: Mon, 26 Aug 2024 11:44:48 -0700 Subject: [PATCH] [fc] Repository: plone.restapi Branch: refs/heads/main Date: 2024-08-26T11:44:48-07:00 Author: Jefferson Bledsoe (JeffersonBledsoe) Commit: https://github.com/plone/plone.restapi/commit/37d02e7772b1b402ef74b8e55bc7ea4ad786d014 Include next/previous only if enabled (#1799) * Add failing test for next/ previous behaviour * Return empty dict if behaviour or toggle is disabled * Fix missing import in test * Remove bad part of test * Fix some git weirdness? * Fix implementation * changelog * skip tests that require DX site root in Plone 5.2 --------- Co-authored-by: David Glick <david@glicksoftware.com> Files changed: A news/1799.bugfix M plone-6.0.x-python3.8.cfg M plone-6.0.x.cfg M requirements-6.0.txt M src/plone/restapi/serializer/nextprev.py M src/plone/restapi/tests/http-examples/collection.resp M src/plone/restapi/tests/http-examples/collection_fullobjects.resp M src/plone/restapi/tests/http-examples/content_get.resp M src/plone/restapi/tests/http-examples/content_get_folder.resp M src/plone/restapi/tests/http-examples/content_patch_representation.resp M src/plone/restapi/tests/http-examples/content_post.resp M src/plone/restapi/tests/http-examples/event.resp M src/plone/restapi/tests/http-examples/file.resp M src/plone/restapi/tests/http-examples/folder.resp M src/plone/restapi/tests/http-examples/image.resp M src/plone/restapi/tests/http-examples/link.resp M src/plone/restapi/tests/http-examples/navroot_lang_content_get.resp M src/plone/restapi/tests/http-examples/navroot_lang_folder_get.resp M src/plone/restapi/tests/http-examples/newsitem.resp M src/plone/restapi/tests/http-examples/search_fullobjects.resp M src/plone/restapi/tests/http-examples/site_get_expand_lang_folder.resp M src/plone/restapi/tests/http-examples/site_get_expand_lang_folder_content.resp M src/plone/restapi/tests/http-examples/translated_messages_types_folder.resp M src/plone/restapi/tests/http-examples/translations_expand_get.resp M src/plone/restapi/tests/http-examples/translations_link_on_post.resp M src/plone/restapi/tests/http-examples/translations_unexpanded_get.resp M src/plone/restapi/tests/http-examples/workingcopy_baseline_get.resp M src/plone/restapi/tests/http-examples/workingcopy_wc_get.resp M src/plone/restapi/tests/test_dxcontent_serializer.py --- last_commit.txt | 122 ++++++++++++++++++++++++------------------------ 1 file changed, 60 insertions(+), 62 deletions(-) diff --git a/last_commit.txt b/last_commit.txt index 3290400fe9..2c5515e070 100644 --- a/last_commit.txt +++ b/last_commit.txt @@ -1,65 +1,63 @@ -Repository: plone.app.event - - -Branch: refs/heads/master -Date: 2024-08-19T16:30:05+02:00 -Author: Peter Mathis (petschki) -Commit: https://github.com/plone/plone.app.event/commit/0fb4a8b2155c4e3377e055993156716e7d4d27e6 - -Fix date conversion when collection context has start/end date queries - -Files changed: -M plone/app/event/browser/event_listing.py -M plone/app/event/tests/test_event_listing.py - -b'diff --git a/plone/app/event/browser/event_listing.py b/plone/app/event/browser/event_listing.py\nindex 55c0f2e1..e01de705 100644\n--- a/plone/app/event/browser/event_listing.py\n+++ b/plone/app/event/browser/event_listing.py\n@@ -222,6 +222,13 @@ def _expand_events_start_end(self, start, end):\n # XXX: This actually fits most needs, but not all. Maybe someone\n # wants to come up with some edgecases!\n se = dict(start=None, end=None)\n+\n+ def safe_dt(val):\n+ # convert value from DateTime to datetime\n+ if hasattr(val, "asdatetime"):\n+ return val.asdatetime()\n+ return val\n+\n if start:\n q = start.get("query")\n r = start.get("range")\n@@ -238,7 +245,7 @@ def _expand_events_start_end(self, start, end):\n r = end.get("range")\n if r == "min":\n se["start"] = q\n- return se["start"], se["end"]\n+ return safe_dt(se["start"]), safe_dt(se["end"])\n \n def formatted_date(self, occ):\n provider = getMultiAdapter(\ndiff --git a/plone/app/event/tests/test_event_listing.py b/plone/app/event/tests/test_event_listing.py\nindex 5eb94662..abab4e12 100644\n--- a/plone/app/event/tests/test_event_listing.py\n+++ b/plone/app/event/tests/test_event_listing.py\n@@ -1,3 +1,4 @@\n+from DateTime import DateTime\n from plone.app.event.base import localized_today\n from plone.app.event.testing import make_fake_response\n from plone.app.event.testing import PAEventDX_INTEGRATION_TESTING\n@@ -151,3 +152,26 @@ def test_no_date_filtering(self):\n "mode_day",\n ]:\n self.assertTrue(_class not in out)\n+\n+ @mock.patch("plone.app.event.browser.event_listing.localized_now", new=PN)\n+ @mock.patch("plone.app.event.base.localized_now", new=PN)\n+ def test_end_date_filtering(self):\n+ """Test date conversion between collection query and \'expand_events\'\n+ filtering.\n+ """\n+ # plone.app.contenttypes ICollection type\n+ self.portal.invokeFactory("Collection", "col_with_date_criterion", title="Col")\n+ collection = self.portal.col_with_date_criterion\n+ collection.query = [\n+ {\n+ "i": "portal_type",\n+ "o": "plone.app.querystring.operation.selection.any",\n+ "v": ["Event", "plone.app.event.dx.event"],\n+ },\n+ {\n+ "i": "end",\n+ "o": "plone.app.querystring.operation.date.largerThan",\n+ "v": DateTime(PN()),\n+ },\n+ ]\n+ self.assertIs(len(self.portal.col_with_date_criterion.results()), 3)\n' - -Repository: plone.app.event - - -Branch: refs/heads/master -Date: 2024-08-19T16:36:19+02:00 -Author: Peter Mathis (petschki) -Commit: https://github.com/plone/plone.app.event/commit/f6a3aa8244bbcac89630a336cb8f148061ca2907 - -changenote - -Files changed: -A news/404.bugfix - -b'diff --git a/news/404.bugfix b/news/404.bugfix\nnew file mode 100644\nindex 000000000..26707eee0\n--- /dev/null\n+++ b/news/404.bugfix\n@@ -0,0 +1,2 @@\n+Fix date conversion between collection query and ``expand_events`` filtering.\n+[petschki]\n' - -Repository: plone.app.event - - -Branch: refs/heads/master -Date: 2024-08-19T16:40:16+02:00 -Author: Peter Mathis (petschki) -Commit: https://github.com/plone/plone.app.event/commit/22ce428cd56187d5327b34b1611359d9064dd56b - -call event_listing view in test to test conversion +Repository: plone.restapi + + +Branch: refs/heads/main +Date: 2024-08-26T11:44:48-07:00 +Author: Jefferson Bledsoe (JeffersonBledsoe) +Commit: https://github.com/plone/plone.restapi/commit/37d02e7772b1b402ef74b8e55bc7ea4ad786d014 + +Include next/previous only if enabled (#1799) + +* Add failing test for next/ previous behaviour + +* Return empty dict if behaviour or toggle is disabled + +* Fix missing import in test + +* Remove bad part of test + +* Fix some git weirdness? + +* Fix implementation + +* changelog + +* skip tests that require DX site root in Plone 5.2 + +--------- + +Co-authored-by: David Glick <david@glicksoftware.com> Files changed: -M plone/app/event/tests/test_event_listing.py - -b'diff --git a/plone/app/event/tests/test_event_listing.py b/plone/app/event/tests/test_event_listing.py\nindex abab4e12..db67bbd8 100644\n--- a/plone/app/event/tests/test_event_listing.py\n+++ b/plone/app/event/tests/test_event_listing.py\n@@ -174,4 +174,8 @@ def test_end_date_filtering(self):\n "v": DateTime(PN()),\n },\n ]\n+\n+ view = self.portal.col_with_date_criterion.restrictedTraverse("@@event_listing")\n+ view()\n+\n self.assertIs(len(self.portal.col_with_date_criterion.results()), 3)\n' - -Repository: plone.app.event - - -Branch: refs/heads/master -Date: 2024-08-22T09:09:35+02:00 -Author: Peter Mathis (petschki) -Commit: https://github.com/plone/plone.app.event/commit/8a991b10d2e9ebeee76edf36c6dedbb68b253679 - -Merge pull request #404 from plone/eventlisting_date_filter - -Fix date conversion when collection context has start/end date queries - -Files changed: -A news/404.bugfix -M plone/app/event/browser/event_listing.py -M plone/app/event/tests/test_event_listing.py - -b'diff --git a/news/404.bugfix b/news/404.bugfix\nnew file mode 100644\nindex 00000000..26707eee\n--- /dev/null\n+++ b/news/404.bugfix\n@@ -0,0 +1,2 @@\n+Fix date conversion between collection query and ``expand_events`` filtering.\n+[petschki]\ndiff --git a/plone/app/event/browser/event_listing.py b/plone/app/event/browser/event_listing.py\nindex 55c0f2e1..e01de705 100644\n--- a/plone/app/event/browser/event_listing.py\n+++ b/plone/app/event/browser/event_listing.py\n@@ -222,6 +222,13 @@ def _expand_events_start_end(self, start, end):\n # XXX: This actually fits most needs, but not all. Maybe someone\n # wants to come up with some edgecases!\n se = dict(start=None, end=None)\n+\n+ def safe_dt(val):\n+ # convert value from DateTime to datetime\n+ if hasattr(val, "asdatetime"):\n+ return val.asdatetime()\n+ return val\n+\n if start:\n q = start.get("query")\n r = start.get("range")\n@@ -238,7 +245,7 @@ def _expand_events_start_end(self, start, end):\n r = end.get("range")\n if r == "min":\n se["start"] = q\n- return se["start"], se["end"]\n+ return safe_dt(se["start"]), safe_dt(se["end"])\n \n def formatted_date(self, occ):\n provider = getMultiAdapter(\ndiff --git a/plone/app/event/tests/test_event_listing.py b/plone/app/event/tests/test_event_listing.py\nindex 5eb94662..db67bbd8 100644\n--- a/plone/app/event/tests/test_event_listing.py\n+++ b/plone/app/event/tests/test_event_listing.py\n@@ -1,3 +1,4 @@\n+from DateTime import DateTime\n from plone.app.event.base import localized_today\n from plone.app.event.testing import make_fake_response\n from plone.app.event.testing import PAEventDX_INTEGRATION_TESTING\n@@ -151,3 +152,30 @@ def test_no_date_filtering(self):\n "mode_day",\n ]:\n self.assertTrue(_class not in out)\n+\n+ @mock.patch("plone.app.event.browser.event_listing.localized_now", new=PN)\n+ @mock.patch("plone.app.event.base.localized_now", new=PN)\n+ def test_end_date_filtering(self):\n+ """Test date conversion between collection query and \'expand_events\'\n+ filtering.\n+ """\n+ # plone.app.contenttypes ICollection type\n+ self.portal.invokeFactory("Collection", "col_with_date_criterion", title="Col")\n+ collection = self.portal.col_with_date_criterion\n+ collection.query = [\n+ {\n+ "i": "portal_type",\n+ "o": "plone.app.querystring.operation.selection.any",\n+ "v": ["Event", "plone.app.event.dx.event"],\n+ },\n+ {\n+ "i": "end",\n+ "o": "plone.app.querystring.operation.date.largerThan",\n+ "v": DateTime(PN()),\n+ },\n+ ]\n+\n+ view = self.portal.col_with_date_criterion.restrictedTraverse("@@event_listing")\n+ view()\n+\n+ self.assertIs(len(self.portal.col_with_date_criterion.results()), 3)\n' +A news/1799.bugfix +M plone-6.0.x-python3.8.cfg +M plone-6.0.x.cfg +M requirements-6.0.txt +M src/plone/restapi/serializer/nextprev.py +M src/plone/restapi/tests/http-examples/collection.resp +M src/plone/restapi/tests/http-examples/collection_fullobjects.resp +M src/plone/restapi/tests/http-examples/content_get.resp +M src/plone/restapi/tests/http-examples/content_get_folder.resp +M src/plone/restapi/tests/http-examples/content_patch_representation.resp +M src/plone/restapi/tests/http-examples/content_post.resp +M src/plone/restapi/tests/http-examples/event.resp +M src/plone/restapi/tests/http-examples/file.resp +M src/plone/restapi/tests/http-examples/folder.resp +M src/plone/restapi/tests/http-examples/image.resp +M src/plone/restapi/tests/http-examples/link.resp +M src/plone/restapi/tests/http-examples/navroot_lang_content_get.resp +M src/plone/restapi/tests/http-examples/navroot_lang_folder_get.resp +M src/plone/restapi/tests/http-examples/newsitem.resp +M src/plone/restapi/tests/http-examples/search_fullobjects.resp +M src/plone/restapi/tests/http-examples/site_get_expand_lang_folder.resp +M src/plone/restapi/tests/http-examples/site_get_expand_lang_folder_content.resp +M src/plone/restapi/tests/http-examples/translated_messages_types_folder.resp +M src/plone/restapi/tests/http-examples/translations_expand_get.resp +M src/plone/restapi/tests/http-examples/translations_link_on_post.resp +M src/plone/restapi/tests/http-examples/translations_unexpanded_get.resp +M src/plone/restapi/tests/http-examples/workingcopy_baseline_get.resp +M src/plone/restapi/tests/http-examples/workingcopy_wc_get.resp +M src/plone/restapi/tests/test_dxcontent_serializer.py + +b'diff --git a/news/1799.bugfix b/news/1799.bugfix\nnew file mode 100644\nindex 000000000..171db7f10\n--- /dev/null\n+++ b/news/1799.bugfix\n@@ -0,0 +1,4 @@\n+Fixed Dexterity content serializer:\n+Return an empty object for `next_item` and `previous_item`\n+unless the parent has next/previous support enabled.\n+@JeffersonBledsoe, @davisagli\ndiff --git a/plone-6.0.x-python3.8.cfg b/plone-6.0.x-python3.8.cfg\nindex 9fca1a2f0..2bd12daf3 100644\n--- a/plone-6.0.x-python3.8.cfg\n+++ b/plone-6.0.x-python3.8.cfg\n@@ -1,6 +1,6 @@\n [buildout]\n extends =\n- https://dist.plone.org/release/6.0.11.1/versions.cfg\n+ https://dist.plone.org/release/6.0.12/versions.cfg\n base.cfg\n \n [instance]\ndiff --git a/plone-6.0.x.cfg b/plone-6.0.x.cfg\nindex 5172c9dda..26373fac8 100644\n--- a/plone-6.0.x.cfg\n+++ b/plone-6.0.x.cfg\n@@ -1,6 +1,6 @@\n [buildout]\n extends =\n- https://dist.plone.org/release/6.0.11.1/versions.cfg\n+ https://dist.plone.org/release/6.0.12/versions.cfg\n base.cfg\n \n [buildout:python37]\ndiff --git a/requirements-6.0.txt b/requirements-6.0.txt\nindex 2f870811b..b654a46a9 100644\n--- a/requirements-6.0.txt\n+++ b/requirements-6.0.txt\n@@ -1 +1 @@\n--r https://dist.plone.org/release/6.0.11.1/requirements.txt\n+-r https://dist.plone.org/release/6.0.12/requirements.txt\ndiff --git a/src/plone/restapi/serializer/nextprev.py b/src/plone/restapi/serializer/nextprev.py\nindex ffcd0e29f..6d11ef323 100644\n--- a/src/plone/restapi/serializer/nextprev.py\n+++ b/src/plone/restapi/serializer/nextprev.py\n@@ -1,24 +1,9 @@\n-from AccessControl import getSecurityManager\n from Acquisition import aq_inner\n from Acquisition import aq_parent\n-from plone.app.dexterity.behaviors.nextprevious import NextPreviousBase\n-from plone.registry.interfaces import IRegistry\n+from plone.app.dexterity.behaviors.nextprevious import (\n+ INextPreviousProvider,\n+)\n from plone.restapi.serializer.utils import get_portal_type_title\n-from zope.component import getUtility\n-\n-\n-class NextPreviousFixed(NextPreviousBase):\n- """\n- Based on plone.app.dexterity.behaviors.nextprevious.NextPreviousBase\n- but works for IPloneSite object\n- """\n-\n- def __init__(self, context):\n- self.context = context\n- registry = getUtility(IRegistry)\n- self.vat = registry.get("plone.types_use_view_action_in_listings", [])\n- self.security = getSecurityManager()\n- self.order = self.context.objectIds()\n \n \n class NextPrevious:\n@@ -27,11 +12,14 @@ class NextPrevious:\n def __init__(self, context):\n self.context = context\n self.parent = aq_parent(aq_inner(context))\n- self.nextprev = NextPreviousFixed(self.parent)\n+ self.nextprev = INextPreviousProvider(self.parent, None)\n+ self.enabled = self.nextprev is not None and self.nextprev.enabled\n \n @property\n def next(self):\n """return info about the next item in the container"""\n+ if not self.enabled:\n+ return {}\n if getattr(self.parent, "_ordering", "") == "unordered":\n # Unordered folder\n return {}\n@@ -49,6 +37,8 @@ def next(self):\n @property\n def previous(self):\n """return info about the previous item in the container"""\n+ if not self.enabled:\n+ return {}\n if getattr(self.parent, "_ordering", "") == "unordered":\n # Unordered folder\n return {}\ndiff --git a/src/plone/restapi/tests/http-examples/collection.resp b/src/plone/restapi/tests/http-examples/collection.resp\nindex 036a0bfd2..5a839d300 100644\n--- a/src/plone/restapi/tests/http-examples/collection.resp\n+++ b/src/plone/restapi/tests/http-examples/collection.resp\n@@ -97,13 +97,7 @@ Content-Type: application/json\n "stealable": true\n },\n "modified": "1995-07-31T17:30:00+00:00",\n- "next_item": {\n- "@id": "http://localhost:55001/plone/doc1",\n- "@type": "Document",\n- "description": "",\n- "title": "Document 1",\n- "type_title": "Page"\n- },\n+ "next_item": {},\n "parent": {\n "@id": "http://localhost:55001/plone",\n "@type": "Plone Site",\n@@ -111,13 +105,7 @@ Content-Type: application/json\n "title": "Plone site",\n "type_title": "Plone Site"\n },\n- "previous_item": {\n- "@id": "http://localhost:55001/plone/front-page",\n- "@type": "Document",\n- "description": "Congratulations! You have successfully installed Plone.",\n- "title": "Welcome to Plone",\n- "type_title": "Page"\n- },\n+ "previous_item": {},\n "query": [\n {\n "i": "portal_type",\ndiff --git a/src/plone/restapi/tests/http-examples/collection_fullobjects.resp b/src/plone/restapi/tests/http-examples/collection_fullobjects.resp\nindex 04b573bff..3836845d8 100644\n--- a/src/plone/restapi/tests/http-examples/collection_fullobjects.resp\n+++ b/src/plone/restapi/tests/http-examples/collection_fullobjects.resp\n@@ -113,13 +113,7 @@ Content-Type: application/json\n "stealable": true\n },\n "modified": "1995-07-31T17:30:00+00:00",\n- "next_item": {\n- "@id": "http://localhost:55001/plone/collection",\n- "@type": "Collection",\n- "description": "This is a collection with two documents",\n- "title": "My Collection",\n- "type_title": "Collection"\n- },\n+ "next_item": {},\n "parent": {\n "@id": "http://localhost:55001/plone",\n "@type": "Plone Site",\n@@ -195,13 +189,7 @@ Content-Type: application/json\n "stealable": true\n },\n "modified": "1995-07-31T17:30:00+00:00",\n- "next_item": {\n- "@id": "http://localhost:55001/plone/doc2",\n- "@type": "Document",\n- "description": "",\n- "title": "Document 2",\n- "type_title": "Page"\n- },\n+ "next_item": {},\n "parent": {\n "@id": "http://localhost:55001/plone",\n "@type": "Plone Site",\n@@ -209,13 +197,7 @@ Content-Type: application/json\n "title": "Plone site",\n "type_title": "Plone Site"\n },\n- "previous_item": {\n- "@id": "http://localhost:55001/plone/collection",\n- "@type": "Collection",\n- "description": "This is a collection with two documents",\n- "title": "My Collection",\n- "type_title": "Collection"\n- },\n+ "previous_item": {},\n "relatedItems": [],\n "review_state": "private",\n "rights": "",\n@@ -287,13 +269,7 @@ Content-Type: application/json\n "title": "Plone site",\n "type_title": "Plone Site"\n },\n- "previous_item": {\n- "@id": "http://localhost:55001/plone/doc1",\n- "@type": "Document",\n- "description": "",\n- "title": "Document 1",\n- "type_title": "Page"\n- },\n+ "previous_item": {},\n "relatedItems": [],\n "review_state": "private",\n "rights": "",\n@@ -317,13 +293,7 @@ Content-Type: application/json\n "stealable": true\n },\n "modified": "1995-07-31T17:30:00+00:00",\n- "next_item": {\n- "@id": "http://localhost:55001/plone/doc1",\n- "@type": "Document",\n- "description": "",\n- "title": "Document 1",\n- "type_title": "Page"\n- },\n+ "next_item": {},\n "parent": {\n "@id": "http://localhost:55001/plone",\n "@type": "Plone Site",\n@@ -331,13 +301,7 @@ Content-Type: application/json\n "title": "Plone site",\n "type_title": "Plone Site"\n },\n- "previous_item": {\n- "@id": "http://localhost:55001/plone/front-page",\n- "@type": "Document",\n- "description": "Congratulations! You have successfully installed Plone.",\n- "title": "Welcome to Plone",\n- "type_title": "Page"\n- },\n+ "previous_item": {},\n "query": [\n {\n "i": "portal_type",\ndiff --git a/src/plone/restapi/tests/http-examples/content_get.resp b/src/plone/restapi/tests/http-examples/content_get.resp\nindex 9d876dccf..ad574ee2f 100644\n--- a/src/plone/restapi/tests/http-examples/content_get.resp\n+++ b/src/plone/restapi/tests/http-examples/content_get.resp\n@@ -60,13 +60,7 @@ Content-Type: application/json\n "title": "My Folder",\n "type_title": "Folder"\n },\n- "previous_item": {\n- "@id": "http://localhost:55001/plone/folder/doc2",\n- "@type": "Document",\n- "description": "",\n- "title": "A document within a folder",\n- "type_title": "Page"\n- },\n+ "previous_item": {},\n "relatedItems": [],\n "review_state": "private",\n "rights": "",\ndiff --git a/src/plone/restapi/tests/http-examples/content_get_folder.resp b/src/plone/restapi/tests/http-examples/content_get_folder.resp\nindex c7e9f35e7..7c2915f48 100644\n--- a/src/plone/restapi/tests/http-examples/content_get_folder.resp\n+++ b/src/plone/restapi/tests/http-examples/content_get_folder.resp\n@@ -89,13 +89,7 @@ Content-Type: application/json\n "title": "Plone site",\n "type_title": "Plone Site"\n },\n- "previous_item": {\n- "@id": "http://localhost:55001/plone/front-page",\n- "@type": "Document",\n- "description": "Congratulations! You have successfully installed Plone.",\n- "title": "Welcome to Plone",\n- "type_title": "Page"\n- },\n+ "previous_item": {},\n "relatedItems": [],\n "review_state": "private",\n "rights": "",\ndiff --git a/src/plone/restapi/tests/http-examples/content_patch_representation.resp b/src/plone/restapi/tests/http-examples/content_patch_representation.resp\nindex 0003cae47..bbc788a94 100644\n--- a/src/plone/restapi/tests/http-examples/content_patch_representation.resp\n+++ b/src/plone/restapi/tests/http-examples/content_patch_representation.resp\n@@ -60,13 +60,7 @@ Content-Type: application/json\n "title": "My Folder",\n "type_title": "Folder"\n },\n- "previous_item": {\n- "@id": "http://localhost:55001/plone/folder/doc2",\n- "@type": "Document",\n- "description": "",\n- "title": "A document within a folder",\n- "type_title": "Page"\n- },\n+ "previous_item": {},\n "relatedItems": [],\n "review_state": "private",\n "rights": "",\ndiff --git a/src/plone/restapi/tests/http-examples/content_post.resp b/src/plone/restapi/tests/http-examples/content_post.resp\nindex 9cd542a49..99c1f3c4b 100644\n--- a/src/plone/restapi/tests/http-examples/content_post.resp\n+++ b/src/plone/restapi/tests/http-examples/content_post.resp\n@@ -61,13 +61,7 @@ Location: http://localhost:55001/plone/folder/my-document\n "title": "My Folder",\n "type_title": "Folder"\n },\n- "previous_item": {\n- "@id": "http://localhost:55001/plone/folder/doc2",\n- "@type": "Document",\n- "description": "",\n- "title": "A document within a folder",\n- "type_title": "Page"\n- },\n+ "previous_item": {},\n "relatedItems": [],\n "review_state": "private",\n "rights": "",\ndiff --git a/src/plone/restapi/tests/http-examples/event.resp b/src/plone/restapi/tests/http-examples/event.resp\nindex 2591e6180..7cf0051a3 100644\n--- a/src/plone/restapi/tests/http-examples/event.resp\n+++ b/src/plone/restapi/tests/http-examples/event.resp\n@@ -67,13 +67,7 @@ Content-Type: application/json\n "title": "Plone site",\n "type_title": "Plone Site"\n },\n- "previous_item": {\n- "@id": "http://localhost:55001/plone/front-page",\n- "@type": "Document",\n- "description": "Congratulations! You have successfully installed Plone.",\n- "title": "Welcome to Plone",\n- "type_title": "Page"\n- },\n+ "previous_item": {},\n "recurrence": null,\n "relatedItems": [],\n "review_state": "private",\ndiff --git a/src/plone/restapi/tests/http-examples/file.resp b/src/plone/restapi/tests/http-examples/file.resp\nindex 4b463c60f..1f2f7ad12 100644\n--- a/src/plone/restapi/tests/http-examples/file.resp\n+++ b/src/plone/restapi/tests/http-examples/file.resp\n@@ -61,13 +61,7 @@ Content-Type: application/json\n "title": "Plone site",\n "type_title": "Plone Site"\n },\n- "previous_item": {\n- "@id": "http://localhost:55001/plone/front-page",\n- "@type": "Document",\n- "description": "Congratulations! You have successfully installed Plone.",\n- "title": "Welcome to Plone",\n- "type_title": "Page"\n- },\n+ "previous_item": {},\n "relatedItems": [],\n "review_state": null,\n "rights": "",\ndiff --git a/src/plone/restapi/tests/http-examples/folder.resp b/src/plone/restapi/tests/http-examples/folder.resp\nindex 7a41f8227..ad3dd229b 100644\n--- a/src/plone/restapi/tests/http-examples/folder.resp\n+++ b/src/plone/restapi/tests/http-examples/folder.resp\n@@ -75,13 +75,7 @@ Content-Type: application/json\n "title": "Plone site",\n "type_title": "Plone Site"\n },\n- "previous_item": {\n- "@id": "http://localhost:55001/plone/front-page",\n- "@type": "Document",\n- "description": "Congratulations! You have successfully installed Plone.",\n- "title": "Welcome to Plone",\n- "type_title": "Page"\n- },\n+ "previous_item": {},\n "relatedItems": [],\n "review_state": "private",\n "rights": "",\ndiff --git a/src/plone/restapi/tests/http-examples/image.resp b/src/plone/restapi/tests/http-examples/image.resp\nindex c2e13cbd1..e459b587c 100644\n--- a/src/plone/restapi/tests/http-examples/image.resp\n+++ b/src/plone/restapi/tests/http-examples/image.resp\n@@ -120,13 +120,7 @@ Content-Type: application/json\n "title": "Plone site",\n "type_title": "Plone Site"\n },\n- "previous_item": {\n- "@id": "http://localhost:55001/plone/front-page",\n- "@type": "Document",\n- "description": "Congratulations! You have successfully installed Plone.",\n- "title": "Welcome to Plone",\n- "type_title": "Page"\n- },\n+ "previous_item": {},\n "relatedItems": [],\n "review_state": null,\n "rights": "",\ndiff --git a/src/plone/restapi/tests/http-examples/link.resp b/src/plone/restapi/tests/http-examples/link.resp\nindex 76d990359..64ac54490 100644\n--- a/src/plone/restapi/tests/http-examples/link.resp\n+++ b/src/plone/restapi/tests/http-examples/link.resp\n@@ -56,13 +56,7 @@ Content-Type: application/json\n "title": "Plone site",\n "type_title": "Plone Site"\n },\n- "previous_item": {\n- "@id": "http://localhost:55001/plone/front-page",\n- "@type": "Document",\n- "description": "Congratulations! You have successfully installed Plone.",\n- "title": "Welcome to Plone",\n- "type_title": "Page"\n- },\n+ "previous_item": {},\n "remoteUrl": "http://localhost:55001/plone",\n "review_state": "private",\n "rights": "",\ndiff --git a/src/plone/restapi/tests/http-examples/navroot_lang_content_get.resp b/src/plone/restapi/tests/http-examples/navroot_lang_content_get.resp\nindex d537815af..cfbf9e1cf 100644\n--- a/src/plone/restapi/tests/http-examples/navroot_lang_content_get.resp\n+++ b/src/plone/restapi/tests/http-examples/navroot_lang_content_get.resp\n@@ -74,13 +74,7 @@ Content-Type: application/json\n "layout": "folder_listing",\n "lock": {},\n "modified": "1995-07-31T17:30:00+00:00",\n- "next_item": {\n- "@id": "http://localhost:55001/plone/de",\n- "@type": "LRF",\n- "description": "",\n- "title": "Deutsch",\n- "type_title": "Language Root Folder"\n- },\n+ "next_item": {},\n "parent": {\n "@id": "http://localhost:55001/plone",\n "@type": "Plone Site",\ndiff --git a/src/plone/restapi/tests/http-examples/navroot_lang_folder_get.resp b/src/plone/restapi/tests/http-examples/navroot_lang_folder_get.resp\nindex 801625f6d..3932a805e 100644\n--- a/src/plone/restapi/tests/http-examples/navroot_lang_folder_get.resp\n+++ b/src/plone/restapi/tests/http-examples/navroot_lang_folder_get.resp\n@@ -74,13 +74,7 @@ Content-Type: application/json\n "layout": "folder_listing",\n "lock": {},\n "modified": "1995-07-31T17:30:00+00:00",\n- "next_item": {\n- "@id": "http://localhost:55001/plone/de",\n- "@type": "LRF",\n- "description": "",\n- "title": "Deutsch",\n- "type_title": "Language Root Folder"\n- },\n+ "next_item": {},\n "parent": {\n "@id": "http://localhost:55001/plone",\n "@type": "Plone Site",\ndiff --git a/src/plone/restapi/tests/http-examples/newsitem.resp b/src/plone/restapi/tests/http-examples/newsitem.resp\nindex e1dab847d..cab5656ca 100644\n--- a/src/plone/restapi/tests/http-examples/newsitem.resp\n+++ b/src/plone/restapi/tests/http-examples/newsitem.resp\n@@ -125,13 +125,7 @@ Content-Type: application/json\n "title": "Plone site",\n "type_title": "Plone Site"\n },\n- "previous_item": {\n- "@id": "http://localhost:55001/plone/front-page",\n- "@type": "Document",\n- "description": "Congratulations! You have successfully installed Plone.",\n- "title": "Welcome to Plone",\n- "type_title": "Page"\n- },\n+ "previous_item": {},\n "relatedItems": [],\n "review_state": "private",\n "rights": "",\ndiff --git a/src/plone/restapi/tests/http-examples/search_fullobjects.resp b/src/plone/restapi/tests/http-examples/search_fullobjects.resp\nindex 5c9fe7ebf..e74567105 100644\n--- a/src/plone/restapi/tests/http-examples/search_fullobjects.resp\n+++ b/src/plone/restapi/tests/http-examples/search_fullobjects.resp\n@@ -62,13 +62,7 @@ Content-Type: application/json\n "title": "Plone site",\n "type_title": "Plone Site"\n },\n- "previous_item": {\n- "@id": "http://localhost:55001/plone/front-page",\n- "@type": "Document",\n- "description": "Congratulations! You have successfully installed Plone.",\n- "title": "Welcome to Plone",\n- "type_title": "Page"\n- },\n+ "previous_item": {},\n "relatedItems": [],\n "review_state": "private",\n "rights": "",\ndiff --git a/src/plone/restapi/tests/http-examples/site_get_expand_lang_folder.resp b/src/plone/restapi/tests/http-examples/site_get_expand_lang_folder.resp\nindex 4d636bc25..705960dde 100644\n--- a/src/plone/restapi/tests/http-examples/site_get_expand_lang_folder.resp\n+++ b/src/plone/restapi/tests/http-examples/site_get_expand_lang_folder.resp\n@@ -91,13 +91,7 @@ Content-Type: application/json\n "layout": "folder_listing",\n "lock": {},\n "modified": "1995-07-31T17:30:00+00:00",\n- "next_item": {\n- "@id": "http://localhost:55001/plone/de",\n- "@type": "LRF",\n- "description": "",\n- "title": "Deutsch",\n- "type_title": "Language Root Folder"\n- },\n+ "next_item": {},\n "parent": {\n "@id": "http://localhost:55001/plone",\n "@type": "Plone Site",\n@@ -167,13 +161,7 @@ Content-Type: application/json\n "layout": "folder_listing",\n "lock": {},\n "modified": "1995-07-31T17:30:00+00:00",\n- "next_item": {\n- "@id": "http://localhost:55001/plone/de",\n- "@type": "LRF",\n- "description": "",\n- "title": "Deutsch",\n- "type_title": "Language Root Folder"\n- },\n+ "next_item": {},\n "parent": {\n "@id": "http://localhost:55001/plone",\n "@type": "Plone Site",\ndiff --git a/src/plone/restapi/tests/http-examples/site_get_expand_lang_folder_content.resp b/src/plone/restapi/tests/http-examples/site_get_expand_lang_folder_content.resp\nindex 9d4058231..443f2014b 100644\n--- a/src/plone/restapi/tests/http-examples/site_get_expand_lang_folder_content.resp\n+++ b/src/plone/restapi/tests/http-examples/site_get_expand_lang_folder_content.resp\n@@ -91,13 +91,7 @@ Content-Type: application/json\n "layout": "folder_listing",\n "lock": {},\n "modified": "1995-07-31T17:30:00+00:00",\n- "next_item": {\n- "@id": "http://localhost:55001/plone/de",\n- "@type": "LRF",\n- "description": "",\n- "title": "Deutsch",\n- "type_title": "Language Root Folder"\n- },\n+ "next_item": {},\n "parent": {\n "@id": "http://localhost:55001/plone",\n "@type": "Plone Site",\n@@ -161,13 +155,7 @@ Content-Type: application/json\n "title": "English",\n "type_title": "Language Root Folder"\n },\n- "previous_item": {\n- "@id": "http://localhost:55001/plone/en/assets",\n- "@type": "LIF",\n- "description": "",\n- "title": "Assets",\n- "type_title": "Language Independent Folder"\n- },\n+ "previous_item": {},\n "relatedItems": [],\n "review_state": "private",\n "rights": "",\ndiff --git a/src/plone/restapi/tests/http-examples/translated_messages_types_folder.resp b/src/plone/restapi/tests/http-examples/translated_messages_types_folder.resp\nindex 3bdf1d009..ab0702c56 100644\n--- a/src/plone/restapi/tests/http-examples/translated_messages_types_folder.resp\n+++ b/src/plone/restapi/tests/http-examples/translated_messages_types_folder.resp\n@@ -59,11 +59,11 @@ Content-Type: application/json+schema\n ],\n "layouts": [\n "album_view",\n+ "event_listing",\n "full_view",\n "listing_view",\n "summary_view",\n- "tabular_view",\n- "event_listing"\n+ "tabular_view"\n ],\n "properties": {\n "allow_discussion": {\ndiff --git a/src/plone/restapi/tests/http-examples/translations_expand_get.resp b/src/plone/restapi/tests/http-examples/translations_expand_get.resp\nindex 010dce733..fc5e677b1 100644\n--- a/src/plone/restapi/tests/http-examples/translations_expand_get.resp\n+++ b/src/plone/restapi/tests/http-examples/translations_expand_get.resp\n@@ -78,13 +78,7 @@ Content-Type: application/json\n "title": "English",\n "type_title": "Language Root Folder"\n },\n- "previous_item": {\n- "@id": "http://localhost:55001/plone/en/assets",\n- "@type": "LIF",\n- "description": "",\n- "title": "Assets",\n- "type_title": "Language Independent Folder"\n- },\n+ "previous_item": {},\n "relatedItems": [],\n "review_state": "private",\n "rights": "",\ndiff --git a/src/plone/restapi/tests/http-examples/translations_link_on_post.resp b/src/plone/restapi/tests/http-examples/translations_link_on_post.resp\nindex 798d35219..699d57edd 100644\n--- a/src/plone/restapi/tests/http-examples/translations_link_on_post.resp\n+++ b/src/plone/restapi/tests/http-examples/translations_link_on_post.resp\n@@ -67,13 +67,7 @@ Location: http://localhost:55001/plone/de/mydocument\n "title": "Deutsch",\n "type_title": "Basisordner einer Sprache"\n },\n- "previous_item": {\n- "@id": "http://localhost:55001/plone/de/assets",\n- "@type": "LIF",\n- "description": "",\n- "title": "Assets",\n- "type_title": "Sprachunabh\\u00e4ngiger Ordner"\n- },\n+ "previous_item": {},\n "relatedItems": [],\n "review_state": "private",\n "rights": "",\ndiff --git a/src/plone/restapi/tests/http-examples/translations_unexpanded_get.resp b/src/plone/restapi/tests/http-examples/translations_unexpanded_get.resp\nindex 7b0e67424..f266e4dc1 100644\n--- a/src/plone/restapi/tests/http-examples/translations_unexpanded_get.resp\n+++ b/src/plone/restapi/tests/http-examples/translations_unexpanded_get.resp\n@@ -66,13 +66,7 @@ Content-Type: application/json\n "title": "English",\n "type_title": "Language Root Folder"\n },\n- "previous_item": {\n- "@id": "http://localhost:55001/plone/en/assets",\n- "@type": "LIF",\n- "description": "",\n- "title": "Assets",\n- "type_title": "Language Independent Folder"\n- },\n+ "previous_item": {},\n "relatedItems": [],\n "review_state": "private",\n "rights": "",\ndiff --git a/src/plone/restapi/tests/http-examples/workingcopy_baseline_get.resp b/src/plone/restapi/tests/http-examples/workingcopy_baseline_get.resp\nindex 32a97febf..aea5b625d 100644\n--- a/src/plone/restapi/tests/http-examples/workingcopy_baseline_get.resp\n+++ b/src/plone/restapi/tests/http-examples/workingcopy_baseline_get.resp\n@@ -58,13 +58,7 @@ Content-Type: application/json\n "token": "0.12345678901234567-0.98765432109876543-00105A989226:1630609830.249"\n },\n "modified": "1995-07-31T17:30:00+00:00",\n- "next_item": {\n- "@id": "http://localhost:55001/plone/copy_of_document",\n- "@type": "Document",\n- "description": "",\n- "title": "Test document",\n- "type_title": "Page"\n- },\n+ "next_item": {},\n "parent": {\n "@id": "http://localhost:55001/plone",\n "@type": "Plone Site",\ndiff --git a/src/plone/restapi/tests/http-examples/workingcopy_wc_get.resp b/src/plone/restapi/tests/http-examples/workingcopy_wc_get.resp\nindex 08598e4e2..2db877143 100644\n--- a/src/plone/restapi/tests/http-examples/workingcopy_wc_get.resp\n+++ b/src/plone/restapi/tests/http-examples/workingcopy_wc_get.resp\n@@ -58,13 +58,7 @@ Content-Type: application/json\n "title": "Plone site",\n "type_title": "Plone Site"\n },\n- "previous_item": {\n- "@id": "http://localhost:55001/plone/document",\n- "@type": "Document",\n- "description": "",\n- "title": "Test document",\n- "type_title": "Page"\n- },\n+ "previous_item": {},\n "relatedItems": [],\n "review_state": "private",\n "rights": "",\ndiff --git a/src/plone/restapi/tests/test_dxcontent_serializer.py b/src/plone/restapi/tests/test_dxcontent_serializer.py\nindex 18d41cb95..789cf865f 100644\n--- a/src/plone/restapi/tests/test_dxcontent_serializer.py\n+++ b/src/plone/restapi/tests/test_dxcontent_serializer.py\n@@ -11,6 +11,8 @@\n from plone.app.testing import TEST_USER_ID\n from plone.app.textfield.interfaces import ITransformer\n from plone.app.textfield.value import RichTextValue\n+from plone.dexterity.interfaces import IDexterityFTI\n+from plone.dexterity.schema import SCHEMA_CACHE\n from plone.namedfile.file import NamedFile\n from plone.registry.interfaces import IRegistry\n from plone.restapi.interfaces import IExpandableElement\n@@ -31,6 +33,9 @@\n import json\n import unittest\n \n+HAS_PLONE_6 = getattr(\n+ import_module("Products.CMFPlone.factory"), "PLONE60MARKER", False\n+)\n HAS_PLONE_61 = getattr(\n import_module("Products.CMFPlone.factory"), "PLONE61MARKER", False\n )\n@@ -200,12 +205,42 @@ def test_get_is_folderish_in_folder(self):\n self.assertIn("is_folderish", obj)\n self.assertEqual(True, obj["is_folderish"])\n \n+ def test_enable_disable_nextprev(self):\n+ folder = api.content.create(\n+ container=self.portal,\n+ type="Folder",\n+ title="Folder with items",\n+ description="This is a folder with some documents",\n+ nextPreviousEnabled=False,\n+ )\n+ api.content.create(\n+ container=folder,\n+ type="Document",\n+ title="Item 1",\n+ description="Previous item",\n+ )\n+ doc = api.content.create(\n+ container=folder,\n+ type="Document",\n+ title="Item 2",\n+ description="Current item",\n+ )\n+ api.content.create(\n+ container=folder, type="Document", title="Item 2", description="Next item"\n+ )\n+\n+ data = self.serialize(doc)\n+\n+ self.assertEqual({}, data["previous_item"])\n+ self.assertEqual({}, data["next_item"])\n+\n def test_nextprev_no_nextprev(self):\n folder = api.content.create(\n container=self.portal,\n type="Folder",\n title="Folder with items",\n description="This is a folder with some documents",\n+ nextPreviousEnabled=True,\n )\n doc = api.content.create(\n container=folder,\n@@ -223,6 +258,7 @@ def test_nextprev_has_prev(self):\n type="Folder",\n title="Folder with items",\n description="This is a folder with some documents",\n+ nextPreviousEnabled=True,\n )\n api.content.create(\n container=folder,\n@@ -255,6 +291,7 @@ def test_nextprev_has_next(self):\n type="Folder",\n title="Folder with items",\n description="This is a folder with some documents",\n+ nextPreviousEnabled=True,\n )\n doc = api.content.create(\n container=folder,\n@@ -284,6 +321,7 @@ def test_nextprev_has_nextprev(self):\n type="Folder",\n title="Folder with items",\n description="This is a folder with some documents",\n+ nextPreviousEnabled=True,\n )\n api.content.create(\n container=folder,\n@@ -327,7 +365,16 @@ def test_nextprev_root_no_nextprev(self):\n self.assertEqual({}, data["previous_item"])\n self.assertEqual({}, data["next_item"])\n \n+ @unittest.skipUnless(HAS_PLONE_6, "Requires Dexterity-based site root")\n def test_nextprev_root_has_prev(self):\n+ fti = queryUtility(IDexterityFTI, name="Plone Site")\n+ behavior_list = [a for a in fti.behaviors]\n+ behavior_list.append("plone.nextpreviousenabled")\n+ fti.behaviors = tuple(behavior_list)\n+ # Invalidating the cache is required for the FTI to be applied\n+ # on the existing object\n+ SCHEMA_CACHE.invalidate("Plone Site")\n+\n doc = api.content.create(\n container=self.portal,\n type="Document",\n@@ -347,7 +394,16 @@ def test_nextprev_root_has_prev(self):\n )\n self.assertEqual({}, data["next_item"])\n \n+ @unittest.skipUnless(HAS_PLONE_6, "Requires Dexterity-based site root")\n def test_nextprev_root_has_next(self):\n+ fti = queryUtility(IDexterityFTI, name="Plone Site")\n+ behavior_list = [a for a in fti.behaviors]\n+ behavior_list.append("plone.nextpreviousenabled")\n+ fti.behaviors = tuple(behavior_list)\n+ # Invalidating the cache is required for the FTI to be applied\n+ # on the existing object\n+ SCHEMA_CACHE.invalidate("Plone Site")\n+\n api.content.create(\n container=self.portal,\n type="Document",\n@@ -367,7 +423,16 @@ def test_nextprev_root_has_next(self):\n data["next_item"],\n )\n \n+ @unittest.skipUnless(HAS_PLONE_6, "Requires Dexterity-based site root")\n def test_nextprev_root_has_nextprev(self):\n+ fti = queryUtility(IDexterityFTI, name="Plone Site")\n+ behavior_list = [a for a in fti.behaviors]\n+ behavior_list.append("plone.nextpreviousenabled")\n+ fti.behaviors = tuple(behavior_list)\n+ # Invalidating the cache is required for the FTI to be applied\n+ # on the existing object\n+ SCHEMA_CACHE.invalidate("Plone Site")\n+\n api.content.create(\n container=self.portal,\n type="Document",\n@@ -414,6 +479,7 @@ def test_nextprev_unordered_folder(self):\n type="Folder",\n title="Folder with items",\n description="This is a folder with some documents",\n+ nextPreviousEnabled=True,\n )\n folder.setOrdering("unordered")\n doc = api.content.create(\n'