diff --git a/news/1847.feature b/news/1847.feature new file mode 100644 index 0000000000..c2f91e123e --- /dev/null +++ b/news/1847.feature @@ -0,0 +1 @@ +When a Link content item is linked by UID, resolve its URL as the linked target URL for anonymous users. @cekk diff --git a/src/plone/restapi/serializer/configure.zcml b/src/plone/restapi/serializer/configure.zcml index 0e84f64f42..32c63b2d78 100644 --- a/src/plone/restapi/serializer/configure.zcml +++ b/src/plone/restapi/serializer/configure.zcml @@ -8,6 +8,8 @@ + + diff --git a/src/plone/restapi/serializer/dxcontent.py b/src/plone/restapi/serializer/dxcontent.py index 1c546d091d..e497bb6b68 100644 --- a/src/plone/restapi/serializer/dxcontent.py +++ b/src/plone/restapi/serializer/dxcontent.py @@ -1,6 +1,7 @@ from AccessControl import getSecurityManager from Acquisition import aq_inner from Acquisition import aq_parent +from plone.app.contenttypes.interfaces import ILink from plone.autoform.interfaces import READ_PERMISSIONS_KEY from plone.dexterity.interfaces import IDexterityContainer from plone.dexterity.interfaces import IDexterityContent @@ -266,3 +267,27 @@ def check_permission(self, permission_name, obj): sm.checkPermission(permission.title, obj) ) return self.permission_cache[permission_name] + + +@adapter(ILink, Interface) +@implementer(IObjectPrimaryFieldTarget) +class LinkObjectPrimaryFieldTarget: + def __init__(self, context, request): + self.context = context + self.request = request + + self.permission_cache = {} + + def __call__(self): + """ + If user can edit Link object, do not return remoteUrl + """ + pm = getToolByName(self.context, "portal_membership") + if bool(pm.isAnonymousUser()): + for schema in iterSchemata(self.context): + for name, field in getFields(schema).items(): + if name == "remoteUrl": + serializer = queryMultiAdapter( + (field, self.context, self.request), IFieldSerializer + ) + return serializer() diff --git a/src/plone/restapi/tests/test_dxcontent_serializer.py b/src/plone/restapi/tests/test_dxcontent_serializer.py index 567aa01e62..1989181472 100644 --- a/src/plone/restapi/tests/test_dxcontent_serializer.py +++ b/src/plone/restapi/tests/test_dxcontent_serializer.py @@ -16,11 +16,13 @@ from plone.namedfile.file import NamedFile from plone.registry.interfaces import IRegistry from plone.restapi.interfaces import IExpandableElement +from plone.restapi.interfaces import IObjectPrimaryFieldTarget from plone.restapi.interfaces import ISerializeToJson from plone.restapi.testing import PLONE_RESTAPI_DX_INTEGRATION_TESTING from plone.restapi.tests.test_expansion import ExpandableElementFoo from plone.restapi.serializer.utils import get_portal_type_title from plone.uuid.interfaces import IMutableUUID +from plone.uuid.interfaces import IUUID from Products.CMFCore.utils import getToolByName from zope.component import getGlobalSiteManager from zope.component import getMultiAdapter @@ -756,3 +758,38 @@ def test_primary_field_target_with_edit_permissions(self): serializer = getMultiAdapter((self.portal.doc1, self.request), ISerializeToJson) data = serializer() self.assertNotIn("targetUrl", data) + + def test_primary_field_target_for_link_objects_for_auth_return_none(self): + self.portal.invokeFactory( + "Document", + id="linked", + ) + self.portal.invokeFactory( + "Link", + id="link", + remoteUrl=f"../resolveuid/{IUUID(self.portal.linked)}", + ) + wftool = getToolByName(self.portal, "portal_workflow") + wftool.doActionFor(self.portal.linked, "publish") + adapter = getMultiAdapter( + (self.portal.link, self.request), IObjectPrimaryFieldTarget + ) + self.assertEqual(adapter(), None) + + def test_primary_field_target_for_link_objects_for_anonymous(self): + self.portal.invokeFactory( + "Document", + id="linked", + ) + self.portal.invokeFactory( + "Link", + id="link", + remoteUrl=f"../resolveuid/{IUUID(self.portal.linked)}", + ) + wftool = getToolByName(self.portal, "portal_workflow") + wftool.doActionFor(self.portal.linked, "publish") + logout() + adapter = getMultiAdapter( + (self.portal.link, self.request), IObjectPrimaryFieldTarget + ) + self.assertEqual(adapter(), self.portal.linked.absolute_url())