Check FTI when pasting content.
Conflicts: docs/HISTORY.txt plone/dexterity/content.py plone/dexterity/tests/test_content.py
This commit is contained in:
parent
681510ab6e
commit
56879c86bf
|
@ -4,7 +4,11 @@ Changelog
|
|||
2.1.1 (unreleased)
|
||||
------------------
|
||||
|
||||
- Fixed schema caching. Previously, a non-persistent counter would be
|
||||
* When pasting into a dexterity container check the FTI for the the pasted
|
||||
object to see if it is allowed in the new container.
|
||||
[wichert]
|
||||
|
||||
* Fixed schema caching. Previously, a non-persistent counter would be
|
||||
used as part of the cache key, and changes made to this counter in
|
||||
one process would obviously not propagate to other processes.
|
||||
|
||||
|
@ -26,22 +30,22 @@ Changelog
|
|||
expected effect.
|
||||
[gaudenzius]
|
||||
|
||||
- The default attribute accessor now also looks through subtypes
|
||||
* The default attribute accessor now also looks through subtypes
|
||||
(behaviors) to find a field default.
|
||||
[malthe]
|
||||
|
||||
- Added support in the FTI to look up behaviors by utility name when
|
||||
* Added support in the FTI to look up behaviors by utility name when
|
||||
getting additional schemata (i.e. fields provided by behaviors).
|
||||
|
||||
This functionality makes it possible to create a behavior where the
|
||||
interface is dynamically generated.
|
||||
[malthe]
|
||||
|
||||
- Return early for attributes that begin with two underscores.
|
||||
* Return early for attributes that begin with two underscores.
|
||||
https://github.com/plone/plone.dexterity/pull/11
|
||||
[malthe]
|
||||
|
||||
- Make it possible to define a SchemaPolicy for the FTI
|
||||
* Make it possible to define a SchemaPolicy for the FTI
|
||||
[Frédéric Péters]
|
||||
[gbastien]
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
from Acquisition import Explicit, aq_parent
|
||||
from Acquisition import Explicit, aq_base, aq_parent
|
||||
from zExceptions import Unauthorized
|
||||
|
||||
from copy import deepcopy
|
||||
|
@ -32,6 +32,7 @@ from Products.CMFCore.PortalContent import PortalContent
|
|||
from Products.CMFCore.PortalFolder import PortalFolderBase
|
||||
from Products.CMFCore.CMFCatalogAware import CMFCatalogAware
|
||||
from Products.CMFPlone.interfaces import IConstrainTypes
|
||||
from Products.CMFCore.interfaces import ITypeInformation
|
||||
|
||||
from Products.CMFDefault.DublinCore import DefaultDublinCoreImpl
|
||||
from Products.CMFDefault.utils import tuplize
|
||||
|
@ -152,6 +153,22 @@ class AttributeValidator(Explicit):
|
|||
return None
|
||||
|
||||
|
||||
class PasteBehaviourMixin(object):
|
||||
def _verifyObjectPaste(self, obj, validate_src=True):
|
||||
# Extend the paste checks from OFS.CopySupport.CopyContainer
|
||||
# (permission checks) and
|
||||
# Products.CMFCore.PortalFolder.PortalFolderBase (permission checks and
|
||||
# allowed content types) to also ask the FTI if construction is
|
||||
# allowed.
|
||||
super(PasteBehaviourMixin, self)._verifyObjectPaste(obj, validate_src)
|
||||
if validate_src:
|
||||
portal_type = getattr(aq_base(obj), 'portal_type', None)
|
||||
if portal_type:
|
||||
fti = queryUtility(ITypeInformation, name=portal_type)
|
||||
if fti is not None and not fti.isConstructionAllowed(self):
|
||||
raise ValueError('You can not add the copied content here.')
|
||||
|
||||
|
||||
class DexterityContent(DAVResourceMixin, PortalContent, DefaultDublinCoreImpl, Contained):
|
||||
"""Base class for Dexterity content
|
||||
"""
|
||||
|
@ -246,7 +263,8 @@ class DexterityContent(DAVResourceMixin, PortalContent, DefaultDublinCoreImpl, C
|
|||
s.append(part)
|
||||
return tuple(s)
|
||||
|
||||
class Item(BrowserDefaultMixin, DexterityContent):
|
||||
|
||||
class Item(PasteBehaviourMixin, BrowserDefaultMixin, DexterityContent):
|
||||
"""A non-containerish, CMFish item
|
||||
"""
|
||||
|
||||
|
@ -275,7 +293,7 @@ class Item(BrowserDefaultMixin, DexterityContent):
|
|||
__getattr__ = DexterityContent.__getattr__
|
||||
|
||||
|
||||
class Container(DAVCollectionMixin, BrowserDefaultMixin, CMFCatalogAware, CMFOrderedBTreeFolderBase, DexterityContent):
|
||||
class Container(PasteBehaviourMixin, DAVCollectionMixin, BrowserDefaultMixin, CMFCatalogAware, CMFOrderedBTreeFolderBase, DexterityContent):
|
||||
"""Base class for folderish items
|
||||
"""
|
||||
|
||||
|
|
|
@ -705,6 +705,68 @@ class TestContent(MockTestCase):
|
|||
self.assertEquals(folder.allowedContentTypes(), [fti_mock])
|
||||
self.assertRaises(ValueError, folder.invokeFactory, u"disallowed_type", id="test")
|
||||
|
||||
def test_verifyObjectPaste_paste_without_portal_type(self):
|
||||
original_container = Container(id='parent')
|
||||
original_container.manage_permission('View', ('Anonymous',))
|
||||
content = Item(id='test')
|
||||
content.__factory_meta_type__ = 'document'
|
||||
container = Container(id='container')
|
||||
container.all_meta_types = [{'name': 'document',
|
||||
'action': None,
|
||||
'permission': 'View'}]
|
||||
container.manage_permission('View', ('Anonymous',))
|
||||
container['test'] = content
|
||||
content = container['test']
|
||||
container._verifyObjectPaste(content, True)
|
||||
|
||||
def test_verifyObjectPaste_fti_does_not_allow_content(self):
|
||||
from Products.CMFCore.interfaces import ITypeInformation
|
||||
original_container = Container(id='parent')
|
||||
original_container.manage_permission('View', ('Anonymous',))
|
||||
content = Item(id='test')
|
||||
content.__factory_meta_type__ = 'document'
|
||||
content.portal_type = 'document'
|
||||
container = Container(id='container')
|
||||
container.all_meta_types = [{'name': 'document',
|
||||
'action': None,
|
||||
'permission': 'View'}]
|
||||
container.manage_permission('View', ('Anonymous',))
|
||||
container['test'] = content
|
||||
content = container['test']
|
||||
fti = self.mocker.mock()
|
||||
self.expect(fti.isConstructionAllowed(container)).result(False)
|
||||
self.mock_utility(fti, ITypeInformation, name='document')
|
||||
pt = self.mocker.mock()
|
||||
self.expect(pt.getTypeInfo('document')).result(None)
|
||||
self.expect(pt.getTypeInfo(container)).result(None)
|
||||
self.mock_tool(pt, 'portal_types')
|
||||
self.replay()
|
||||
self.assertRaises(ValueError, container._verifyObjectPaste, content, True)
|
||||
|
||||
def test_verifyObjectPaste_fti_does_allow_content(self):
|
||||
from Products.CMFCore.interfaces import ITypeInformation
|
||||
original_container = Container(id='parent')
|
||||
original_container.manage_permission('View', ('Anonymous',))
|
||||
content = Item(id='test')
|
||||
content.__factory_meta_type__ = 'document'
|
||||
content.portal_type = 'document'
|
||||
container = Container(id='container')
|
||||
container.all_meta_types = [{'name': 'document',
|
||||
'action': None,
|
||||
'permission': 'View'}]
|
||||
container.manage_permission('View', ('Anonymous',))
|
||||
container['test'] = content
|
||||
content = container['test']
|
||||
fti = self.mocker.mock()
|
||||
self.expect(fti.isConstructionAllowed(container)).result(True)
|
||||
self.mock_utility(fti, ITypeInformation, name='document')
|
||||
pt = self.mocker.mock()
|
||||
self.expect(pt.getTypeInfo('document')).result(None)
|
||||
self.expect(pt.getTypeInfo(container)).result(None)
|
||||
self.mock_tool(pt, 'portal_types')
|
||||
self.replay()
|
||||
container._verifyObjectPaste(content, True)
|
||||
|
||||
|
||||
def test_suite():
|
||||
return unittest.defaultTestLoader.loadTestsFromName(__name__)
|
||||
|
|
Reference in New Issue