Skip to content
This repository was archived by the owner on Oct 26, 2021. It is now read-only.

Commit a2995a2

Browse files
committed
Add very basic object serialization to json.
1 parent 185bef7 commit a2995a2

File tree

4 files changed

+210
-71
lines changed

4 files changed

+210
-71
lines changed

src/plone/app/angularjs/api/api.py

Lines changed: 6 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,9 @@
66
from zope.component.hooks import getSite
77
from Products.CMFCore.utils import getToolByName
88
from plone.app.angularjs.interfaces import IAPIMethod
9+
from plone.app.angularjs.utils import serialize_to_json
10+
11+
912
import json
1013

1114

@@ -60,20 +63,9 @@ def __call__(self):
6063
'message': "No object found for path '%s'." % path,
6164
}
6265

63-
# process rich text
64-
text = getattr(obj, 'text', None)
65-
if text is not None:
66-
text = text.output
67-
else:
68-
text = ''
69-
70-
return {
71-
'route': path,
72-
'id': obj.id,
73-
'title': obj.title,
74-
'description': obj.Description(),
75-
'text': text
76-
}
66+
result = serialize_to_json(obj)
67+
result['route'] = path
68+
return result
7769

7870

7971
class TopNavigation(BrowserView):

src/plone/app/angularjs/tests/test_api_traversal.py

Lines changed: 27 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -24,14 +24,14 @@ def setUp(self):
2424
setRoles(self.portal, TEST_USER_ID, ['Manager'])
2525
directlyProvides(self.portal, IAPIRequest)
2626

27-
def test_traversal_view_is_registered(self):
27+
def test_api_traversal_view_is_registered(self):
2828
view = getMultiAdapter(
2929
(self.portal, self.request),
3030
name="traversal"
3131
)
3232
self.assertTrue(view())
3333

34-
def test_traversal_without_param(self):
34+
def test_api_traversal_without_param(self):
3535
view = getMultiAdapter(
3636
(self.portal, self.request),
3737
name="traversal"
@@ -41,7 +41,7 @@ def test_traversal_without_param(self):
4141
{u'message': u'No path has been provided.', u'code': u'404'}
4242
)
4343

44-
def test_traversal_empty_param(self):
44+
def test_api_traversal_empty_param(self):
4545
self.request.set('path', '')
4646
view = getMultiAdapter(
4747
(self.portal, self.request),
@@ -52,7 +52,7 @@ def test_traversal_empty_param(self):
5252
{u'message': u'No path has been provided.', u'code': u'404'}
5353
)
5454

55-
def test_traversal_not_found(self):
55+
def test_api_traversal_not_found(self):
5656
self.request.set('path', 'nonexisting')
5757

5858
view = getMultiAdapter(
@@ -68,13 +68,8 @@ def test_traversal_not_found(self):
6868
}
6969
)
7070

71-
def test_traversal_document(self):
72-
self.portal.invokeFactory('Document', 'doc1')
73-
self.portal.doc1.text = RichTextValue(
74-
u"Lorem ipsum.",
75-
'text/plain',
76-
'text/html'
77-
)
71+
def test_api_traversal_document_returns_title(self):
72+
self.portal.invokeFactory('Document', 'doc1', title=u'Document 1')
7873
self.request.set('path', 'doc1')
7974

8075
view = getMultiAdapter(
@@ -83,18 +78,13 @@ def test_traversal_document(self):
8378
)
8479

8580
self.assertEqual(
86-
json.loads(view()),
87-
{
88-
u'route': u'/plone/doc1',
89-
u'title': u'',
90-
u'description': u'',
91-
u'text': u'<p>Lorem ipsum.</p>',
92-
u'id': u'doc1'
93-
}
81+
json.loads(view())['title'],
82+
u'Document 1'
9483
)
9584

96-
def test_traversal_document_sets_title(self):
85+
def test_api_traversal_document_returns_description(self):
9786
self.portal.invokeFactory('Document', 'doc1', title=u'Document 1')
87+
self.portal.doc1.setDescription(u'I am the first document!')
9888
self.request.set('path', 'doc1')
9989

10090
view = getMultiAdapter(
@@ -103,19 +93,17 @@ def test_traversal_document_sets_title(self):
10393
)
10494

10595
self.assertEqual(
106-
json.loads(view()),
107-
{
108-
u'route': u'/plone/doc1',
109-
u'title': u'Document 1',
110-
u'description': u'',
111-
u'text': u'',
112-
u'id': u'doc1'
113-
}
96+
json.loads(view())['description'],
97+
u'I am the first document!'
11498
)
11599

116-
def test_traversal_document_sets_description(self):
117-
self.portal.invokeFactory('Document', 'doc1', title=u'Document 1')
118-
self.portal.doc1.setDescription(u'I am the first document!')
100+
def test_api_traversal_document_returns_text(self):
101+
self.portal.invokeFactory('Document', 'doc1')
102+
self.portal.doc1.text = RichTextValue(
103+
u"Lorem ipsum.",
104+
'text/plain',
105+
'text/html'
106+
)
119107
self.request.set('path', 'doc1')
120108

121109
view = getMultiAdapter(
@@ -124,17 +112,11 @@ def test_traversal_document_sets_description(self):
124112
)
125113

126114
self.assertEqual(
127-
json.loads(view()),
128-
{
129-
u'route': u'/plone/doc1',
130-
u'title': u'Document 1',
131-
u'description': u'I am the first document!',
132-
u'text': u'',
133-
u'id': u'doc1'
134-
}
115+
json.loads(view())['text'],
116+
u'<p>Lorem ipsum.</p>'
135117
)
136118

137-
def test_traversal_folder(self):
119+
def test_api_traversal_folder(self):
138120
self.portal.invokeFactory('Folder', 'folder1')
139121
self.request.set('path', 'folder1')
140122

@@ -144,17 +126,11 @@ def test_traversal_folder(self):
144126
)
145127

146128
self.assertEqual(
147-
json.loads(view()),
148-
{
149-
u'route': u'/plone/folder1',
150-
u'title': u'',
151-
u'description': u'',
152-
u'text': u'',
153-
u'id': u'folder1'
154-
}
129+
json.loads(view())['route'],
130+
u'/plone/folder1'
155131
)
156132

157-
def test_traversal_nested_document(self):
133+
def test_api_traversal_nested_document(self):
158134
self.portal.invokeFactory('Folder', 'folder1')
159135
self.portal.folder1.invokeFactory('Document', 'doc1')
160136
self.request.set('path', 'folder1/doc1')
@@ -165,12 +141,6 @@ def test_traversal_nested_document(self):
165141
)
166142

167143
self.assertEqual(
168-
json.loads(view()),
169-
{
170-
u'route': u'/plone/folder1/doc1',
171-
u'title': u'',
172-
u'description': u'',
173-
u'text': u'',
174-
u'id': u'doc1'
175-
}
144+
json.loads(view())['route'],
145+
u'/plone/folder1/doc1'
176146
)

src/plone/app/angularjs/tests/test_utils.py

Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,135 @@
11
# -*- coding: utf-8 -*-
22
import unittest2 as unittest
33
from plone.app.angularjs.utils import underscore_to_camelcase
4+
from plone.app.angularjs.utils import get_object_schema
5+
from plone.app.angularjs.utils import serialize_to_json
6+
from plone.app.angularjs.testing import\
7+
PLONE_APP_ANGULARJS_INTEGRATION_TESTING
8+
from plone.app.textfield.value import RichTextValue
9+
10+
from DateTime import DateTime
11+
12+
13+
class SerializeToJsonIntegrationTest(unittest.TestCase):
14+
15+
layer = PLONE_APP_ANGULARJS_INTEGRATION_TESTING
16+
17+
def setUp(self):
18+
self.app = self.layer['app']
19+
self.portal = self.layer['portal']
20+
21+
def test_serialize_title(self):
22+
self.portal.invokeFactory('Document', id='doc1', title='Doc 1')
23+
24+
self.assertEqual(
25+
serialize_to_json(self.portal.doc1).get('title'),
26+
'Doc 1'
27+
)
28+
29+
def test_serialize_description(self):
30+
self.portal.invokeFactory('Document', id='doc1', title='Doc 1')
31+
self.portal.doc1.description = u'Lorem Ipsum'
32+
33+
self.assertEqual(
34+
serialize_to_json(self.portal.doc1).get('description'),
35+
'Lorem Ipsum'
36+
)
37+
38+
def test_serialize_rich_text(self):
39+
self.portal.invokeFactory('Document', id='doc1', title='Doc 1')
40+
self.portal.doc1.text = RichTextValue(
41+
u"Lorem ipsum.",
42+
'text/plain',
43+
'text/html'
44+
)
45+
46+
self.assertEqual(
47+
serialize_to_json(self.portal.doc1).get('text'),
48+
u'<p>Lorem ipsum.</p>'
49+
)
50+
51+
def test_serialize_datetime(self):
52+
self.portal.invokeFactory('Document', id='doc1', title='Doc 1')
53+
self.portal.doc1.setEffectiveDate(DateTime('2014/04/04'))
54+
self.assertEqual(
55+
serialize_to_json(self.portal.doc1).get('effective'),
56+
'2014/04/04 00:00:00 GMT+2'
57+
)
58+
59+
def test_ignore_underscore_values(self):
60+
self.portal.invokeFactory('Document', id='doc1', title='Doc 1')
61+
62+
self.assertFalse(
63+
'__name__' in serialize_to_json(self.portal.doc1)
64+
)
65+
self.assertFalse(
66+
'manage_options' in serialize_to_json(self.portal.doc1)
67+
)
68+
69+
70+
class GetObjectSchemaUnitTest(unittest.TestCase):
71+
72+
layer = PLONE_APP_ANGULARJS_INTEGRATION_TESTING
73+
74+
def setUp(self):
75+
self.app = self.layer['app']
76+
self.portal = self.layer['portal']
77+
78+
def test_empty(self):
79+
self.portal.invokeFactory('Document', id='doc1', title='Doc 1')
80+
schema = [x[0] for x in get_object_schema(self.portal.doc1)]
81+
self.assertEqual(
82+
schema,
83+
[
84+
'title',
85+
'allow_discussion',
86+
'exclude_from_nav',
87+
'text',
88+
'relatedItems',
89+
'table_of_contents',
90+
'__dav_resource__',
91+
'title',
92+
'manage_options',
93+
'meta_type',
94+
'__name__',
95+
'__http_methods__',
96+
'isPrincipiaFolderish',
97+
'icon',
98+
'__dav_resource__',
99+
'title',
100+
'manage_options',
101+
'meta_type',
102+
'__name__',
103+
'__http_methods__',
104+
'isPrincipiaFolderish',
105+
'icon',
106+
'meta_type',
107+
'isPrincipiaFolderish',
108+
'manage_options',
109+
'__dav_resource__',
110+
'__http_methods__',
111+
'_properties',
112+
'title',
113+
'__name__',
114+
'__name__',
115+
'title',
116+
'allow_discussion',
117+
'exclude_from_nav',
118+
'rights',
119+
'contributors',
120+
'effective',
121+
'title',
122+
'expires',
123+
'language',
124+
'subjects',
125+
'creators',
126+
'description',
127+
'text',
128+
'relatedItems',
129+
'changeNote',
130+
'table_of_contents'
131+
]
132+
)
4133

5134

6135
class UnderscoreToCamelcaseUnitTest(unittest.TestCase):

src/plone/app/angularjs/utils.py

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,52 @@
11
# -*- coding: utf-8 -*-
2+
from zope.schema import getFields
3+
from zope.interface import providedBy
4+
from plone.behavior.interfaces import IBehaviorAssignable
5+
from plone.app.textfield import RichText
6+
from zope.schema import Datetime
7+
8+
9+
def serialize_to_json(obj):
10+
result = {}
11+
for title, schema_object in get_object_schema(obj):
12+
value = getattr(obj, title, None)
13+
no_underscore_method = not title.startswith('_')
14+
no_manage_method = not title.startswith('manage')
15+
if value and no_underscore_method and no_manage_method:
16+
# RichText
17+
if isinstance(schema_object, RichText):
18+
result[title] = value.output
19+
# DateTime
20+
elif isinstance(schema_object, Datetime):
21+
# XXX: Time string needs to be localized!
22+
result[title] = str(value())
23+
# Callables
24+
elif callable(schema_object):
25+
result[title] = value()
26+
# Tuple
27+
elif isinstance(value, tuple):
28+
result[title] = list(value)
29+
# String
30+
elif isinstance(value, str):
31+
result[title] = value
32+
# Unicode
33+
elif isinstance(value, unicode):
34+
result[title] = value
35+
else:
36+
result[title] = str(value)
37+
return result
38+
39+
40+
def get_object_schema(obj):
41+
for iface in providedBy(obj).flattened():
42+
for name, field in getFields(iface).items():
43+
yield name, field
44+
45+
assignable = IBehaviorAssignable(obj, None)
46+
if assignable:
47+
for behavior in assignable.enumerateBehaviors():
48+
for name, field in getFields(behavior.interface).items():
49+
yield name, field
250

351

452
def underscore_to_camelcase(underscore_string):

0 commit comments

Comments
 (0)