Skip to content

Commit

Permalink
Merge branch 'master' into plip10359-security-controlpanel
Browse files Browse the repository at this point in the history
Conflicts:
	CHANGES.rst
  • Loading branch information
Timo Stollenwerk committed Feb 27, 2015
2 parents 67e3724 + 30d66ce commit 321bd07
Show file tree
Hide file tree
Showing 6 changed files with 170 additions and 4 deletions.
3 changes: 3 additions & 0 deletions CHANGES.rst
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@ Changelog
properties (see https://github.com/plone/Products.CMFPlone/issues/216).
[jcerjak]

- Support for import and export of collections using FTP, DAV and GenericSetup
[matthewwilkes]


1.1.2 (2014-10-23)
------------------
Expand Down
7 changes: 5 additions & 2 deletions plone/app/collection/collection.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
from OFS.ObjectManager import ObjectManager
from plone.app.collection.field import QueryField
from plone.app.contentlisting.interfaces import IContentListing
from plone.app.widgets.at import QueryStringWidget
from Products.Archetypes.Widget import QueryStringWidget
from Products.ATContentTypes.content import document, schemata
from Products.Archetypes import atapi
from Products.Archetypes.atapi import (BooleanField,
Expand All @@ -20,7 +20,7 @@
from plone.app.collection import PloneMessageFactory as _
from plone.app.collection.config import ATCT_TOOLNAME, PROJECTNAME
from plone.app.collection.interfaces import ICollection

from plone.app.collection.marshaller import CollectionRFC822Marshaller

CollectionSchema = document.ATDocumentSchema.copy() + atapi.Schema((

Expand Down Expand Up @@ -86,6 +86,9 @@
),
))

# Use the extended marshaller that understands queries
CollectionSchema.registerLayer("marshall", CollectionRFC822Marshaller())

CollectionSchema.moveField('query', after='description')
if 'presentation' in CollectionSchema:
CollectionSchema['presentation'].widget.visible = False
Expand Down
2 changes: 1 addition & 1 deletion plone/app/collection/configure.zcml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
name="default"
title="plone.app.collection"
directory="profiles/default"
description="Adds support for new style collections to Plone"
description="Archetypes-based collections"
provides="Products.GenericSetup.interfaces.EXTENSION"
/>

Expand Down
68 changes: 68 additions & 0 deletions plone/app/collection/marshaller.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
from types import ListType, TupleType

from zope.contenttype import guess_content_type

from AccessControl import ClassSecurityInfo
from App.class_init import InitializeClass
from Products.Archetypes.interfaces.base import IBaseUnit
from Products.Archetypes.utils import mapply
from Products.Archetypes.Marshall import RFC822Marshaller, parseRFC822, formatRFC822Headers


class CollectionRFC822Marshaller(RFC822Marshaller):

security = ClassSecurityInfo()
security.declareObjectPrivate()
security.setDefaultAccess('deny')

def demarshall(self, instance, data, **kwargs):
# We don't want to pass file forward.
if 'file' in kwargs:
if not data:
# TODO Yuck! Shouldn't read the whole file, never.
# OTOH, if you care about large files, you should be
# using the PrimaryFieldMarshaller or something
# similar.
data = kwargs['file'].read()
del kwargs['file']
headers, body = parseRFC822(data)

query = {}
for k, v in headers.items():
if not k.startswith("query"):
continue
else:
index = int(k[5])
sub_key = k.split("_")[1]
query_part = query.get(index, {})
query_part[sub_key] = v
query[index] = query_part
del headers[k]
query = [facet[1] for facet in sorted(query.items())]

header = formatRFC822Headers(headers.items())
data = '%s\n\n%s' % (header, body)

try:
return RFC822Marshaller.demarshall(self, instance, data, **kwargs)
finally:
instance.query = query

def marshall(self, instance, **kwargs):
content_type, length, data = RFC822Marshaller.marshall(self, instance, **kwargs)
headers, body = parseRFC822(data)

headers = headers.items()
for i, query in enumerate(instance.query):
for key, value in query.items():
if isinstance(value, list):
value = "\n".join(value)
header_key = 'query%d_%s' % (i, key)
headers.append((header_key, value))

header = formatRFC822Headers(headers)
data = '%s\n\n%s' % (header, body)
length = len(data)
return (content_type, length, data)

InitializeClass(CollectionRFC822Marshaller)
92 changes: 92 additions & 0 deletions plone/app/collection/tests/test_collection.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
from plone.app.testing import setRoles
from plone.testing.z2 import Browser
from transaction import commit
from Products.Archetypes.Marshall import parseRFC822

import unittest2 as unittest

Expand Down Expand Up @@ -235,3 +236,94 @@ def test_selectedViewFields(self):
def test_syndication_enabled_by_default(self):
syn = getToolByName(self.portal, 'portal_syndication')
self.assertTrue(syn.isSyndicationAllowed(self.collection))


class TestMarshalling(unittest.TestCase):

layer = PLONEAPPCOLLECTION_INTEGRATION_TESTING

def test_simple_query_included_in_marshall_results(self):
portal = self.layer['portal']
login(portal, 'admin')
query = [{
'i': 'portal_type',
'o': 'plone.app.querystring.operation.string.is',
'v': 'News Item',
}]
portal.invokeFactory("Collection",
"collection",
query=query,
title="New Collection")
collection = portal['collection']
rfc822 = collection.manage_FTPget()
data = parseRFC822(rfc822)
self.assertIn('query0_i', data[0])
self.assertIn('query0_o', data[0])
self.assertIn('query0_v', data[0])

self.assertEqual(data[0]['query0_i'], query[0]['i'])
self.assertEqual(data[0]['query0_o'], query[0]['o'])
self.assertEqual(data[0]['query0_v'], query[0]['v'])

def test_multiple_query_items_included_in_marshall_results(self):
portal = self.layer['portal']
login(portal, 'admin')
query = [{
'i': 'portal_type',
'o': 'plone.app.querystring.operation.string.is',
'v': 'News Item',
},{ 'i': 'Title',
'o': 'plone.app.querystring.operation.string.is',
'v': 'Test News Item',
}]

portal.invokeFactory("Collection",
"collection",
query=query,
title="New Collection")
collection = portal['collection']
rfc822 = collection.manage_FTPget()
data = parseRFC822(rfc822)

self.assertIn('query0_i', data[0])
self.assertIn('query0_o', data[0])
self.assertIn('query0_v', data[0])
self.assertIn('query1_i', data[0])
self.assertIn('query1_o', data[0])
self.assertIn('query1_v', data[0])

self.assertEqual(data[0]['query0_i'], query[0]['i'])
self.assertEqual(data[0]['query0_o'], query[0]['o'])
self.assertEqual(data[0]['query0_v'], query[0]['v'])
self.assertEqual(data[0]['query1_i'], query[1]['i'])
self.assertEqual(data[0]['query1_o'], query[1]['o'])
self.assertEqual(data[0]['query1_v'], query[1]['v'])

def test_query_gets_set_on_PUT(self):
portal = self.layer['portal']
login(portal, 'admin')
query = [{
'i': 'portal_type',
'o': 'plone.app.querystring.operation.string.is',
'v': 'News Item',
}]

expected_query = [{
'i': 'portal_type',
'o': 'plone.app.querystring.operation.string.is',
'v': 'LOREM IPSUM DOLOR',
}]

portal.invokeFactory("Collection",
"collection",
query=query,
title="New Collection")
collection = portal['collection']
rfc822 = collection.manage_FTPget()
# Modify the response to put in a sentinal, to check it's been updated
rfc822 = rfc822.replace(query[0]['v'], expected_query[0]['v'])

portal.REQUEST.set("BODY", rfc822)
collection.PUT(portal.REQUEST, None)
self.assertEqual(collection.query, expected_query)

2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@
'plone.app.widgets',
'plone.portlet.collection',
'plone.portlets',
'Products.Archetypes',
'Products.Archetypes>=1.10.4.dev0',
'Products.CMFCore',
'Products.CMFPlone',
'Products.CMFQuickInstallerTool',
Expand Down

0 comments on commit 321bd07

Please sign in to comment.