diff --git a/docs/source/usage/content.md b/docs/source/usage/content.md index 82ebb70d90..f9eec9b761 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/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 f3967e145b..7d44e9e56b 100644 --- a/src/plone/restapi/serializer/dxcontent.py +++ b/src/plone/restapi/serializer/dxcontent.py @@ -45,9 +45,23 @@ 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 +69,132 @@ 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 + 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 - obj = self.getVersion(version) + 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, + "version": version, + } + + # Filter basic metadata + for key, value in metadatas.items(): + if not self.can_include_metadata(key): + continue + if callable(value): + value = value(obj=obj) + 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) 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,15 +205,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 + if self.can_include_metadata("targetUrl"): + targetUrl = self.getTargetUrl() + if targetUrl: + result["targetUrl"] = targetUrl - result["allow_discussion"] = getMultiAdapter( - (self.context, self.request), name="conversation_view" - ).enabled() + if self.can_include_metadata("allow_discussion"): + result["allow_discussion"] = self.getAllowDiscussion() return result