Commit 133d6655 authored by Vincent Pelletier's avatar Vincent Pelletier

ERP5Type.Core.Folder.Folder: Inherit from our classes first.

Allows Base to consistently override methods which are also overridden in
CMF where CMF implementation does not propagate the call to other
superclasses (ex: __before_publishing_traverse__ as of CMFCore-2.2.10).
Also allows simplifying several inheritance fixups.
Also, use super() instead of explicit resolution to Base.

Also: testBusinessTemplate: drop dead monkey-patch.
_getCopy is called during BusinessTemplate installation, but not the one
on this class. Which prevents reordering class inheritance between
SimpleItem and copy support classes.
So this is dead code which is not even explaining what it is trying to do
(ex: what document should it really not be called on ? what effect of this
method is undesired ? why is the method attached to this class ? why is
the cleanup restoring the original method, when this class does not have
this method to begin with but it merely gets it from its superclasses
which are re-inherited from by documents hence overriding it anyway ?,
and it is getting in the way of reordering class inheritance so it goes
away.

Also: ERP5Type.patches.CMFBTreeFolder: Fold patch onto ERP5Type.Core.Folder.
All ERP5UI-visible (hence needing to call allowedContentTypes) BTReeFolder2
instances should already inherit ERP5Type.Core.Folder, removing the need
for this monkey-patch.
parent be6f87b2
...@@ -34,7 +34,6 @@ from unittest import expectedFailure, skip ...@@ -34,7 +34,6 @@ from unittest import expectedFailure, skip
from AccessControl import getSecurityManager from AccessControl import getSecurityManager
from Products.ERP5Type.tests.ERP5TypeTestCase import ERP5TypeTestCase from Products.ERP5Type.tests.ERP5TypeTestCase import ERP5TypeTestCase
from Acquisition import aq_base from Acquisition import aq_base
from OFS.SimpleItem import SimpleItem
from App.config import getConfiguration from App.config import getConfiguration
from Products.ERP5Type.tests.Sequence import SequenceList, Sequence from Products.ERP5Type.tests.Sequence import SequenceList, Sequence
from urllib import pathname2url from urllib import pathname2url
...@@ -6302,11 +6301,9 @@ class TestBusinessTemplate(BusinessTemplateMixin): ...@@ -6302,11 +6301,9 @@ class TestBusinessTemplate(BusinessTemplateMixin):
if obj.id in object_id_list: if obj.id in object_id_list:
obj.int_index = marker_list.pop() obj.int_index = marker_list.pop()
return obj return obj
SimpleItem_getCopy = SimpleItem._getCopy
import Products.ERP5.tests import Products.ERP5.tests
try: try:
BaseTemplateItem.removeProperties = removeProperties BaseTemplateItem.removeProperties = removeProperties
SimpleItem._getCopy = lambda *args: self.fail()
template_tool = portal.portal_templates template_tool = portal.portal_templates
bt_path = os.path.join(os.path.dirname(Products.ERP5.tests.__file__), bt_path = os.path.join(os.path.dirname(Products.ERP5.tests.__file__),
'test_data', 'test_data',
...@@ -6335,7 +6332,6 @@ class TestBusinessTemplate(BusinessTemplateMixin): ...@@ -6335,7 +6332,6 @@ class TestBusinessTemplate(BusinessTemplateMixin):
self.tic() self.tic()
finally: finally:
BaseTemplateItem.removeProperties = BaseTemplateItem_removeProperties BaseTemplateItem.removeProperties = BaseTemplateItem_removeProperties
SimpleItem._getCopy = SimpleItem_getCopy
gc.enable() gc.enable()
# check the previously existing instance now behaves as the overriden class # check the previously existing instance now behaves as the overriden class
self.assertTrue(getattr(portal.another_file, 'isClassOverriden', False)) self.assertTrue(getattr(portal.another_file, 'isClassOverriden', False))
......
...@@ -706,11 +706,12 @@ def initializePortalTypeDynamicWorkflowMethods(ptype_klass, portal_workflow): ...@@ -706,11 +706,12 @@ def initializePortalTypeDynamicWorkflowMethods(ptype_klass, portal_workflow):
else: else:
method.registerTransitionAlways(portal_type, wf_id, tr_id) method.registerTransitionAlways(portal_type, wf_id, tr_id)
class Base( CopyContainer, class Base(
CopyContainer,
PropertyManager,
PortalContent, PortalContent,
ActiveObject, ActiveObject,
OFS.History.Historical, OFS.History.Historical,
PropertyManager,
PropertyTranslatableBuiltInDictMixIn, PropertyTranslatableBuiltInDictMixIn,
JSONRepresentableMixin, JSONRepresentableMixin,
): ):
...@@ -3516,7 +3517,11 @@ class Base( CopyContainer, ...@@ -3516,7 +3517,11 @@ class Base( CopyContainer,
return new_document return new_document
def _postCopy(self, container, op=0): def _postCopy(self, container, op=0):
super(Base, self)._postCopy(container, op=op) # Note: "super" cannot be used here, as this method gets stolen by
# ERP5Type.Core.Folder, which also inherits from CopyContainer before
# inheriting from ourselves, in which case "super" ends up calling the
# OFS.CopySupport method, which we do not want.
CopyContainer._postCopy(self, container, op)
if op == 0: # copy (not cut) if op == 0: # copy (not cut)
# We are the copy of another document (either cloned or copy/pasted), # We are the copy of another document (either cloned or copy/pasted),
# forget id generator state. # forget id generator state.
......
...@@ -36,6 +36,7 @@ from Acquisition import aq_base, aq_parent, aq_inner ...@@ -36,6 +36,7 @@ from Acquisition import aq_base, aq_parent, aq_inner
from BTrees.Length import Length from BTrees.Length import Length
from OFS.Folder import Folder as OFSFolder from OFS.Folder import Folder as OFSFolder
from OFS.ObjectManager import ObjectManager, checkValidId from OFS.ObjectManager import ObjectManager, checkValidId
from OFS.SimpleItem import Item
from zExceptions import BadRequest from zExceptions import BadRequest
from OFS.History import Historical from OFS.History import Historical
import ExtensionClass import ExtensionClass
...@@ -162,6 +163,42 @@ class FolderMixIn(ExtensionClass.Base): ...@@ -162,6 +163,42 @@ class FolderMixIn(ExtensionClass.Base):
security = ClassSecurityInfo() security = ClassSecurityInfo()
security.declareObjectProtected(Permissions.AccessContentsInformation) security.declareObjectProtected(Permissions.AccessContentsInformation)
security.declarePublic('allowedContentTypes')
def allowedContentTypes(self):
"""
List portal_types which can be added in this folder / object.
"""
portal_types = self.getPortalObject().portal_types
myType = portal_types.getTypeInfo(self)
if myType is not None:
if myType.filter_content_types:
# Iterate over allowed content types to avoid going over all portal
# types and retaining only a fraction.
result = []
for portal_type in myType.getTypeAllowedContentTypeList():
contentType = portal_types.getTypeInfo(portal_type)
if contentType is None:
raise AttributeError(
"Portal type '%s' does not exist and should not be allowed in '%s'" % (
portal_type,
self.getPortalType(),
),
)
result.append(contentType)
else:
result = [
contentType
for contentType in portal_types.listTypeInfo(self)
if myType.allowType(contentType.getId())
]
else:
result = portal_types.listTypeInfo()
return [
x
for x in result
if x.isConstructionAllowed(self)
]
security.declarePublic('newContent') security.declarePublic('newContent')
def newContent(self, id=None, portal_type=None, id_group=None, def newContent(self, id=None, portal_type=None, id_group=None,
default=None, method=None, container=None, temp_object=0, **kw): default=None, method=None, container=None, temp_object=0, **kw):
...@@ -635,7 +672,16 @@ _HANDLER_LIST = ( ...@@ -635,7 +672,16 @@ _HANDLER_LIST = (
# Bad value, accidentally put everywhere long ago # Bad value, accidentally put everywhere long ago
_BROKEN_BTREE_HANDLER = 'CMFBTreeFolderHandler' _BROKEN_BTREE_HANDLER = 'CMFBTreeFolderHandler'
class Folder(OFSFolder2, CMFBTreeFolder, CMFHBTreeFolder, Base, FolderMixIn): # Import order:
# - FolderMixIn can be about anywhere, let's put it first.
# - our CopyContainer before ObjectManager, as ObjectManager inherits from
# OFS.CopySupport.CopyContainer, which methods we do not want.
# - ObjectManager before Base, as Base is only a PropertyManager (no
# subobjects) and inheriting from ObjectManager overrides the necessary
# methods.
# - Base before generic container types, to allow customising their methods.
# - container types, because instances of Folder must be instances of these.
class Folder(FolderMixIn, CopyContainer, ObjectManager, Base, OFSFolder2, CMFBTreeFolder, CMFHBTreeFolder):
""" """
A Folder is a subclass of Base but not of XMLObject. A Folder is a subclass of Base but not of XMLObject.
Folders are not considered as documents and are therefore Folders are not considered as documents and are therefore
...@@ -685,18 +731,8 @@ class Folder(OFSFolder2, CMFBTreeFolder, CMFHBTreeFolder, Base, FolderMixIn): ...@@ -685,18 +731,8 @@ class Folder(OFSFolder2, CMFBTreeFolder, CMFHBTreeFolder, Base, FolderMixIn):
) )
# Class inheritance fixes # Class inheritance fixes
security.declareProtected( Permissions.ModifyPortalContent, 'edit' ) __repr__ = Item.__repr__
edit = Base.edit _postCopy = Base._postCopy
security.declareProtected( Permissions.ModifyPortalContent, '_edit' )
_edit = Base._edit
security.declareProtected( Permissions.ModifyPortalContent, 'setTitle' )
setTitle = Base.setTitle
security.declareProtected( Permissions.AccessContentsInformation, 'title_or_id' )
title_or_id = Base.title_or_id
security.declareProtected( Permissions.AccessContentsInformation, 'Title' )
Title = Base.Title
_setPropValue = Base._setPropValue
_propertyMap = Base._propertyMap # are there any others XXX ?
PUT_factory = None PUT_factory = None
# XXX Prevent inheritance from PortalFolderBase # XXX Prevent inheritance from PortalFolderBase
description = None description = None
...@@ -730,12 +766,6 @@ class Folder(OFSFolder2, CMFBTreeFolder, CMFHBTreeFolder, Base, FolderMixIn): ...@@ -730,12 +766,6 @@ class Folder(OFSFolder2, CMFBTreeFolder, CMFHBTreeFolder, Base, FolderMixIn):
del self.__dict__['_count'] del self.__dict__['_count']
self._p_changed = 1 self._p_changed = 1
security.declarePublic('newContent')
def newContent(self, *args, **kw):
""" Create a new content """
# Create data structure if none present
return FolderMixIn.newContent(self, *args, **kw)
def _getFolderHandlerData(self): def _getFolderHandlerData(self):
# Internal API working around bogus _folder_handler values. # Internal API working around bogus _folder_handler values.
# This method is a hot-spot for all Folder accesses. DO NOT SLOW IT DOWN. # This method is a hot-spot for all Folder accesses. DO NOT SLOW IT DOWN.
...@@ -1282,9 +1312,6 @@ class Folder(OFSFolder2, CMFBTreeFolder, CMFHBTreeFolder, Base, FolderMixIn): ...@@ -1282,9 +1312,6 @@ class Folder(OFSFolder2, CMFBTreeFolder, CMFHBTreeFolder, Base, FolderMixIn):
# Catalog related # Catalog related
security.declarePublic('reindexObject')
reindexObject = Base.reindexObject
security.declareProtected(Permissions.ModifyPortalContent, security.declareProtected(Permissions.ModifyPortalContent,
'reindexObjectSecurity') 'reindexObjectSecurity')
def reindexObjectSecurity(self, *args, **kw): def reindexObjectSecurity(self, *args, **kw):
...@@ -1396,11 +1423,14 @@ class Folder(OFSFolder2, CMFBTreeFolder, CMFHBTreeFolder, Base, FolderMixIn): ...@@ -1396,11 +1423,14 @@ class Folder(OFSFolder2, CMFBTreeFolder, CMFHBTreeFolder, Base, FolderMixIn):
spec=(), filter=None, portal_type=(), base=0, spec=(), filter=None, portal_type=(), base=0,
keep_default=None, checked_permission=None): keep_default=None, checked_permission=None):
if category == 'content': if category == 'content':
content_list = self.searchFolder(portal_type=spec) return [x.relative_url for x in self.searchFolder(portal_type=spec)]
return map(lambda x: x.relative_url, content_list) return super(Folder, self).getCategoryMembershipList(
else: category,
return Base.getCategoryMembershipList(self, category, spec=spec,
spec=spec, filter=filter, portal_type=portal_type, base=base) filter=filter,
portal_type=portal_type,
base=base,
)
security.declareProtected(Permissions.AccessContentsInformation, security.declareProtected(Permissions.AccessContentsInformation,
'checkConsistency') 'checkConsistency')
...@@ -1419,7 +1449,11 @@ class Folder(OFSFolder2, CMFBTreeFolder, CMFHBTreeFolder, Base, FolderMixIn): ...@@ -1419,7 +1449,11 @@ class Folder(OFSFolder2, CMFBTreeFolder, CMFHBTreeFolder, Base, FolderMixIn):
error_list += [ConsistencyMessage( error_list += [ConsistencyMessage(
self, self.getRelativeUrl(), 'BTree Inconsistency (fixed)')] self, self.getRelativeUrl(), 'BTree Inconsistency (fixed)')]
# Call superclass # Call superclass
error_list += Base.checkConsistency(self, fixit=fixit, filter=filter, **kw) error_list += super(Folder, self).checkConsistency(
fixit=fixit,
filter=filter,
**kw
)
# We must commit before listing folder contents # We must commit before listing folder contents
# in case we erased some data # in case we erased some data
if fixit: if fixit:
...@@ -1469,13 +1503,6 @@ class Folder(OFSFolder2, CMFBTreeFolder, CMFHBTreeFolder, Base, FolderMixIn): ...@@ -1469,13 +1503,6 @@ class Folder(OFSFolder2, CMFBTreeFolder, CMFHBTreeFolder, Base, FolderMixIn):
return [ ti.id for ti in self.allowedContentTypes() return [ ti.id for ti in self.allowedContentTypes()
if ti.id not in hidden_type_list ] if ti.id not in hidden_type_list ]
# Multiple Inheritance Priority Resolution
_setProperty = Base._setProperty
setProperty = Base.setProperty
getProperty = Base.getProperty
hasProperty = Base.hasProperty
view = Base.view
# Aliases # Aliases
security.declareProtected(Permissions.AccessContentsInformation, security.declareProtected(Permissions.AccessContentsInformation,
'getObjectIds') 'getObjectIds')
...@@ -1562,7 +1589,7 @@ class Folder(OFSFolder2, CMFBTreeFolder, CMFHBTreeFolder, Base, FolderMixIn): ...@@ -1562,7 +1589,7 @@ class Folder(OFSFolder2, CMFBTreeFolder, CMFHBTreeFolder, Base, FolderMixIn):
Make document behave as a template. Make document behave as a template.
A template is no longer indexable A template is no longer indexable
""" """
Base.makeTemplate(self) super(Folder, self).makeTemplate()
for o in self.objectValues(): for o in self.objectValues():
if getattr(aq_base(o), 'makeTemplate', None) is not None: if getattr(aq_base(o), 'makeTemplate', None) is not None:
o.makeTemplate() o.makeTemplate()
...@@ -1573,7 +1600,7 @@ class Folder(OFSFolder2, CMFBTreeFolder, CMFHBTreeFolder, Base, FolderMixIn): ...@@ -1573,7 +1600,7 @@ class Folder(OFSFolder2, CMFBTreeFolder, CMFHBTreeFolder, Base, FolderMixIn):
""" """
Make document behave as standard document (indexable) Make document behave as standard document (indexable)
""" """
Base.makeTemplateInstance(self) super(Folder, self).makeTemplateInstance()
for o in self.objectValues(): for o in self.objectValues():
if getattr(aq_base(o), 'makeTemplateInstance', None) is not None: if getattr(aq_base(o), 'makeTemplateInstance', None) is not None:
o.makeTemplateInstance() o.makeTemplateInstance()
...@@ -1695,19 +1722,3 @@ for source_klass, destination_klass in \ ...@@ -1695,19 +1722,3 @@ for source_klass, destination_klass in \
# Zope 2.7 required to have methodId__roles__ defined # Zope 2.7 required to have methodId__roles__ defined
# to know the security ot the method # to know the security ot the method
setattr(destination_klass, method_id+'__roles__', None) setattr(destination_klass, method_id+'__roles__', None)
# Some of Folder base inherits indirectly from a different CopyContainer which
# lacks our customisations.
# Resolve all inheritence conflicts between CopyContainer (which Folder
# inherits from via Base) and those bases in favour of the property
# from Base (so it may override CopyContainer).
for CopyContainer_property_id in CopyContainer.__dict__:
if CopyContainer_property_id.startswith('__') or CopyContainer_property_id in Folder.__dict__:
continue
try:
Base_property = getattr(Base, CopyContainer_property_id)
except AttributeError:
continue
if isinstance(Base_property, ClassSecurityInfo):
continue
setattr(Folder, CopyContainer_property_id, Base_property)
...@@ -51,7 +51,6 @@ from Products.ERP5Type.patches import CookieCrumbler ...@@ -51,7 +51,6 @@ from Products.ERP5Type.patches import CookieCrumbler
from Products.ERP5Type.patches import PropertySheets from Products.ERP5Type.patches import PropertySheets
from Products.ERP5Type.patches import CMFCoreSkinnable from Products.ERP5Type.patches import CMFCoreSkinnable
from Products.ERP5Type.patches import CMFCoreSkinsTool from Products.ERP5Type.patches import CMFCoreSkinsTool
from Products.ERP5Type.patches import CMFBTreeFolder
from Products.ERP5Type.patches import OFSFile from Products.ERP5Type.patches import OFSFile
from Products.ERP5Type.patches import OFSFolder from Products.ERP5Type.patches import OFSFolder
from Products.ERP5Type.patches import OFSUninstalled from Products.ERP5Type.patches import OFSUninstalled
......
##############################################################################
#
# Copyright (c) 2001 Zope Corporation and Contributors. All Rights Reserved.
# Copyright (c) 2006 Nexedi SARL and Contributors. All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE.
#
##############################################################################
#from Products.CMFCore.PortalFolder import PortalFolder
try:
from Products.CMFCore.CMFBTreeFolder import CMFBTreeFolder
except ImportError:
from Products.BTreeFolder2.CMFBTreeFolder import CMFBTreeFolder
"""
This patch tries to give only portal types that are defined
in the allowed content types field. This will make the
speed of allowed content type in almost O(1) instead of O(n),
where n is the number of portal types in types tool.
"""
def CMFBTreeFolder_allowedContentTypes(self):
"""
List type info objects for types which can be added in
this folder.
"""
result = []
portal_types = self.getPortalObject().portal_types
myType = portal_types.getTypeInfo(self)
if myType is not None:
allowed_types_to_check = []
if myType.filter_content_types:
for portal_type in myType.getTypeAllowedContentTypeList():
contentType = portal_types.getTypeInfo(portal_type)
if contentType is None:
raise AttributeError, "Portal type '%s' does not exist " \
"and should not be allowed in '%s'" % \
(portal_type, self.getPortalType())
result.append(contentType)
else:
for contentType in portal_types.listTypeInfo(self):
if myType.allowType(contentType.getId()):
result.append(contentType)
else:
result = portal_types.listTypeInfo()
return filter(
lambda typ, container=self: typ.isConstructionAllowed(container),
result)
CMFBTreeFolder.allowedContentTypes = CMFBTreeFolder_allowedContentTypes
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment