diff --git a/base.cfg b/base.cfg
index 33f786f8ec..27278b4bc8 100644
--- a/base.cfg
+++ b/base.cfg
@@ -18,6 +18,7 @@ parts =
develop = .
sources-dir = extras
auto-checkout =
+ plone.volto
# plone.rest
allow-hosts =
@@ -45,6 +46,7 @@ eggs =
Pillow
plone.app.debugtoolbar
plone.restapi [test]
+ plone.volto
environment-vars =
zope_i18n_compile_mo_files true
@@ -190,6 +192,7 @@ output = ${buildout:directory}/bin/zpretty-run
mode = 755
[sources]
+plone.volto = git git@github.com:plone/plone.volto.git pushurl=git@github.com:plone/plone.volto.git branch=preview-image-link
plone.rest = git git://github.com/plone/plone.rest.git pushurl=git@github.com:plone/plone.rest.git branch=master
plone.schema = git git://github.com/plone/plone.schema.git pushurl=git@github.com:plone/plone.schema.git branch=master
Products.ZCatalog = git git://github.com/zopefoundation/Products.ZCatalog.git pushurl=git@github.com:zopefoundation/Products.ZCatalog.git
\ No newline at end of file
diff --git a/news/xxxx.feature b/news/xxxx.feature
new file mode 100644
index 0000000000..637f61bba1
--- /dev/null
+++ b/news/xxxx.feature
@@ -0,0 +1 @@
+- Enhance relation field serialization for image content types [@reebalazs]
\ No newline at end of file
diff --git a/src/plone/restapi/interfaces.py b/src/plone/restapi/interfaces.py
index 9137914b80..e20c2d9cbd 100644
--- a/src/plone/restapi/interfaces.py
+++ b/src/plone/restapi/interfaces.py
@@ -224,3 +224,15 @@ def non_metadata_attributes():
def blocklisted_attributes():
"""Returns a set with attributes blocked during serialization."""
+
+
+class IRelationObjectSerializer(Interface):
+ """The relation object serializer multi adapter serializes the relation object into
+ JSON compatible python data.
+ """
+
+ def __init__(rel_obj, field, context, request):
+ """Adapts relation object, field, context and request."""
+
+ def __call__():
+ """Returns JSON compatible python data."""
diff --git a/src/plone/restapi/serializer/configure.zcml b/src/plone/restapi/serializer/configure.zcml
index f827730c19..ceb1915e91 100644
--- a/src/plone/restapi/serializer/configure.zcml
+++ b/src/plone/restapi/serializer/configure.zcml
@@ -113,4 +113,9 @@
name="plone.restapi.summary_serializer_metadata"
/>
+
+
+
+
+
diff --git a/src/plone/restapi/serializer/relationfield.py b/src/plone/restapi/serializer/relationfield.py
index 931453fbea..12e253a5fd 100644
--- a/src/plone/restapi/serializer/relationfield.py
+++ b/src/plone/restapi/serializer/relationfield.py
@@ -27,7 +27,25 @@ def relationvalue_converter(value):
@adapter(IRelationChoice, IDexterityContent, Interface)
@implementer(IFieldSerializer)
class RelationChoiceFieldSerializer(DefaultFieldSerializer):
- pass
+ def __call__(self):
+ result = json_compatible(self.get_value())
+ # Enhance information based on the content type in relation
+ if result is None:
+ return None
+ portal = getMultiAdapter(
+ (self.context, self.request), name="plone_portal_state"
+ ).portal()
+ portal_url = portal.absolute_url()
+ rel_url = result["@id"]
+ if not rel_url.startswith(portal_url):
+ raise RuntimeError(
+ f"Url must start with portal url. [{portal_url} <> {rel_url}]"
+ )
+ rel_path = rel_url[len(portal_url) + 1 :]
+ rel_obj = portal.unrestrictedTraverse(rel_path, None)
+ serializer = getMultiAdapter((rel_obj, self.field, self.context, self.request))
+ result = serializer()
+ return result
@adapter(IRelationList, IDexterityContent, Interface)
diff --git a/src/plone/restapi/serializer/relationobject.py b/src/plone/restapi/serializer/relationobject.py
new file mode 100644
index 0000000000..a7d22d5f1a
--- /dev/null
+++ b/src/plone/restapi/serializer/relationobject.py
@@ -0,0 +1,81 @@
+from plone.dexterity.interfaces import IDexterityContent
+from plone.restapi.interfaces import IRelationObjectSerializer
+from plone.restapi.serializer.converters import json_compatible
+from zope.component import adapter
+from zope.component import getMultiAdapter
+from zope.interface import implementer
+from zope.interface import Interface
+from zope.interface import alsoProvides
+from plone.app.contenttypes.interfaces import IImage
+from plone.namedfile.interfaces import INamedImageField
+from z3c.relationfield.interfaces import IRelationChoice
+
+import logging
+
+
+log = logging.getLogger(__name__)
+
+
+@adapter(IDexterityContent, IRelationChoice, IDexterityContent, Interface)
+@implementer(IRelationObjectSerializer)
+class DefaultRelationObjectSerializer:
+ def __init__(self, rel_obj, field, context, request):
+ self.context = context
+ self.request = request
+ self.field = field
+ self.rel_obj = rel_obj
+
+ def __call__(self):
+ obj = self.rel_obj
+ # Start from the values of the default field serializer
+ result = json_compatible(self.get_value())
+ # Add some more values from the object in relation
+ additional = {
+ "id": obj.id,
+ "created": obj.created(),
+ "modified": obj.modified(),
+ "UID": obj.UID(),
+ }
+ result.update(additional)
+ return json_compatible(result)
+
+ def get_value(self, default=None):
+ return getattr(self.field.interface(self.context), self.field.__name__, default)
+
+
+class FieldSim:
+ def __init__(self, name, provides):
+ self.__name__ = name
+ alsoProvides(self, provides)
+
+ def get(self, context):
+ return getattr(context, self.__name__)
+
+
+class FieldRelationObjectSerializer(DefaultRelationObjectSerializer):
+ """The relationship object is treatable like a field
+
+ So we can reuse the serialization for that specific field.
+ """
+
+ field_name = None
+ field_interface = None
+
+ def __call__(self):
+ field = FieldSim(self.field_name, self.field_interface)
+ result = super().__call__()
+ # Reuse a serializer from dxfields
+ serializer = getMultiAdapter((field, self.rel_obj, self.request))
+ # Extend the default values with the content specific ones
+ additional = serializer()
+ if additional is not None:
+ result.update(additional)
+ return result
+
+
+@adapter(IImage, IRelationChoice, IDexterityContent, Interface)
+class ImageRelationObjectSerializer(FieldRelationObjectSerializer):
+ # The name of the attribute that contains the data object within the relation object
+ field_name = "image"
+ # The field adapter that we will emulate to get the serialized data for this content type
+ field_interface = INamedImageField