plone.restapi/plone-only-login: Add support for expandable elements
Thomas Buchberger <jenkins-z4DKO/[email protected]>
| Newsgroups | gmane.comp.web.zope.plone.cvs |
|---|---|
| Message-ID | <[email protected]> |
Repository: plone.restapi Branch: refs/heads/plone-only-login Date: 2017-07-25T10:37:43+02:00 Author: Thomas Buchberger (buchi) <t.buchberger-/8aE7x569Db/[email protected]> Commit: https://github.com/plone/plone.restapi/commit/89952f511d97f67eb2439534383ff012dd9bf434 Add support for expandable elements Files changed: A src/plone/restapi/serializer/expansion.py A src/plone/restapi/tests/test_expansion.py M CHANGES.rst M src/plone/restapi/interfaces.py M src/plone/restapi/serializer/atcontent.py M src/plone/restapi/serializer/dxcontent.py M src/plone/restapi/serializer/site.py M src/plone/restapi/tests/test_atcontent_serializer.py M src/plone/restapi/tests/test_dxcontent_serializer.py diff --git a/CHANGES.rst b/CHANGES.rst index f84f297b..f25f1bb3 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -1,6 +1,15 @@ Changelog ========= +1.0a21 (unreleased) +------------------- + +New Features: + +- Add support for expandable elements. + [buchi] + + 1.0a20 (2017-07-24) ------------------- diff --git a/src/plone/restapi/interfaces.py b/src/plone/restapi/interfaces.py index e3b15f59..eb57f229 100644 --- a/src/plone/restapi/interfaces.py +++ b/src/plone/restapi/interfaces.py @@ -65,6 +65,16 @@ def __call__(value): """ +class IExpandableElement(Interface): + """A named adapter that deserializes an element in expanded or collapsed + form. + """ + + def __call__(expand=False): + """ + """ + + class IZCatalogCompatibleQuery(Interface): """A multi adapter responsible for converting a catalog query provided as a Python dictionary, but with possibly incorrect value types, to a diff --git a/src/plone/restapi/serializer/atcontent.py b/src/plone/restapi/serializer/atcontent.py index 42a68e18..53125363 100644 --- a/src/plone/restapi/serializer/atcontent.py +++ b/src/plone/restapi/serializer/atcontent.py @@ -1,18 +1,19 @@ # -*- coding: utf-8 -*- from Acquisition import aq_inner from Acquisition import aq_parent +from Products.Archetypes.interfaces import IBaseFolder +from Products.Archetypes.interfaces import IBaseObject +from Products.CMFCore.utils import getToolByName from plone.restapi.batching import HypermediaBatch from plone.restapi.interfaces import IFieldSerializer from plone.restapi.interfaces import ISerializeToJson from plone.restapi.interfaces import ISerializeToJsonSummary -from Products.Archetypes.interfaces import IBaseFolder -from Products.Archetypes.interfaces import IBaseObject -from Products.CMFCore.utils import getToolByName +from plone.restapi.serializer.expansion import expandable_elements from zope.component import adapter from zope.component import getMultiAdapter from zope.component import queryMultiAdapter -from zope.interface import implementer from zope.interface import Interface +from zope.interface import implementer @implementer(ISerializeToJson) @@ -48,6 +49,9 @@ def __call__(self, version=None): 'layout': self.context.getLayout(), } + # Insert expandable elements + result.update(expandable_elements(self.context, self.request)) + for field in obj.Schema().fields(): if 'r' not in field.mode or not field.checkPermission('r', obj): diff --git a/src/plone/restapi/serializer/dxcontent.py b/src/plone/restapi/serializer/dxcontent.py index b5a89239..3ca280eb 100644 --- a/src/plone/restapi/serializer/dxcontent.py +++ b/src/plone/restapi/serializer/dxcontent.py @@ -2,6 +2,7 @@ from AccessControl import getSecurityManager from Acquisition import aq_inner from Acquisition import aq_parent +from Products.CMFCore.utils import getToolByName from plone.autoform.interfaces import READ_PERMISSIONS_KEY from plone.dexterity.interfaces import IDexterityContainer from plone.dexterity.interfaces import IDexterityContent @@ -11,14 +12,14 @@ from plone.restapi.interfaces import ISerializeToJson from plone.restapi.interfaces import ISerializeToJsonSummary from plone.restapi.serializer.converters import json_compatible +from plone.restapi.serializer.expansion import expandable_elements from plone.supermodel.utils import mergedTaggedValueDict -from Products.CMFCore.utils import getToolByName from zope.component import adapter from zope.component import getMultiAdapter from zope.component import queryMultiAdapter from zope.component import queryUtility -from zope.interface import implementer from zope.interface import Interface +from zope.interface import implementer from zope.schema import getFields from zope.security.interfaces import IPermission @@ -61,7 +62,11 @@ def __call__(self, version=None): 'layout': self.context.getLayout(), } - for schema in iterSchemata(obj): + # Insert 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) diff --git a/src/plone/restapi/serializer/expansion.py b/src/plone/restapi/serializer/expansion.py new file mode 100644 index 00000000..6253f69c --- /dev/null +++ b/src/plone/restapi/serializer/expansion.py @@ -0,0 +1,17 @@ +# -*- coding: utf-8 -*- +from plone.restapi.interfaces import IExpandableElement +from zope.component import getAdapters + + +def expandable_elements(context, request): + """Returns a dict containing all expandable elements. + """ + expands = request.form.get('expand', '').split(',') + elements = getAdapters((context, request), IExpandableElement) + res = {} + for element in elements: + if element[0] in expands: + res.update(element[1](expand=True)) + else: + res.update(element[1](expand=False)) + return res diff --git a/src/plone/restapi/serializer/site.py b/src/plone/restapi/serializer/site.py index 8476f64d..24b8284e 100644 --- a/src/plone/restapi/serializer/site.py +++ b/src/plone/restapi/serializer/site.py @@ -1,13 +1,14 @@ # -*- coding: utf-8 -*- +from Products.CMFCore.utils import getToolByName +from Products.CMFPlone.interfaces import IPloneSiteRoot from plone.restapi.batching import HypermediaBatch from plone.restapi.interfaces import ISerializeToJson from plone.restapi.interfaces import ISerializeToJsonSummary -from Products.CMFCore.utils import getToolByName -from Products.CMFPlone.interfaces import IPloneSiteRoot +from plone.restapi.serializer.expansion import expandable_elements from zope.component import adapter from zope.component import getMultiAdapter -from zope.interface import implementer from zope.interface import Interface +from zope.interface import implementer @implementer(ISerializeToJson) @@ -44,6 +45,9 @@ def __call__(self, version=None): 'parent': {}, } + # Insert expandable elements + result.update(expandable_elements(self.context, self.request)) + result['items_total'] = batch.items_total if batch.links: result['batching'] = batch.links diff --git a/src/plone/restapi/tests/test_atcontent_serializer.py b/src/plone/restapi/tests/test_atcontent_serializer.py index 143dde64..09d41f87 100644 --- a/src/plone/restapi/tests/test_atcontent_serializer.py +++ b/src/plone/restapi/tests/test_atcontent_serializer.py @@ -2,9 +2,14 @@ from DateTime import DateTime from plone.app.testing import TEST_USER_ID from plone.app.testing import setRoles +from plone.restapi.interfaces import IExpandableElement from plone.restapi.interfaces import ISerializeToJson from plone.restapi.testing import PLONE_RESTAPI_AT_INTEGRATION_TESTING +from plone.restapi.tests.test_expansion import ExpandableElementFoo from zope.component import getMultiAdapter +from zope.component import provideAdapter +from zope.interface import Interface +from zope.publisher.interfaces.browser import IBrowserRequest import json import unittest @@ -149,3 +154,14 @@ def test_get_layout(self): obj = self.serialize(self.doc1) self.assertIn('layout', obj) self.assertEquals(current_layout, obj['layout']) + + def test_serializer_includes_expansion(self): + provideAdapter( + ExpandableElementFoo, + adapts=(Interface, IBrowserRequest), + provides=IExpandableElement, + name='foo' + ) + obj = self.serialize(self.doc1) + self.assertIn('foo', obj) + self.assertEqual('collapsed', obj['foo']) diff --git a/src/plone/restapi/tests/test_dxcontent_serializer.py b/src/plone/restapi/tests/test_dxcontent_serializer.py index ad695176..80c3ac01 100644 --- a/src/plone/restapi/tests/test_dxcontent_serializer.py +++ b/src/plone/restapi/tests/test_dxcontent_serializer.py @@ -6,10 +6,15 @@ from datetime import timedelta from plone.app.testing import TEST_USER_ID from plone.app.testing import setRoles +from plone.restapi.interfaces import IExpandableElement 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.uuid.interfaces import IMutableUUID from zope.component import getMultiAdapter +from zope.component import provideAdapter +from zope.interface import Interface +from zope.publisher.interfaces.browser import IBrowserRequest import json import unittest @@ -122,3 +127,14 @@ def test_get_layout(self): obj = self.serialize() self.assertIn('layout', obj) self.assertEquals(current_layout, obj['layout']) + + def test_serializer_includes_expansion(self): + provideAdapter( + ExpandableElementFoo, + adapts=(Interface, IBrowserRequest), + provides=IExpandableElement, + name='foo' + ) + obj = self.serialize() + self.assertIn('foo', obj) + self.assertEqual('collapsed', obj['foo']) diff --git a/src/plone/restapi/tests/test_expansion.py b/src/plone/restapi/tests/test_expansion.py new file mode 100644 index 00000000..394c3f8b --- /dev/null +++ b/src/plone/restapi/tests/test_expansion.py @@ -0,0 +1,67 @@ +# -*- coding: utf-8 -*- +from plone.restapi.interfaces import IExpandableElement +from plone.restapi.serializer.expansion import expandable_elements +from zope.component import provideAdapter +from zope.interface import Interface +from zope.publisher.browser import TestRequest +from zope.publisher.interfaces.browser import IBrowserRequest + +import unittest + + +class ExpandableElementFoo(object): + + def __init__(self, context, request): + self.context = context + self.request = request + + def __call__(self, expand=False): + if expand: + return {'foo': 'expanded'} + else: + return {'foo': 'collapsed'} + + +class ExpandableElementBar(object): + + def __init__(self, context, request): + self.context = context + self.request = request + + def __call__(self, expand=False): + if expand: + return {'bar': 'expanded'} + else: + return {'bar': 'collapsed'} + + +class TestExpansion(unittest.TestCase): + + def setUp(self): + provideAdapter( + ExpandableElementFoo, + adapts=(Interface, IBrowserRequest), + provides=IExpandableElement, + name='foo' + ) + provideAdapter( + ExpandableElementBar, + adapts=(Interface, IBrowserRequest), + provides=IExpandableElement, + name='bar' + ) + + def test_expansion_returns_collapsed_elements(self): + request = TestRequest() + self.assertEqual({'foo': 'collapsed', 'bar': 'collapsed'}, + expandable_elements(None, request)) + + def test_expansion_returns_expanded_element(self): + request = TestRequest(form={'expand': 'foo'}) + self.assertEqual({'foo': 'expanded', 'bar': 'collapsed'}, + expandable_elements(None, request)) + + def test_expansion_returns_multiple_expanded_elements(self): + request = TestRequest(form={'expand': 'foo,bar'}) + self.assertEqual({'foo': 'expanded', 'bar': 'expanded'}, + expandable_elements(None, request)) ------------------------------------------------------------------------------ Check out the vibrant tech community on one of the world's most engaging tech sites, Slashdot.org! http://sdm.link/slashdot