Skip to content

Commit

Permalink
Add ._pasteObjects() to be able to paste objects without limits.
Browse files Browse the repository at this point in the history
Fixes #217.
  • Loading branch information
Michael Howitz committed Apr 19, 2018
1 parent b064a18 commit 6858502
Show file tree
Hide file tree
Showing 3 changed files with 57 additions and 19 deletions.
4 changes: 3 additions & 1 deletion doc/CHANGES.rst
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,9 @@ http://docs.zope.org/zope2/
2.13.28 (unreleased)
--------------------

- ...
- Add ``OFS.CopySupport.CopyContainer._pasteObjects()`` to be able to paste
objects no matter how many objects where cut or copied.
(`#217 <https://github.com/zopefoundation/Zope/issues/217>`_)


2.13.27 (2018-01-27)
Expand Down
67 changes: 49 additions & 18 deletions src/OFS/CopySupport.py
Original file line number Diff line number Diff line change
Expand Up @@ -169,28 +169,29 @@ def _get_id(self, id):
id='copy%s_of_%s' % (n and n+1 or '', orig_id)
n=n+1

security.declareProtected(view_management_screens, 'manage_pasteObjects')
def manage_pasteObjects(self, cb_copy_data=None, REQUEST=None):
def _pasteObjects(self, cp, cb_maxsize=0):
"""Paste previously copied objects into the current object.
If calling manage_pasteObjects from python code, pass the result of a
``cp`` is the list of objects for paste as encoded by ``_cb_encode``.
If calling _pasteObjects from python code, pass the result of a
previous call to manage_cutObjects or manage_copyObjects as the first
argument.
Also sends IObjectCopiedEvent and IObjectClonedEvent
``cb_maxsize`` is the maximum size of the JSON representation of the
object list. Set it to a non-zero value to prevent DoS attacks with
huge object lists or zlib bombs.
This method sends IObjectCopiedEvent and IObjectClonedEvent
or IObjectWillBeMovedEvent and IObjectMovedEvent.
Returns tuple of (operator, list of {'id': orig_id, 'new_id': new_id}).
Where `operator` is 0 for a copy operation and 1 for a move operation.
"""
if cb_copy_data is not None:
cp = cb_copy_data
elif REQUEST is not None and REQUEST.has_key('__cp'):
cp = REQUEST['__cp']
else:
cp = None
if cp is None:
raise CopyError(eNoData)

try:
op, mdatas = _cb_decode(cp)
op, mdatas = _cb_decode(cp, cb_maxsize)
except:
raise CopyError(eInvalid)

Expand Down Expand Up @@ -243,10 +244,6 @@ def manage_pasteObjects(self, cb_copy_data=None, REQUEST=None):

notify(ObjectClonedEvent(ob))

if REQUEST is not None:
return self.manage_main(self, REQUEST, update_menu=1,
cb_dataValid=1)

elif op == 1:
# Move operation
for ob in oblist:
Expand Down Expand Up @@ -311,13 +308,40 @@ def manage_pasteObjects(self, cb_copy_data=None, REQUEST=None):
# try to make ownership implicit if possible
ob.manage_changeOwnershipType(explicit=0)

if REQUEST is not None:
return op, result

security.declareProtected(view_management_screens, 'manage_pasteObjects')
def manage_pasteObjects(self, cb_copy_data=None, REQUEST=None):
"""Paste previously copied objects into the current object.
If calling manage_pasteObjects from python code, pass the result of a
previous call to manage_cutObjects or manage_copyObjects as the first
argument.
Also sends IObjectCopiedEvent and IObjectClonedEvent
or IObjectWillBeMovedEvent and IObjectMovedEvent.
If `REQUEST` is None it returns a
list of dicts {'id': orig_id, 'new_id': new_id} otherwise it renders
a HTML page.
"""
if (cb_copy_data is None and
REQUEST is not None and
REQUEST.has_key('__cp')):
cb_copy_data = REQUEST['__cp']
op, result = self._pasteObjects(cb_copy_data, cb_maxsize=8192)

if REQUEST is not None:
if op == 0:
cb_valid = 1
elif op == 1:
REQUEST['RESPONSE'].setCookie('__cp', 'deleted',
path='%s' % cookie_path(REQUEST),
expires='Wed, 31-Dec-97 23:59:59 GMT')
REQUEST['__cp'] = None
return self.manage_main(self, REQUEST, update_menu=1,
cb_dataValid=0)
cb_valid = 0
return self.manage_main(self, REQUEST, update_menu=1,
cb_dataValid=cb_valid)

return result

Expand Down Expand Up @@ -684,6 +708,13 @@ def _cb_encode(d):
return quote(compress(dumps(d), 9))

def _cb_decode(s, maxsize=8192):
"""Decode a list of IDs from storage in a cookie.
``s`` is text as encoded by ``_cb_encode``.
``maxsize`` is the maximum size of uncompressed data. ``0`` means no limit.
Return a list of text IDs.
"""
dec = decompressobj()
data = dec.decompress(unquote(s), maxsize)
if dec.unconsumed_tail:
Expand Down
5 changes: 5 additions & 0 deletions src/OFS/tests/testCopySupport.py
Original file line number Diff line number Diff line change
Expand Up @@ -307,6 +307,11 @@ def make_data(lenght):
self.folder1.manage_pasteObjects(make_data(300))
self.assertTrue('Item Not Found' in str(err.exception))

# _pasteObjects allows to paste without restriction:
with self.assertRaises(CopyError) as err:
self.folder1._pasteObjects(make_data(3500))
self.assertTrue('Item Not Found' in str(err.exception))


class _SensitiveSecurityPolicy:

Expand Down

0 comments on commit 6858502

Please sign in to comment.