Check FTI when pasting content.

Conflicts:
	docs/HISTORY.txt
	plone/dexterity/content.py
	plone/dexterity/tests/test_content.py
This commit is contained in:
Wichert Akkerman 2013-01-11 12:32:01 +01:00 committed by David Glick
parent 681510ab6e
commit 56879c86bf
3 changed files with 92 additions and 8 deletions

View File

@ -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]

View File

@ -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
"""

View File

@ -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__)