plone.restapi/plone-only-login: Merge pull request #379 from plone/root-reordering

GitHub <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-14T11:34:31+02:00
Author: Víctor Fernández de Alba (sneridagh) <[email protected]>
Commit: https://github.com/plone/plone.restapi/commit/b0d2a87641419831ccc71447165dda770e88fcad

Merge pull request #379 from plone/root-reordering

Site root reordering + set layout

Files changed:
A src/plone/restapi/deserializer/site.py
A src/plone/restapi/tests/mixin_ordering.py
A src/plone/restapi/tests/test_site_deserializer.py
M CHANGES.rst
M docs/source/content.rst
M src/plone/restapi/deserializer/configure.zcml
M src/plone/restapi/deserializer/mixins.py
M src/plone/restapi/services/content/configure.zcml
M src/plone/restapi/tests/test_atcontent_deserializer.py
M src/plone/restapi/tests/test_dxcontent_deserializer.py

diff --git a/CHANGES.rst b/CHANGES.rst
index 66f57a27..f99d1992 100644
--- a/CHANGES.rst
+++ b/CHANGES.rst
@@ -8,6 +8,12 @@ Changelog
   and breadcrumbs as components.
   [timo]
 
+- Support content reordering on the site root.
+  [jaroel]
+
+- Support setting Layout on the site root.
+  [jaroel]
+
 
 1.0a19 (2017-06-25)
 -------------------
diff --git a/docs/source/content.rst b/docs/source/content.rst
index 396f0cf0..5595bc70 100644
--- a/docs/source/content.rst
+++ b/docs/source/content.rst
@@ -328,8 +328,12 @@ The resources contained within a resource can be reordered using the `ordering`
 
 Use the `obj_id` subkey to specify which resource to reorder.
 The subkey `delta` can be 'top', 'bottom', or a negative or positive integer for moving up or down.
+
 Reordering resources within a subset of resources can be done using the `subset_ids` subkey.
+
 A response 400 BadRequest with a message 'Client/server ordering mismatch' will be returned if the value is not in the same order as serverside.
 
+A response 400 BadRequest with a message 'Content ordering is not supported by this resource' will be returned if the container does not support ordering.
+
 ..  http:example:: curl httpie python-requests
     :request: _json/content_reorder.req
diff --git a/src/plone/restapi/deserializer/configure.zcml b/src/plone/restapi/deserializer/configure.zcml
index 006d4482..21fcf196 100644
--- a/src/plone/restapi/deserializer/configure.zcml
+++ b/src/plone/restapi/deserializer/configure.zcml
@@ -3,6 +3,8 @@
     xmlns:zcml="http://namespaces.zope.org/zcml"
     i18n_domain="plone.restapi">
 
+  <adapter factory=".site.DeserializeSiteRootFromJson" />
+
   <adapter factory=".dxcontent.DeserializeFromJson" />
   <adapter factory=".dxfields.DefaultFieldDeserializer" />
   <adapter factory=".dxfields.DatetimeFieldDeserializer" />
diff --git a/src/plone/restapi/deserializer/mixins.py b/src/plone/restapi/deserializer/mixins.py
index c72f7ef5..98739ed3 100644
--- a/src/plone/restapi/deserializer/mixins.py
+++ b/src/plone/restapi/deserializer/mixins.py
@@ -12,11 +12,20 @@ def handle_ordering(self, data):
             delta = data['ordering']['delta']
             subset_ids = data['ordering'].get('subset_ids')
 
+            # The REST api returns only content items and a Zope resource
+            # may contain non-content items. We need to set the subset_ids
+            # so we'll move items relative to each other.
+            if not subset_ids:
+                subset_ids = self.context.contentIds()
+
             self.reorderItems(obj_id, delta, subset_ids)
 
-    def reorderItems(self, obj_id, delta, subset_ids=None):
+    def reorderItems(self, obj_id, delta, subset_ids):
         # Based on wildcard.foldercontents.viewsItemOrder
         ordering = self.getOrdering()
+        if ordering is None:
+            msg = 'Content ordering is not supported by this resource'
+            raise BadRequest(msg)
 
         # Make sure we're seeing the same order as the client is.
         if subset_ids:
@@ -38,7 +47,7 @@ def reorderItems(self, obj_id, delta, subset_ids=None):
     def getOrdering(self):
         if IPloneSiteRoot.providedBy(self.context):
             return self.context
-        else:
+        elif getattr(self.context, 'getOrdering', None):
             ordering = self.context.getOrdering()
             if not IExplicitOrdering.providedBy(ordering):
                 return None
diff --git a/src/plone/restapi/deserializer/site.py b/src/plone/restapi/deserializer/site.py
new file mode 100644
index 00000000..7b749383
--- /dev/null
+++ b/src/plone/restapi/deserializer/site.py
@@ -0,0 +1,36 @@
+# -*- coding: utf-8 -*-
+from Products.CMFPlone.interfaces import IPloneSiteRoot
+from plone.restapi.deserializer import json_body
+from plone.restapi.interfaces import IDeserializeFromJson
+from zope.component import adapter
+from zope.interface import implementer
+
+from plone.restapi.deserializer.mixins import OrderingMixin
+from zope.publisher.interfaces import IRequest
+
+
+@implementer(IDeserializeFromJson)
+@adapter(IPloneSiteRoot, IRequest)
+class DeserializeSiteRootFromJson(OrderingMixin, object):
+    """JSON deserializer for the Plone site root
+    """
+
+    def __init__(self, context, request):
+        self.context = context
+        self.request = request
+
+    def __call__(self, validate_all=False):
+        # Currently we only do layout and ordering, as the plone site root
+        # has no schema or something like that.
+        data = json_body(self.request)
+
+        if 'layout' in data:
+            layout = data['layout']
+            self.context.setLayout(layout)
+
+        # OrderingMixin
+        if 'ordering' in data and 'subset_ids' not in data['ordering']:
+            data['ordering']['subset_ids'] = self.context.contentIds()
+        self.handle_ordering(data)
+
+        return self.context
diff --git a/src/plone/restapi/services/content/configure.zcml b/src/plone/restapi/services/content/configure.zcml
index 9e338f5f..f3cbde14 100644
--- a/src/plone/restapi/services/content/configure.zcml
+++ b/src/plone/restapi/services/content/configure.zcml
@@ -19,6 +19,13 @@
 
   <plone:service
     method="PATCH"
+    for="Products.CMFPlone.interfaces.IPloneSiteRoot"
+    factory=".update.ContentPatch"
+    permission="cmf.ModifyPortalContent"
+    />
+
+  <plone:service
+    method="PATCH"
     for="Products.CMFCore.interfaces.IContentish"
     factory=".update.ContentPatch"
     permission="cmf.ModifyPortalContent"
diff --git a/src/plone/restapi/tests/mixin_ordering.py b/src/plone/restapi/tests/mixin_ordering.py
new file mode 100644
index 00000000..180cc0e8
--- /dev/null
+++ b/src/plone/restapi/tests/mixin_ordering.py
@@ -0,0 +1,99 @@
+# -*- coding: utf-8 -*-
+import json
+
+from zExceptions import BadRequest
+
+
+class OrderingMixin():
+    # This assumes there is a self.folder with 9 documents in it.
+
+    def test_reorder(self):
+        # We run all this in one test, because of dependend ordering.
+        # initial situation
+        self.assertEquals(
+            ['doc1', 'doc2', 'doc3', 'doc4', 'doc5', 'doc6', 'doc7', 'doc8', 'doc9', ],  # noqa
+            self.folder.contentIds()
+        )
+
+        # Normal
+        # Move to top
+        data = {'ordering': {'delta': 'top', 'obj_id': 'doc9'}}
+        self.deserialize(body=json.dumps(data), context=self.folder)
+        self.assertEquals(
+            ['doc9', 'doc1', 'doc2', 'doc3', 'doc4', 'doc5', 'doc6', 'doc7', 'doc8', ],  # noqa
+            self.folder.contentIds()
+        )
+
+        # Move to bottom
+        data = {'ordering': {'delta': 'bottom', 'obj_id': 'doc9'}}
+        self.deserialize(body=json.dumps(data), context=self.folder)
+        self.assertEquals(
+            ['doc1', 'doc2', 'doc3', 'doc4', 'doc5', 'doc6', 'doc7', 'doc8', 'doc9', ],  # noqa
+            self.folder.contentIds()
+        )
+
+        # Delta up
+        data = {'ordering': {'delta': -2, 'obj_id': 'doc5'}}
+        self.deserialize(body=json.dumps(data), context=self.folder)
+        self.assertEquals(
+            ['doc1', 'doc2', 'doc5', 'doc3', 'doc4', 'doc6', 'doc7', 'doc8', 'doc9', ],  # noqa
+            self.folder.contentIds()
+        )
+
+        # Delta down
+        data = {'ordering': {'delta': 2, 'obj_id': 'doc6'}}
+        self.deserialize(body=json.dumps(data), context=self.folder)
+        self.assertEquals(
+            ['doc1', 'doc2', 'doc5', 'doc3', 'doc4', 'doc7', 'doc8', 'doc6', 'doc9', ],  # noqa
+            self.folder.contentIds()
+        )
+
+        # subset ids
+        # Move to top
+        data = {'ordering': {'delta': 'top', 'obj_id': 'doc8', 'subset_ids': ['doc2', 'doc3', 'doc8']}}  # noqa
+        self.deserialize(body=json.dumps(data), context=self.folder)
+        self.assertEquals(
+            ['doc1', 'doc8', 'doc5', 'doc2', 'doc4', 'doc7', 'doc3', 'doc6', 'doc9'],  # noqa
+            self.folder.contentIds()
+        )
+
+        # Move to bottom
+        data = {'ordering': {'delta': 'bottom', 'obj_id': 'doc8', 'subset_ids': ['doc8', 'doc2', 'doc3']}}  # noqa
+        self.deserialize(body=json.dumps(data), context=self.folder)
+        self.assertEquals(
+            ['doc1', 'doc2', 'doc5', 'doc3', 'doc4', 'doc7', 'doc8', 'doc6', 'doc9'],  # noqa
+            self.folder.contentIds()
+        )
+
+        # Delta up
+        data = {'ordering': {'delta': -1, 'obj_id': 'doc8', 'subset_ids': ['doc2', 'doc3', 'doc8']}}  # noqa
+        self.deserialize(body=json.dumps(data), context=self.folder)
+        self.assertEquals(
+            ['doc1', 'doc2', 'doc5', 'doc8', 'doc4', 'doc7', 'doc3', 'doc6', 'doc9'],  # noqa
+            self.folder.contentIds()
+        )
+
+        # Delta down
+        data = {'ordering': {'delta': 1, 'obj_id': 'doc2', 'subset_ids': ['doc2', 'doc8', 'doc3']}}  # noqa
+        self.deserialize(body=json.dumps(data), context=self.folder)
+        self.assertEquals(
+            ['doc1', 'doc8', 'doc5', 'doc2', 'doc4', 'doc7', 'doc3', 'doc6', 'doc9'],  # noqa
+            self.folder.contentIds()
+        )
+
+    def test_reorder_subsetids(self):
+        # sanity check, initial situation
+        self.assertEquals(
+            ['doc1', 'doc2', 'doc3', 'doc4', 'doc5', 'doc6', 'doc7', 'doc8', 'doc9'],  # noqa
+            self.folder.contentIds()
+        )
+
+        data = {'ordering': {'delta': 1, 'obj_id': 'doc8', 'subset_ids': ['doc2', 'doc8', 'doc6']}}  # noqa
+
+        with self.assertRaises(BadRequest) as cm:
+            self.deserialize(body=json.dumps(data), context=self.folder)
+
+        self.assertEquals(
+            u'Client/server ordering mismatch',
+            cm.exception.message
+        )
diff --git a/src/plone/restapi/tests/test_atcontent_deserializer.py b/src/plone/restapi/tests/test_atcontent_deserializer.py
index 2a7d5a2d..a97f3cbd 100644
--- a/src/plone/restapi/tests/test_atcontent_deserializer.py
+++ b/src/plone/restapi/tests/test_atcontent_deserializer.py
@@ -7,6 +7,7 @@
 from plone.restapi.deserializer.atcontent import ValidationRequest
 from plone.restapi.interfaces import IDeserializeFromJson
 from plone.restapi.testing import PLONE_RESTAPI_AT_INTEGRATION_TESTING
+from plone.restapi.tests.mixin_ordering import OrderingMixin
 from zExceptions import BadRequest
 from zope.component import getMultiAdapter
 from zope.component import provideHandler
@@ -17,11 +18,10 @@
 from Products.Archetypes.interfaces import IObjectPreValidation
 
 
-import json
 import unittest
 
 
-class TestATContentDeserializer(unittest.TestCase):
+class TestATContentDeserializer(unittest.TestCase, OrderingMixin):
 
     layer = PLONE_RESTAPI_AT_INTEGRATION_TESTING
 
@@ -152,97 +152,6 @@ def test_set_layout(self):
         self.deserialize(body='{"layout": "my_new_layout"}')
         self.assertEquals('my_new_layout', self.doc1.getLayout())
 
-    def test_reorder(self):
-        # We run all this in one test, because of dependend ordering.
-        # initial situation
-        self.assertEquals(
-            ['doc1', 'doc2', 'doc3', 'doc4', 'doc5', 'doc6', 'doc7', 'doc8', 'doc9', ],  # noqa
-            self.folder.objectIds()
-        )
-
-        # Normal
-        # Move to top
-        data = {'ordering': {'delta': 'top', 'obj_id': 'doc9'}}
-        self.deserialize(body=json.dumps(data), context=self.folder)
-        self.assertEquals(
-            ['doc9', 'doc1', 'doc2', 'doc3', 'doc4', 'doc5', 'doc6', 'doc7', 'doc8', ],  # noqa
-            self.folder.objectIds()
-        )
-
-        # Move to bottom
-        data = {'ordering': {'delta': 'bottom', 'obj_id': 'doc9'}}
-        self.deserialize(body=json.dumps(data), context=self.folder)
-        self.assertEquals(
-            ['doc1', 'doc2', 'doc3', 'doc4', 'doc5', 'doc6', 'doc7', 'doc8', 'doc9', ],  # noqa
-            self.folder.objectIds()
-        )
-
-        # Delta up
-        data = {'ordering': {'delta': -2, 'obj_id': 'doc5'}}
-        self.deserialize(body=json.dumps(data), context=self.folder)
-        self.assertEquals(
-            ['doc1', 'doc2', 'doc5', 'doc3', 'doc4', 'doc6', 'doc7', 'doc8', 'doc9', ],  # noqa
-            self.folder.objectIds()
-        )
-
-        # Delta down
-        data = {'ordering': {'delta': 2, 'obj_id': 'doc6'}}
-        self.deserialize(body=json.dumps(data), context=self.folder)
-        self.assertEquals(
-            ['doc1', 'doc2', 'doc5', 'doc3', 'doc4', 'doc7', 'doc8', 'doc6', 'doc9', ],  # noqa
-            self.folder.objectIds()
-        )
-
-        # subset ids
-        # Move to top
-        data = {'ordering': {'delta': 'top', 'obj_id': 'doc8', 'subset_ids': ['doc2', 'doc3', 'doc8']}}  # noqa
-        self.deserialize(body=json.dumps(data), context=self.folder)
-        self.assertEquals(
-            ['doc1', 'doc8', 'doc5', 'doc2', 'doc4', 'doc7', 'doc3', 'doc6', 'doc9'],  # noqa
-            self.folder.objectIds()
-        )
-
-        # Move to bottom
-        data = {'ordering': {'delta': 'bottom', 'obj_id': 'doc8', 'subset_ids': ['doc8', 'doc2', 'doc3']}}  # noqa
-        self.deserialize(body=json.dumps(data), context=self.folder)
-        self.assertEquals(
-            ['doc1', 'doc2', 'doc5', 'doc3', 'doc4', 'doc7', 'doc8', 'doc6', 'doc9'],  # noqa
-            self.folder.objectIds()
-        )
-
-        # Delta up
-        data = {'ordering': {'delta': -1, 'obj_id': 'doc8', 'subset_ids': ['doc2', 'doc3', 'doc8']}}  # noqa
-        self.deserialize(body=json.dumps(data), context=self.folder)
-        self.assertEquals(
-            ['doc1', 'doc2', 'doc5', 'doc8', 'doc4', 'doc7', 'doc3', 'doc6', 'doc9'],  # noqa
-            self.folder.objectIds()
-        )
-
-        # Delta down
-        data = {'ordering': {'delta': 1, 'obj_id': 'doc2', 'subset_ids': ['doc2', 'doc8', 'doc3']}}  # noqa
-        self.deserialize(body=json.dumps(data), context=self.folder)
-        self.assertEquals(
-            ['doc1', 'doc8', 'doc5', 'doc2', 'doc4', 'doc7', 'doc3', 'doc6', 'doc9'],  # noqa
-            self.folder.objectIds()
-        )
-
-    def test_reorder_subsetids(self):
-        # sanity check, initial situation
-        self.assertEquals(
-            ['doc1', 'doc2', 'doc3', 'doc4', 'doc5', 'doc6', 'doc7', 'doc8', 'doc9'],  # noqa
-            self.folder.objectIds()
-        )
-
-        data = {'ordering': {'delta': 1, 'obj_id': 'doc8', 'subset_ids': ['doc2', 'doc8', 'doc6']}}  # noqa
-
-        with self.assertRaises(BadRequest) as cm:
-            self.deserialize(body=json.dumps(data), context=self.folder)
-
-        self.assertEquals(
-            u'Client/server ordering mismatch',
-            cm.exception.message
-        )
-
 
 class TestValidationRequest(unittest.TestCase):
 
diff --git a/src/plone/restapi/tests/test_dxcontent_deserializer.py b/src/plone/restapi/tests/test_dxcontent_deserializer.py
index 7e8dd906..26fd2411 100644
--- a/src/plone/restapi/tests/test_dxcontent_deserializer.py
+++ b/src/plone/restapi/tests/test_dxcontent_deserializer.py
@@ -6,16 +6,16 @@
 from plone.restapi.interfaces import IDeserializeFromJson
 from plone.restapi.testing import PLONE_RESTAPI_DX_INTEGRATION_TESTING
 from plone.restapi.tests.dxtypes import ITestAnnotationsBehavior
+from plone.restapi.tests.mixin_ordering import OrderingMixin
 from zExceptions import BadRequest
 from zope.component import getMultiAdapter
 from zope.component import provideHandler
 from zope.lifecycleevent.interfaces import IObjectModifiedEvent
 
-import json
 import unittest
 
 
-class TestDXContentDeserializer(unittest.TestCase):
+class TestDXContentDeserializer(unittest.TestCase, OrderingMixin):
 
     layer = PLONE_RESTAPI_DX_INTEGRATION_TESTING
 
@@ -147,93 +147,3 @@ def test_set_layout(self):
         self.assertNotEquals(current_layout, "my_new_layout")
         self.deserialize(body='{"layout": "my_new_layout"}')
         self.assertEquals('my_new_layout', self.portal.doc1.getLayout())
-
-    def test_reorder(self):  # noqa: ignore=E501
-        # We run all this in one test, because of dependend ordering.
-        # initial situation
-        self.assertEquals(
-            ['doc1', 'doc2', 'doc3', 'doc4', 'doc5', 'doc6', 'doc7', 'doc8', 'doc9', ],  # noqa
-            self.folder.objectIds()
-        )
-
-        # Normal
-        # Move to top
-        data = {'ordering': {'delta': 'top', 'obj_id': 'doc9'}}
-        self.deserialize(body=json.dumps(data), context=self.folder)
-        self.assertEquals(
-            ['doc9', 'doc1', 'doc2', 'doc3', 'doc4', 'doc5', 'doc6', 'doc7', 'doc8', ],  # noqa
-            self.folder.objectIds())
-
-        # Move to bottom
-        data = {'ordering': {'delta': 'bottom', 'obj_id': 'doc9'}}
-        self.deserialize(body=json.dumps(data), context=self.folder)
-        self.assertEquals(
-            ['doc1', 'doc2', 'doc3', 'doc4', 'doc5', 'doc6', 'doc7', 'doc8', 'doc9', ],  # noqa
-            self.folder.objectIds()
-        )
-
-        # Delta up
-        data = {'ordering': {'delta': -2, 'obj_id': 'doc5'}}
-        self.deserialize(body=json.dumps(data), context=self.folder)
-        self.assertEquals(
-            ['doc1', 'doc2', 'doc5', 'doc3', 'doc4', 'doc6', 'doc7', 'doc8', 'doc9', ],  # noqa
-            self.folder.objectIds()
-        )
-
-        # Delta down
-        data = {'ordering': {'delta': 2, 'obj_id': 'doc6'}}
-        self.deserialize(body=json.dumps(data), context=self.folder)
-        self.assertEquals(
-            ['doc1', 'doc2', 'doc5', 'doc3', 'doc4', 'doc7', 'doc8', 'doc6', 'doc9', ],  # noqa
-            self.folder.objectIds()
-        )
-
-        # subset ids
-        # Move to top
-        data = {'ordering': {'delta': 'top', 'obj_id': 'doc8', 'subset_ids': ['doc2', 'doc3', 'doc8']}}  # noqa
-        self.deserialize(body=json.dumps(data), context=self.folder)
-        self.assertEquals(
-            ['doc1', 'doc8', 'doc5', 'doc2', 'doc4', 'doc7', 'doc3', 'doc6', 'doc9'],  # noqa
-            self.folder.objectIds()
-        )
-
-        # Move to bottom
-        data = {'ordering': {'delta': 'bottom', 'obj_id': 'doc8', 'subset_ids': ['doc8', 'doc2', 'doc3']}}  # noqa
-        self.deserialize(body=json.dumps(data), context=self.folder)
-        self.assertEquals(
-            ['doc1', 'doc2', 'doc5', 'doc3', 'doc4', 'doc7', 'doc8', 'doc6', 'doc9'],  # noqa
-            self.folder.objectIds()
-        )
-
-        # Delta up
-        data = {'ordering': {'delta': -1, 'obj_id': 'doc8', 'subset_ids': ['doc2', 'doc3', 'doc8']}}  # noqa
-        self.deserialize(body=json.dumps(data), context=self.folder)
-        self.assertEquals(
-            ['doc1', 'doc2', 'doc5', 'doc8', 'doc4', 'doc7', 'doc3', 'doc6', 'doc9'],  # noqa
-            self.folder.objectIds()
-        )
-
-        # Delta down
-        data = {'ordering': {'delta': 1, 'obj_id': 'doc2', 'subset_ids': ['doc2', 'doc8', 'doc3']}}  # noqa
-        self.deserialize(body=json.dumps(data), context=self.folder)
-        self.assertEquals(
-            ['doc1', 'doc8', 'doc5', 'doc2', 'doc4', 'doc7', 'doc3', 'doc6', 'doc9'],  # noqa
-            self.folder.objectIds()
-        )
-
-    def test_reorder_subsetids(self):
-        # sanity check, initial situation
-        self.assertEquals(
-            ['doc1', 'doc2', 'doc3', 'doc4', 'doc5', 'doc6', 'doc7', 'doc8', 'doc9'],  # noqa
-            self.folder.objectIds()
-        )
-
-        data = {'ordering': {'delta': 1, 'obj_id': 'doc8', 'subset_ids': ['doc2', 'doc8', 'doc6']}}  # noqa
-
-        with self.assertRaises(BadRequest) as cm:
-            self.deserialize(body=json.dumps(data), context=self.folder)
-
-        self.assertEquals(
-            u'Client/server ordering mismatch',
-            cm.exception.message
-        )
diff --git a/src/plone/restapi/tests/test_site_deserializer.py b/src/plone/restapi/tests/test_site_deserializer.py
new file mode 100644
index 00000000..4374a46b
--- /dev/null
+++ b/src/plone/restapi/tests/test_site_deserializer.py
@@ -0,0 +1,39 @@
+# -*- coding: utf-8 -*-
+from plone.restapi.interfaces import IDeserializeFromJson
+from plone.restapi.testing import PLONE_RESTAPI_DX_INTEGRATION_TESTING
+from plone.restapi.tests.mixin_ordering import OrderingMixin
+from zope.component import getMultiAdapter
+
+import unittest
+
+
+class TestDXContentDeserializer(unittest.TestCase, OrderingMixin):
+
+    layer = PLONE_RESTAPI_DX_INTEGRATION_TESTING
+
+    def setUp(self):
+        self.portal = self.layer['portal']
+        self.request = self.layer['request']
+
+        # ordering setup
+        self.folder = self.portal
+
+        for x in range(1, 10):
+            self.folder.invokeFactory(
+                'Document',
+                id='doc' + str(x),
+                title='Test doc ' + str(x)
+            )
+
+    def deserialize(self, body='{}', validate_all=False, context=None):
+        context = context or self.portal
+        self.request['BODY'] = body
+        deserializer = getMultiAdapter((context, self.request),
+                                       IDeserializeFromJson)
+        return deserializer(validate_all=validate_all)
+
+    def test_set_layout(self):
+        current_layout = self.portal.getLayout()
+        self.assertNotEquals(current_layout, "my_new_layout")
+        self.deserialize(body='{"layout": "my_new_layout"}')
+        self.assertEquals('my_new_layout', self.portal.getLayout())



------------------------------------------------------------------------------
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.