From 2492385aa76bdb6f486287df23a28bb39d946c26 Mon Sep 17 00:00:00 2001 From: Luca Date: Fri, 23 Aug 2024 13:40:46 +0200 Subject: [PATCH] Us 57885 fix rss feed for volto (#112) * Fix problem with colletion serialization in case of links between results * fix black/flake8 --- CHANGES.rst | 4 +- .../volto/restapi/serializer/collection.py | 64 +++++++++++++++++++ .../volto/restapi/serializer/configure.zcml | 4 ++ .../volto/tests/test_summary_customization.py | 36 +++++++++++ 4 files changed, 107 insertions(+), 1 deletion(-) create mode 100644 src/redturtle/volto/restapi/serializer/collection.py diff --git a/CHANGES.rst b/CHANGES.rst index 52f4885c..6eadab4d 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -4,7 +4,9 @@ Changelog 5.5.2 (unreleased) ------------------ -- Nothing changed yet. +- Fix SummarySerializer for collection exporting + Links + [lucabel] 5.5.1 (2024-07-22) diff --git a/src/redturtle/volto/restapi/serializer/collection.py b/src/redturtle/volto/restapi/serializer/collection.py new file mode 100644 index 00000000..64fc2b15 --- /dev/null +++ b/src/redturtle/volto/restapi/serializer/collection.py @@ -0,0 +1,64 @@ +""" +Override of the serializer for collections; we only use it to export feeds, so I +consider a complete override of this acceptable. +In redturtle.volto within the summary serializer, the remote URL of links is +requested. The summary serializer it's called on the collection's results items; +If we have a: +plone.app.contentlisting.catalog.CatalogContentListingObject +(the wrapper for collection results on the brain), it's not possible to +calculate the remote URL due to an error when traversing to the +plone_portal_state view. This issue does not occur with the catalog brains. +The CatalogContentListingObject already has the _brain attribute populated. + + +Let's use that (See later the XXX FIX) +""" + +from plone.app.contenttypes.interfaces import ICollection +from plone.restapi.batching import HypermediaBatch +from plone.restapi.deserializer import boolean_value +from plone.restapi.interfaces import ISerializeToJson +from plone.restapi.interfaces import ISerializeToJsonSummary +from plone.restapi.serializer.dxcontent import SerializeToJson +from redturtle.volto.interfaces import IRedturtleVoltoLayer +from zope.component import adapter +from zope.component import getMultiAdapter +from zope.interface import implementer + + +@implementer(ISerializeToJson) +@adapter(ICollection, IRedturtleVoltoLayer) +class SerializeCollectionToJson(SerializeToJson): + def __call__(self, version=None, include_items=True): + result = super().__call__(version=version) + + include_items = self.request.form.get("include_items", include_items) + include_items = boolean_value(include_items) + if include_items: + results = self.context.results(batch=False) + batch = HypermediaBatch(self.request, results) + + if not self.request.form.get("fullobjects"): + result["@id"] = batch.canonical_url + result["items_total"] = batch.items_total + if batch.links: + result["batching"] = batch.links + + if "fullobjects" in list(self.request.form): + result["items"] = [ + getMultiAdapter( + (brain.getObject(), self.request), ISerializeToJson + )() + for brain in batch + ] + else: + # XXX FIX: use brain._brain instead of brain + result["items"] = [ + getMultiAdapter( + (brain._brain, self.request), ISerializeToJsonSummary + )() + for brain in batch + ] + # XXX FIX: end + + return result diff --git a/src/redturtle/volto/restapi/serializer/configure.zcml b/src/redturtle/volto/restapi/serializer/configure.zcml index e963a7d4..f27a79e7 100644 --- a/src/redturtle/volto/restapi/serializer/configure.zcml +++ b/src/redturtle/volto/restapi/serializer/configure.zcml @@ -26,4 +26,8 @@ + + + + diff --git a/src/redturtle/volto/tests/test_summary_customization.py b/src/redturtle/volto/tests/test_summary_customization.py index 55e0ec1a..c5f6c85e 100644 --- a/src/redturtle/volto/tests/test_summary_customization.py +++ b/src/redturtle/volto/tests/test_summary_customization.py @@ -1,5 +1,8 @@ # -*- coding: utf-8 -*- from plone import api +from plone.app.contenttypes.behaviors.collection import ( + ICollection as ICollection_behavior, +) from plone.app.testing import setRoles from plone.app.testing import SITE_OWNER_NAME from plone.app.testing import SITE_OWNER_PASSWORD @@ -104,3 +107,36 @@ def test_summary_return_empty_effective_date_if_not_set(self): self.assertEqual(len(res["items"]), 1) self.assertEqual(res["items"][0]["effective"], page.effective_date) + + def test_summary_serializer_with_links_in_collection_results(self): + link = api.content.create( + container=self.portal, + type="Link", + title="Funny link", + ) + link.remoteUrl = "/events" + # allow to add collection + portal = api.portal.get() + pt = portal.portal_types + pt["Collection"].global_allow = True + # add a collection to find links + collection = api.content.create( + container=self.portal, + type="Collection", + title="A collection", + ) + wrapped = ICollection_behavior(collection) + wrapped.query = [ + { + "i": "portal_type", + "o": "plone.app.querystring.operation.string.is", + "v": "Link", + }, + ] + transaction.commit() + # Check if only the item inside folder1 is returned, since it's a + # navigation root. + response = self.api_session.get(collection.getId()) + items = response.json().get("items", []) + self.assertEqual(len(items), 1) + self.assertEqual(items[0]["title"], "Funny link")