From 1d8514bdb61bf7bb03c53cc55479fc82adb9e3fa Mon Sep 17 00:00:00 2001 From: Miu Razvan Date: Mon, 19 Jun 2023 14:38:27 +0300 Subject: [PATCH 01/11] Add param option to exclude basic metadata from GET content --- docs/source/usage/content.md | 2 + src/plone/restapi/serializer/dxcontent.py | 180 ++++++++++++++++------ 2 files changed, 135 insertions(+), 47 deletions(-) diff --git a/docs/source/usage/content.md b/docs/source/usage/content.md index 4c62288902..f83694795e 100644 --- a/docs/source/usage/content.md +++ b/docs/source/usage/content.md @@ -142,6 +142,8 @@ For folderish types, their children are automatically included in the response a To disable the inclusion, add the `GET` parameter `include_items=false` to the URL. By default, only basic metadata is included. +To exclude basic metadata add the `GET` parameter `include_basic_metadata=false` to the URL. +To exclude expandable elements add the `GET` parameter `include_expandable_elements=false` to the URL To include additional metadata, you can specify the names of the properties with the `metadata_fields` parameter. See also {ref}`retrieving-additional-metadata`. diff --git a/src/plone/restapi/serializer/dxcontent.py b/src/plone/restapi/serializer/dxcontent.py index f3967e145b..46702d2803 100644 --- a/src/plone/restapi/serializer/dxcontent.py +++ b/src/plone/restapi/serializer/dxcontent.py @@ -45,9 +45,19 @@ class SerializeToJson: def __init__(self, context, request): self.context = context self.request = request + self.metadata_fields = self.getParam("metadata_fields", list) + self.include_basic_metadata = self.getParam("include_basic_metadata", bool, True) + self.include_expandable_elements = self.getParam("include_expandable_elements", bool, True) self.permission_cache = {} + def can_include_metadata(self, metadata): + if self.include_basic_metadata: + return True + if metadata in self.metadata_fields: + return True + return False + def getVersion(self, version): if version == "current": return self.context @@ -55,60 +65,138 @@ def getVersion(self, version): repo_tool = getToolByName(self.context, "portal_repository") return repo_tool.retrieve(self.context, int(version)).object - def __call__(self, version=None, include_items=True): - version = "current" if version is None else version - - obj = self.getVersion(version) + def getParam(self, param, value_type, default_value=None): + value = self.request.form.get(param, default_value) + if value_type == list and not value: + return [] + if value_type == list and type(value) != list: + return [value] + if value_type == bool: + return boolean_value(value) + return value + + def getId(self, obj): + return obj.id + + def getTypeTitle(self, obj): + return get_portal_type_title(obj.portal_type) + + def getUID(self, obj): + return obj.UID() + + def getParent(self, obj): parent = aq_parent(aq_inner(obj)) parent_summary = getMultiAdapter( (parent, self.request), ISerializeToJsonSummary )() + return parent_summary + + def getCreated(self, obj): + return json_compatible(obj.created()) + + def getModified(self, obj): + return json_compatible(obj.modified()) + + def getLayout(self, **kwargs): + return self.context.getLayout() + + def getLock(self, obj): + return lock_info(obj) + + def getReviewState(self, obj): + return self._get_workflow_state(obj) + + def getAllowDiscussion(self, **kwargs): + return getMultiAdapter( + (self.context, self.request), name="conversation_view" + ).enabled() + + def getTargetUrl(self, **kwargs): + target_url = getMultiAdapter( + (self.context, self.request), IObjectPrimaryFieldTarget + )() + return target_url + + def __call__(self, version=None, include_items=True): + version = "current" if version is None else version + + obj = self.getVersion(version) result = { - # '@context': 'http://www.w3.org/ns/hydra/context.jsonld', "@id": obj.absolute_url(), - "id": obj.id, "@type": obj.portal_type, - "type_title": get_portal_type_title(obj.portal_type), - "parent": parent_summary, - "created": json_compatible(obj.created()), - "modified": json_compatible(obj.modified()), - "review_state": self._get_workflow_state(obj), - "UID": obj.UID(), - "version": version, - "layout": self.context.getLayout(), "is_folderish": False, } + metadatas = { + # '@context': 'http://www.w3.org/ns/hydra/context.jsonld', + "id": self.getId, + "type_title": self.getTypeTitle, + "UID": self.getUID, + "parent": self.getParent, + "created": self.getCreated, + "modified": self.getModified, + "layout": self.getLayout, + # Insert locking information + "lock": self.getLock, + "review_state": self.getReviewState, + "allow_discussion": self.getAllowDiscussion, + "targetUrl": self.getTargetUrl, + "version": version, + } + + # Filter basic metadata + for key, value in metadatas.items(): + if not self.can_include_metadata(key): + continue + if callable(value): + result[key] = value(obj=obj) + continue + if not value is None: + result[key] = value # Insert next/prev information - try: - nextprevious = NextPrevious(obj) - result.update( - {"previous_item": nextprevious.previous, "next_item": nextprevious.next} - ) - except ValueError: - # If we're serializing an old version that was renamed or moved, - # then its id might not be found inside the current object's container. - result.update({"previous_item": {}, "next_item": {}}) + if self.can_include_metadata( + "previous_item" + ) or self.can_include_metadata("next_item"): + try: + nextprevious = NextPrevious(obj) + result.update( + { + "previous_item": nextprevious.previous, + "next_item": nextprevious.next, + } + ) + except ValueError: + # If we're serializing an old version that was renamed or moved, + # then its id might not be found inside the current object's container. + result.update({"previous_item": {}, "next_item": {}}) # Insert working copy information - if WorkingCopyInfo is not None: - baseline, working_copy = WorkingCopyInfo( - self.context - ).get_working_copy_info() - result.update({"working_copy": working_copy, "working_copy_of": baseline}) - - # Insert locking information - result.update({"lock": lock_info(obj)}) + if self.can_include_metadata("working_copy"): + if WorkingCopyInfo is not None: + baseline, working_copy = WorkingCopyInfo( + self.context + ).get_working_copy_info() + result.update( + { + "working_copy": working_copy, + "working_copy_of": baseline, + } + ) # Insert expandable elements - result.update(expandable_elements(self.context, self.request)) + if self.include_expandable_elements: + result.update(expandable_elements(self.context, self.request)) # Insert field values for schema in iterSchemata(self.context): - read_permissions = mergedTaggedValueDict(schema, READ_PERMISSIONS_KEY) + read_permissions = mergedTaggedValueDict( + schema, READ_PERMISSIONS_KEY + ) for name, field in getFields(schema).items(): + if not self.can_include_metadata(name): + continue if not self.check_permission(read_permissions.get(name), obj): continue @@ -119,21 +207,13 @@ def __call__(self, version=None, include_items=True): value = serializer() result[json_compatible(name)] = value - target_url = getMultiAdapter( - (self.context, self.request), IObjectPrimaryFieldTarget - )() - if target_url: - result["targetUrl"] = target_url - - result["allow_discussion"] = getMultiAdapter( - (self.context, self.request), name="conversation_view" - ).enabled() - return result def _get_workflow_state(self, obj): wftool = getToolByName(self.context, "portal_workflow") - review_state = wftool.getInfoFor(ob=obj, name="review_state", default=None) + review_state = wftool.getInfoFor( + ob=obj, name="review_state", default=None + ) return review_state def check_permission(self, permission_name, obj): @@ -189,7 +269,9 @@ def __call__(self, version=None, include_items=True): )(fullobjects=True)["items"] else: result["items"] = [ - getMultiAdapter((brain, self.request), ISerializeToJsonSummary)() + getMultiAdapter( + (brain, self.request), ISerializeToJsonSummary + )() for brain in batch ] return result @@ -207,10 +289,14 @@ def __init__(self, context, request): def __call__(self): primary_field_name = self.get_primary_field_name() for schema in iterSchemata(self.context): - read_permissions = mergedTaggedValueDict(schema, READ_PERMISSIONS_KEY) + read_permissions = mergedTaggedValueDict( + schema, READ_PERMISSIONS_KEY + ) for name, field in getFields(schema).items(): - if not self.check_permission(read_permissions.get(name), self.context): + if not self.check_permission( + read_permissions.get(name), self.context + ): continue if name != primary_field_name: From f7432bd06ae8dd4aac3d75292ee23bb248fec22b Mon Sep 17 00:00:00 2001 From: Miu Razvan Date: Mon, 19 Jun 2023 14:47:38 +0300 Subject: [PATCH 02/11] prettier --- src/plone/restapi/serializer/dxcontent.py | 60 +++++++---------------- 1 file changed, 19 insertions(+), 41 deletions(-) diff --git a/src/plone/restapi/serializer/dxcontent.py b/src/plone/restapi/serializer/dxcontent.py index 46702d2803..72aecb2434 100644 --- a/src/plone/restapi/serializer/dxcontent.py +++ b/src/plone/restapi/serializer/dxcontent.py @@ -83,23 +83,21 @@ def getTypeTitle(self, obj): def getUID(self, obj): return obj.UID() - + def getParent(self, obj): parent = aq_parent(aq_inner(obj)) - parent_summary = getMultiAdapter( - (parent, self.request), ISerializeToJsonSummary - )() + parent_summary = getMultiAdapter((parent, self.request), ISerializeToJsonSummary)() return parent_summary - + def getCreated(self, obj): return json_compatible(obj.created()) def getModified(self, obj): return json_compatible(obj.modified()) - + def getLayout(self, **kwargs): return self.context.getLayout() - + def getLock(self, obj): return lock_info(obj) @@ -107,14 +105,10 @@ def getReviewState(self, obj): return self._get_workflow_state(obj) def getAllowDiscussion(self, **kwargs): - return getMultiAdapter( - (self.context, self.request), name="conversation_view" - ).enabled() + return getMultiAdapter((self.context, self.request), name="conversation_view").enabled() def getTargetUrl(self, **kwargs): - target_url = getMultiAdapter( - (self.context, self.request), IObjectPrimaryFieldTarget - )() + target_url = getMultiAdapter((self.context, self.request), IObjectPrimaryFieldTarget)() return target_url def __call__(self, version=None, include_items=True): @@ -151,13 +145,11 @@ def __call__(self, version=None, include_items=True): if callable(value): result[key] = value(obj=obj) continue - if not value is None: + if value is not None: result[key] = value # Insert next/prev information - if self.can_include_metadata( - "previous_item" - ) or self.can_include_metadata("next_item"): + if self.can_include_metadata("previous_item") or self.can_include_metadata("next_item"): try: nextprevious = NextPrevious(obj) result.update( @@ -174,9 +166,7 @@ def __call__(self, version=None, include_items=True): # Insert working copy information if self.can_include_metadata("working_copy"): if WorkingCopyInfo is not None: - baseline, working_copy = WorkingCopyInfo( - self.context - ).get_working_copy_info() + baseline, working_copy = WorkingCopyInfo(self.context).get_working_copy_info() result.update( { "working_copy": working_copy, @@ -190,9 +180,7 @@ def __call__(self, version=None, include_items=True): # Insert field values for schema in iterSchemata(self.context): - read_permissions = mergedTaggedValueDict( - schema, READ_PERMISSIONS_KEY - ) + read_permissions = mergedTaggedValueDict(schema, READ_PERMISSIONS_KEY) for name, field in getFields(schema).items(): if not self.can_include_metadata(name): @@ -201,9 +189,7 @@ def __call__(self, version=None, include_items=True): continue # serialize the field - serializer = queryMultiAdapter( - (field, obj, self.request), IFieldSerializer - ) + serializer = queryMultiAdapter((field, obj, self.request), IFieldSerializer) value = serializer() result[json_compatible(name)] = value @@ -211,9 +197,7 @@ def __call__(self, version=None, include_items=True): def _get_workflow_state(self, obj): wftool = getToolByName(self.context, "portal_workflow") - review_state = wftool.getInfoFor( - ob=obj, name="review_state", default=None - ) + review_state = wftool.getInfoFor(ob=obj, name="review_state", default=None) return review_state def check_permission(self, permission_name, obj): @@ -264,14 +248,12 @@ def __call__(self, version=None, include_items=True): result["batching"] = batch.links if "fullobjects" in list(self.request.form): - result["items"] = getMultiAdapter( - (brains, self.request), ISerializeToJson - )(fullobjects=True)["items"] + result["items"] = getMultiAdapter((brains, self.request), ISerializeToJson)( + fullobjects=True + )["items"] else: result["items"] = [ - getMultiAdapter( - (brain, self.request), ISerializeToJsonSummary - )() + getMultiAdapter((brain, self.request), ISerializeToJsonSummary)() for brain in batch ] return result @@ -289,14 +271,10 @@ def __init__(self, context, request): def __call__(self): primary_field_name = self.get_primary_field_name() for schema in iterSchemata(self.context): - read_permissions = mergedTaggedValueDict( - schema, READ_PERMISSIONS_KEY - ) + read_permissions = mergedTaggedValueDict(schema, READ_PERMISSIONS_KEY) for name, field in getFields(schema).items(): - if not self.check_permission( - read_permissions.get(name), self.context - ): + if not self.check_permission(read_permissions.get(name), self.context): continue if name != primary_field_name: From 02a3ae0d63cf6e1e40f21a797aa9c0761e0187ae Mon Sep 17 00:00:00 2001 From: Miu Razvan Date: Mon, 19 Jun 2023 14:58:46 +0300 Subject: [PATCH 03/11] add changelog --- news/1661.feature | 1 + src/plone/restapi/serializer/dxcontent.py | 3 +-- 2 files changed, 2 insertions(+), 2 deletions(-) create mode 100644 news/1661.feature diff --git a/news/1661.feature b/news/1661.feature new file mode 100644 index 0000000000..772a8fa5a7 --- /dev/null +++ b/news/1661.feature @@ -0,0 +1 @@ +- Add param option to exclude basic metadata from GET content - @razvanMiu \ No newline at end of file diff --git a/src/plone/restapi/serializer/dxcontent.py b/src/plone/restapi/serializer/dxcontent.py index 72aecb2434..39513e2fb1 100644 --- a/src/plone/restapi/serializer/dxcontent.py +++ b/src/plone/restapi/serializer/dxcontent.py @@ -143,8 +143,7 @@ def __call__(self, version=None, include_items=True): if not self.can_include_metadata(key): continue if callable(value): - result[key] = value(obj=obj) - continue + value = value(obj=obj) if value is not None: result[key] = value From dd50e96f292d654be44a87033f42b655188723d2 Mon Sep 17 00:00:00 2001 From: Miu Razvan Date: Mon, 19 Jun 2023 15:00:53 +0300 Subject: [PATCH 04/11] prettier --- src/plone/restapi/serializer/dxcontent.py | 34 ++++++++++++++++------- 1 file changed, 24 insertions(+), 10 deletions(-) diff --git a/src/plone/restapi/serializer/dxcontent.py b/src/plone/restapi/serializer/dxcontent.py index 39513e2fb1..ead4aaf805 100644 --- a/src/plone/restapi/serializer/dxcontent.py +++ b/src/plone/restapi/serializer/dxcontent.py @@ -47,7 +47,9 @@ def __init__(self, context, request): self.request = request self.metadata_fields = self.getParam("metadata_fields", list) self.include_basic_metadata = self.getParam("include_basic_metadata", bool, True) - self.include_expandable_elements = self.getParam("include_expandable_elements", bool, True) + self.include_expandable_elements = self.getParam( + "include_expandable_elements", bool, True + ) self.permission_cache = {} @@ -86,7 +88,9 @@ def getUID(self, obj): def getParent(self, obj): parent = aq_parent(aq_inner(obj)) - parent_summary = getMultiAdapter((parent, self.request), ISerializeToJsonSummary)() + parent_summary = getMultiAdapter( + (parent, self.request), ISerializeToJsonSummary + )() return parent_summary def getCreated(self, obj): @@ -105,10 +109,14 @@ def getReviewState(self, obj): return self._get_workflow_state(obj) def getAllowDiscussion(self, **kwargs): - return getMultiAdapter((self.context, self.request), name="conversation_view").enabled() + return getMultiAdapter( + (self.context, self.request), name="conversation_view" + ).enabled() def getTargetUrl(self, **kwargs): - target_url = getMultiAdapter((self.context, self.request), IObjectPrimaryFieldTarget)() + target_url = getMultiAdapter( + (self.context, self.request), IObjectPrimaryFieldTarget + )() return target_url def __call__(self, version=None, include_items=True): @@ -148,7 +156,9 @@ def __call__(self, version=None, include_items=True): result[key] = value # Insert next/prev information - if self.can_include_metadata("previous_item") or self.can_include_metadata("next_item"): + if self.can_include_metadata("previous_item") or self.can_include_metadata( + "next_item" + ): try: nextprevious = NextPrevious(obj) result.update( @@ -165,7 +175,9 @@ def __call__(self, version=None, include_items=True): # Insert working copy information if self.can_include_metadata("working_copy"): if WorkingCopyInfo is not None: - baseline, working_copy = WorkingCopyInfo(self.context).get_working_copy_info() + baseline, working_copy = WorkingCopyInfo( + self.context + ).get_working_copy_info() result.update( { "working_copy": working_copy, @@ -188,7 +200,9 @@ def __call__(self, version=None, include_items=True): continue # serialize the field - serializer = queryMultiAdapter((field, obj, self.request), IFieldSerializer) + serializer = queryMultiAdapter( + (field, obj, self.request), IFieldSerializer + ) value = serializer() result[json_compatible(name)] = value @@ -247,9 +261,9 @@ def __call__(self, version=None, include_items=True): result["batching"] = batch.links if "fullobjects" in list(self.request.form): - result["items"] = getMultiAdapter((brains, self.request), ISerializeToJson)( - fullobjects=True - )["items"] + result["items"] = getMultiAdapter( + (brains, self.request), ISerializeToJson + )(fullobjects=True)["items"] else: result["items"] = [ getMultiAdapter((brain, self.request), ISerializeToJsonSummary)() From ecd25a05e62a8ab7ff6adc2043392d6be6262123 Mon Sep 17 00:00:00 2001 From: Miu Razvan Date: Mon, 19 Jun 2023 15:02:01 +0300 Subject: [PATCH 05/11] prettier --- src/plone/restapi/serializer/dxcontent.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/plone/restapi/serializer/dxcontent.py b/src/plone/restapi/serializer/dxcontent.py index ead4aaf805..6c5bfef9bd 100644 --- a/src/plone/restapi/serializer/dxcontent.py +++ b/src/plone/restapi/serializer/dxcontent.py @@ -46,7 +46,9 @@ def __init__(self, context, request): self.context = context self.request = request self.metadata_fields = self.getParam("metadata_fields", list) - self.include_basic_metadata = self.getParam("include_basic_metadata", bool, True) + self.include_basic_metadata = self.getParam( + "include_basic_metadata", bool, True + ) self.include_expandable_elements = self.getParam( "include_expandable_elements", bool, True ) From 7d87044c36232a58850759f62261a9343814c4de Mon Sep 17 00:00:00 2001 From: Miu Razvan Date: Mon, 19 Jun 2023 15:44:20 +0300 Subject: [PATCH 06/11] fix tests --- src/plone/restapi/serializer/dxcontent.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/plone/restapi/serializer/dxcontent.py b/src/plone/restapi/serializer/dxcontent.py index 6c5bfef9bd..53abc42367 100644 --- a/src/plone/restapi/serializer/dxcontent.py +++ b/src/plone/restapi/serializer/dxcontent.py @@ -143,11 +143,11 @@ def __call__(self, version=None, include_items=True): # Insert locking information "lock": self.getLock, "review_state": self.getReviewState, - "allow_discussion": self.getAllowDiscussion, "targetUrl": self.getTargetUrl, "version": version, } + # Filter basic metadata for key, value in metadatas.items(): if not self.can_include_metadata(key): @@ -208,6 +208,9 @@ def __call__(self, version=None, include_items=True): value = serializer() result[json_compatible(name)] = value + if self.can_include_metadata('allow_discussion'): + result["allow_discussion"] = self.getAllowDiscussion() + return result def _get_workflow_state(self, obj): From 84b1fe218b37923f8cf3d23a399efb7692bf3773 Mon Sep 17 00:00:00 2001 From: Miu Razvan Date: Mon, 19 Jun 2023 15:46:15 +0300 Subject: [PATCH 07/11] update content.md --- docs/source/usage/content.md | 4 ++-- src/plone/restapi/serializer/dxcontent.py | 3 +-- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/docs/source/usage/content.md b/docs/source/usage/content.md index f83694795e..2d03d3195d 100644 --- a/docs/source/usage/content.md +++ b/docs/source/usage/content.md @@ -142,8 +142,8 @@ For folderish types, their children are automatically included in the response a To disable the inclusion, add the `GET` parameter `include_items=false` to the URL. By default, only basic metadata is included. -To exclude basic metadata add the `GET` parameter `include_basic_metadata=false` to the URL. -To exclude expandable elements add the `GET` parameter `include_expandable_elements=false` to the URL +To exclude basic metadata, add the `GET` parameter `include_basic_metadata=false` to the URL. +To exclude expandable elements, add the `GET` parameter `include_expandable_elements=false` to the URL. To include additional metadata, you can specify the names of the properties with the `metadata_fields` parameter. See also {ref}`retrieving-additional-metadata`. diff --git a/src/plone/restapi/serializer/dxcontent.py b/src/plone/restapi/serializer/dxcontent.py index 53abc42367..04205ace98 100644 --- a/src/plone/restapi/serializer/dxcontent.py +++ b/src/plone/restapi/serializer/dxcontent.py @@ -147,7 +147,6 @@ def __call__(self, version=None, include_items=True): "version": version, } - # Filter basic metadata for key, value in metadatas.items(): if not self.can_include_metadata(key): @@ -208,7 +207,7 @@ def __call__(self, version=None, include_items=True): value = serializer() result[json_compatible(name)] = value - if self.can_include_metadata('allow_discussion'): + if self.can_include_metadata("allow_discussion"): result["allow_discussion"] = self.getAllowDiscussion() return result From 4b9094e9ca5d15a889284b74dcf3187f94d02c33 Mon Sep 17 00:00:00 2001 From: Miu Razvan Date: Mon, 19 Jun 2023 16:27:03 +0300 Subject: [PATCH 08/11] show review_state if null --- src/plone/restapi/serializer/dxcontent.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plone/restapi/serializer/dxcontent.py b/src/plone/restapi/serializer/dxcontent.py index 04205ace98..f2ed522634 100644 --- a/src/plone/restapi/serializer/dxcontent.py +++ b/src/plone/restapi/serializer/dxcontent.py @@ -153,7 +153,7 @@ def __call__(self, version=None, include_items=True): continue if callable(value): value = value(obj=obj) - if value is not None: + if value is not None or key in ['review_state']: result[key] = value # Insert next/prev information From 062298e2345d55575f32ac1a6407c0595a0ca300 Mon Sep 17 00:00:00 2001 From: Miu Razvan Date: Mon, 19 Jun 2023 16:28:46 +0300 Subject: [PATCH 09/11] update --- src/plone/restapi/serializer/dxcontent.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/plone/restapi/serializer/dxcontent.py b/src/plone/restapi/serializer/dxcontent.py index f2ed522634..af8b07b1fe 100644 --- a/src/plone/restapi/serializer/dxcontent.py +++ b/src/plone/restapi/serializer/dxcontent.py @@ -143,7 +143,6 @@ def __call__(self, version=None, include_items=True): # Insert locking information "lock": self.getLock, "review_state": self.getReviewState, - "targetUrl": self.getTargetUrl, "version": version, } @@ -153,8 +152,7 @@ def __call__(self, version=None, include_items=True): continue if callable(value): value = value(obj=obj) - if value is not None or key in ['review_state']: - result[key] = value + result[key] = value # Insert next/prev information if self.can_include_metadata("previous_item") or self.can_include_metadata( @@ -207,6 +205,9 @@ def __call__(self, version=None, include_items=True): value = serializer() result[json_compatible(name)] = value + if self.can_include_metadata("targetUrl"): + result["targetUrl"] = self.getTargetUrl() + if self.can_include_metadata("allow_discussion"): result["allow_discussion"] = self.getAllowDiscussion() From 4d71069dac392fea995d86b54a50d822812f9745 Mon Sep 17 00:00:00 2001 From: Miu Razvan Date: Tue, 20 Jun 2023 11:14:54 +0300 Subject: [PATCH 10/11] update --- src/plone/restapi/serializer/dxcontent.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/plone/restapi/serializer/dxcontent.py b/src/plone/restapi/serializer/dxcontent.py index af8b07b1fe..a77b7c5254 100644 --- a/src/plone/restapi/serializer/dxcontent.py +++ b/src/plone/restapi/serializer/dxcontent.py @@ -206,7 +206,9 @@ def __call__(self, version=None, include_items=True): result[json_compatible(name)] = value if self.can_include_metadata("targetUrl"): - result["targetUrl"] = self.getTargetUrl() + targetUrl = self.getTargetUrl() + if targetUrl: + result["targetUrl"] if self.can_include_metadata("allow_discussion"): result["allow_discussion"] = self.getAllowDiscussion() From 210bb7003cb9f3a9ac7dcf83c33099db92e8dac3 Mon Sep 17 00:00:00 2001 From: Miu Razvan Date: Tue, 20 Jun 2023 11:42:05 +0300 Subject: [PATCH 11/11] update --- src/plone/restapi/serializer/dxcontent.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plone/restapi/serializer/dxcontent.py b/src/plone/restapi/serializer/dxcontent.py index a77b7c5254..7d44e9e56b 100644 --- a/src/plone/restapi/serializer/dxcontent.py +++ b/src/plone/restapi/serializer/dxcontent.py @@ -208,7 +208,7 @@ def __call__(self, version=None, include_items=True): if self.can_include_metadata("targetUrl"): targetUrl = self.getTargetUrl() if targetUrl: - result["targetUrl"] + result["targetUrl"] = targetUrl if self.can_include_metadata("allow_discussion"): result["allow_discussion"] = self.getAllowDiscussion()