from zope.component import getUtility from zope.component import queryUtility from zope.component.interfaces import IFactory from AccessControl import getSecurityManager from AccessControl.PermissionRole import rolesForPermissionOn from AccessControl.ZopeSecurityPolicy import getRoles from Acquisition import aq_parent from Acquisition import aq_base from Acquisition import aq_inner import OFS.subscribers from Products.Archetypes.utils import shasattr from Products.CMFCore.utils import getToolByName from Products.CMFCore.interfaces import ITypeInformation from Products.CMFPlone.FactoryTool import TempFolder, FactoryTool, FACTORY_INFO from Products.CMFPlone.utils import base_hasattr from ZODB.POSException import ConflictError methods = ('__ac_local_roles__','get_valid_userids','valid_roles','userdefined_roles','owner_info','allowedContentTypes') _marker = [] def cached(func): def new_func(self): d = '_data_%s'%func.__name__ data = getattr(self, d, _marker) if data is not _marker: return data res = func(self) setattr(self, d, res) return res return new_func for m in methods: fn = getattr(TempFolder, m) setattr(TempFolder, m, cached(fn)) def _createObjectByType(type_name, container, id, *args, **kw): """This function replaces Products.CMFPlone.utils._createObjectByType. If no product is set on fti, use IFactory to lookup the factory. Additionally we add 'container' as 'parent' kw argument when calling the IFactory implementation. this ensures the availability of the acquisition chain if needed inside the construction logic. The kw argument hack is some kind of semi-valid since the IFactory interface promises the __call__ function to accept all given args and kw args. As long as the specific IFactory implementation provides this signature everything works well unless any other 3rd party factory expects another kind of object as 'parent' kw arg than the provided one. """ id = str(id) typesTool = getToolByName(container, 'portal_types') fti = typesTool.getTypeInfo(type_name) if not fti: raise ValueError, 'Invalid type %s' % type_name if not fti.product: m = queryUtility(IFactory, fti.factory, None) if m is None: raise ValueError, ('Product factory for %s was invalid' % fti.getId()) kw['parent'] = container ob = m(id, *args, **kw) # its not set by factory. container[id] = ob else: p = container.manage_addProduct[fti.product] m = getattr(p, fti.factory, None) if m is None: raise ValueError, ('Product factory for %s was invalid' % fti.getId()) # construct the object m(id, *args, **kw) ob = container._getOb( id ) return fti._finishConstruction(ob) # Try making a FauxArchetypeTool that intercepts class FauxArchetypeTool(object): __allow_access_to_unprotected_subobjects__ = 1 def __init__(self, tool): self.tool = tool def getCatalogsByType(self,type_name): return [] def __getattr__(self, id): return getattr(self.tool, id) def __getitem__(self, id): # Zope's inner acquisition chain for objects returned by __getitem__ will be # portal -> portal_factory -> temporary_folder -> object # What we really want is for the inner acquisition chain to be # intended_parent_folder -> portal_factory -> temporary_folder -> object # So we need to rewrap... portal_factory = aq_parent(self) intended_parent = aq_parent(portal_factory) # If the intended parent has an object with the given id, just do a passthrough if hasattr(intended_parent, id): return getattr(intended_parent, id) # rewrap portal_factory portal_factory = aq_base(portal_factory).__of__(intended_parent) # rewrap self temp_folder = aq_base(self).__of__(portal_factory) if id in self.objectIds(): return (aq_base(self._getOb(id)).__of__(temp_folder)).__of__(intended_parent) else: type_name = self.getId() try: self.archetype_tool = FauxArchetypeTool(getToolByName(self, 'archetype_tool')) _createObjectByType(type_name, self, id) #self.invokeFactory(id=id, type_name=type_name) except ConflictError: raise except: # some errors from invokeFactory (AttributeError, maybe others) # get swallowed -- dump the exception to the log to make sure # developers can see what's going on getToolByName(self, 'plone_utils').logException() raise obj = self._getOb(id) # keep obj out of the catalog obj.unindexObject() # additionally keep it out of Archetypes UID and refs catalogs if base_hasattr(obj, '_uncatalogUID'): obj._uncatalogUID(obj) if base_hasattr(obj, '_uncatalogRefs'): obj._uncatalogRefs(obj) return (aq_base(obj).__of__(temp_folder)).__of__(intended_parent) TempFolder.__getitem__ = __getitem__ def _setObject(self, id, object, roles=None, user=None, set_owner=1, suppress_events=True): """Set an object into this container. Also sends IObjectWillBeAddedEvent and IObjectAddedEvent. """ ob = object # better name, keep original function signature v = self._checkId(id) if v is not None: id = v t = getattr(ob, 'meta_type', None) # If an object by the given id already exists, remove it. for object_info in self._objects: if object_info['id'] == id: self._delObject(id) break self._objects = self._objects + ({'id': id, 'meta_type': t},) self._setOb(id, ob) ob = self._getOb(id) if set_owner: # TODO: eventify manage_fixupOwnershipAfterAdd # This will be called for a copy/clone, or a normal _setObject. ob.manage_fixupOwnershipAfterAdd() # Try to give user the local role "Owner", but only if # no local roles have been set on the object yet. if getattr(ob, '__ac_local_roles__', _marker) is None: user = getSecurityManager().getUser() if user is not None: userid = user.getId() if userid is not None: ob.manage_setLocalRoles(userid, ['Owner']) OFS.subscribers.compatibilityCall('manage_afterAdd', ob, ob, self) return id TempFolder._setObject = _setObject # Override listTypeInfo def listTypeInfo( self, container=None ): """ Return a sequence of instances which implement the TypeInformation interface, one for each content type registered in the portal. """ request = getattr(self, 'REQUEST', None) verify = self.objectIds() key = 'listTypeInfo' if container is None: key = '%s-None'%key elif not shasattr(aq_base(container), 'UID'): key = '%s-%s' % (key, container.getId()) else: key = '%s-%s' % (key, container.UID()) verify_key = "verify-%s" % key if request is not None and getattr(request, verify_key, None)==key: tmp = request.get(key, None) if tmp is not None: return tmp typelist = [] for t in self.objectValues(): # Filter out things that aren't TypeInformation and # types for which the user does not have adequate permission. if not ITypeInformation.providedBy(t): continue elif not t.getId(): # XXX What's this used for ? # Not ready. continue typelist.append(t) # Check permissions if container is None: res = typelist if request is not None: request.set(key, res) return res if not typelist: res = [] if request is not None: request.set(key, []) request.set(verify_key, verify) return res addcontext = container portal_membership = getToolByName(self, 'portal_membership') member = portal_membership.getAuthenticatedMember() rolesInContext = dict.fromkeys(member.getRolesInContext(addcontext)) if not rolesInContext: res = [] if request is not None: request.set(key, []) return res dispatcher = getattr(addcontext, 'manage_addProduct', None) if dispatcher is None: res = [] if request is not None: request.set(key, []) return res result = [] # XXX need to handle constraintypes as well all_meta = None for ti in typelist: if not ti.factory: continue if not ti.product: m = queryUtility(IFactory, ti.factory, None) if m is not None: if all_meta is None: # Set up the meta type -> permission dict all_meta = {} for d in container.all_meta_types(): all_meta[d['name']] = d['permission'] perm = all_meta.get(ti.content_meta_type, None) if perm is not None: roles = rolesForPermissionOn(perm, container) else: p = None try: p = dispatcher[ti.product] except AttributeError: continue m = getattr(p, ti.factory, None) if m is None: continue roles = getRoles(p, ti.factory, m, []) if [r for r in roles if rolesInContext.has_key(r)]: result.append(ti) if request is not None: request.set(key, result) return result from Products.CMFCore.TypesTool import TypesTool TypesTool.listTypeInfo = listTypeInfo def getDefaultAddableTypes(self, context=None): """returns a list of normally allowed objects as ftis. Exactly like PortalFolder.allowedContentTypes except this will check in a specific context. """ if context is None: context = self if shasattr(aq_base(context), 'UID'): uid = context.UID() else: uid = hash(context) key = 'defaulttypes%s' % uid tmp = self.REQUEST.get(key, None) if tmp is not None: return tmp portal_types = getToolByName(self, 'portal_types') myType = portal_types.getTypeInfo(self) if myType is None: res = portal_types.listTypeInfo() self.REQUEST.set(key, res) return res typelist = [] for t in portal_types.objectValues(): # Filter out things that aren't TypeInformation and # types for which the user does not have adequate permission. if not ITypeInformation.providedBy(t): continue if not t.getId(): # XXX What's this used for ? # Not ready. continue if myType.allowType( t.getId() ): typelist.append( t ) if not typelist: res = [] self.REQUEST.set(key, res) return res addcontext = context portal_membership = getToolByName(self, 'portal_membership') member = portal_membership.getAuthenticatedMember() rolesInContext = dict.fromkeys(member.getRolesInContext(addcontext)) if not rolesInContext: res = [] self.REQUEST.set(key, res) return res dispatcher = getattr(addcontext, 'manage_addProduct', None) if dispatcher is None: res = [] self.REQUEST.set(key, res) return res result = [] # XXX need to handle constraintypes as well for ti in typelist: if not ti.product and not ti.factory: continue if not ti.product: m = queryUtility(IFactory, ti.factory, None) if m is None: continue mt = None for d in context.all_meta_types(): if d['name'] == ti.content_meta_type: mt = d break if not mt: continue perm = mt['permission'] if perm is None: continue roles = rolesForPermissionOn(perm, context) else: p = None try: p = dispatcher[ti.product] except AttributeError: continue m = getattr(p, ti.factory, None) if m is None: continue roles = getRoles(p, ti.factory, m, []) if [r for r in roles if rolesInContext.has_key(r)]: result.append(ti) self.REQUEST.set(key, result) return result from Products.ATContentTypes.lib.constraintypes import ConstrainTypesMixin ConstrainTypesMixin.getDefaultAddableTypes = getDefaultAddableTypes from Products.CMFCore.PortalFolder import PortalFolderBase PortalFolderBase.allowedContentTypes = getDefaultAddableTypes def _constructInstance(self, container, id, *args, **kw): """Products.CMFCore.TypesTool.FactoryTypeInformation._constructInstance replacement. Use new style factory if no product is set on fti. Additionally we add 'container' as 'parent' kw argument when calling the IFactory implementation. this ensures the availability of the acquisition chain if needed inside the construction logic. The kw argument hack is some kind of semi-valid since the IFactory interface promises the __call__ function to accept all given args and kw args. As long as the specific IFactory implementation provides this signature everything works well unless any other 3rd party factory expects another kind of object as 'parent' kw arg than the provided one. """ # XXX: this method violates the rules for tools/utilities: # it depends on self.REQUEST id = str(id) if self.product: # oldstyle factory m = self._getFactoryMethod(container, check_security=0) if getattr(aq_base(m), 'isDocTemp', 0): kw['id'] = id newid = m(m.aq_parent, self.REQUEST, *args, **kw) else: newid = m(id, *args, **kw) # allow factory to munge ID newid = newid or id else: # newstyle factory factory = getUtility(IFactory, self.factory) kw['parent'] = container obj = factory(id, *args, **kw) rval = container._setObject(id, obj) newid = isinstance(rval, basestring) and rval or id return container._getOb(newid) from Products.CMFCore.TypesTool import FactoryTypeInformation FactoryTypeInformation._constructInstance = _constructInstance def _getTempFolder(self, type_name): factory_info = self.REQUEST.get(FACTORY_INFO, {}) tempFolder = factory_info.get(type_name, None) if tempFolder: tempFolder = aq_inner(tempFolder).__of__(self) return tempFolder # make sure we can add an object of this type to the temp folder types_tool = getToolByName(self, 'portal_types') if not type_name in types_tool.TempFolder.allowed_content_types: # update allowed types for tempfolder types_tool.TempFolder.allowed_content_types=(types_tool.listContentTypes()) tempFolder = TempFolder(type_name).__of__(self) factory_info[type_name] = tempFolder self.REQUEST.set(FACTORY_INFO, factory_info) return tempFolder FactoryTool._getTempFolder = _getTempFolder