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
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.