if klass is None: klass = _importClass(type_class_path) global property_sheet_generating_portal_type_set accessor_holder_list = [] if portal_type_name not in property_sheet_generating_portal_type_set: # LOG("ERP5Type.dynamic", INFO, # "Filling accessor holder list for portal_type " + portal_type_name) property_sheet_generating_portal_type_set.add(portal_type_name) try: # Initialize ZODB Property Sheets accessor holders accessor_holder_list = createAllAccessorHolderList(site, portal_type_name, portal_type, klass) base_category_set = set(attribute_dict['_categories']) for accessor_holder in accessor_holder_list: base_category_set.update(accessor_holder._categories) attribute_dict['constraints'].extend(accessor_holder.constraints) attribute_dict['_categories'] = list(base_category_set) finally: property_sheet_generating_portal_type_set.remove(portal_type_name) # LOG("ERP5Type.dynamic", INFO, # "Filled accessor holder list for portal_type %s (%s)" % \ # (portal_type_name, accessor_holder_list))
def generatePortalTypeClass(site, portal_type_name): """ Given a portal type, look up in Types Tool the corresponding Base Type object holding the definition of this portal type, and computes __bases__ and __dict__ for the class that will be created to represent this portal type Returns tuple with 4 items: - base_tuple: a tuple of classes to be used as __bases__ - base_category_list: categories defined on the portal type (and portal type only: this excludes property sheets) - interface_list: list of zope interfaces the portal type implements - attribute dictionary: any additional attributes to put on the class """ # LOG("ERP5Type.dynamic", INFO, "Loading portal type " + portal_type_name) global core_portal_type_class_dict portal_type_category_list = [] attribute_dict = dict(portal_type=portal_type_name, _categories=[], constraints=[]) if portal_type_name in core_portal_type_class_dict: if not core_portal_type_class_dict[portal_type_name]['generating']: # Loading the (full) outer portal type class core_portal_type_class_dict[portal_type_name]['generating'] = True else: # Loading the inner portal type class without any mixin, # interface or Property Sheet klass = _importClass( document_class_registry.get( core_portal_type_class_dict[portal_type_name] ['type_class'])) # LOG("ERP5Type.dynamic", INFO, # "Loaded portal type %s (INNER)" % portal_type_name) # Don't do anything else, just allow to load fully the outer # portal type class return ((klass, ), [], [], attribute_dict) # Do not use __getitem__ (or _getOb) because portal_type may exist in a # type provider other than Types Tool. portal_type = getattr(site.portal_types, portal_type_name, None) type_class = None if portal_type is not None: # type_class has a compatibility getter that should return # something even if the field is not set (i.e. Base Type object # was not migrated yet). It only works if factory_method_id is set. type_class = portal_type.getTypeClass() # The Tools used to have 'Folder' or None as type_class instead of # 'NAME Tool', so make sure the type_class is correct # # NOTE: under discussion so might be removed later on if portal_type_name.endswith('Tool') and type_class in ('Folder', None): type_class = portal_type_name.replace(' ', '') mixin_list = portal_type.getTypeMixinList() interface_list = portal_type.getTypeInterfaceList() portal_type_category_list = portal_type.getTypeBaseCategoryList() attribute_dict['_categories'] = portal_type_category_list[:] acquire_local_role = bool(portal_type.getTypeAcquireLocalRole()) else: LOG( "ERP5Type.dynamic", WARNING, "Cannot find a portal type definition for '%s', trying to guess..." % portal_type_name) # But if neither factory_init_method_id nor type_class are set on # the portal type, we have to try to guess, for compatibility. # Moreover, some tools, such as 'Activity Tool', don't have any # portal type if type_class is None: if portal_type_name in core_portal_type_class_dict: # Only happen when portal_types is empty (e.g. when creating a # new ERP5Site) type_class = core_portal_type_class_dict[portal_type_name][ 'type_class'] else: # Try to figure out a coresponding document class from the # document side. This can happen when calling newTempAmount for # instance: # Amount has no corresponding Base Type and will never have one # But the semantic of newTempXXX requires us to create an # object using the Amount Document, so we promptly do it: type_class = portal_type_name.replace(' ', '') mixin_list = [] interface_list = [] acquire_local_role = True if type_class is None: raise AttributeError('Document class is not defined on Portal Type ' + \ portal_type_name) klass = None if '.' in type_class: type_class_path = type_class else: type_class_path = None # Skip any document within ERP5Type Product as it is needed for # bootstrapping anyway type_class_namespace = document_class_registry.get(type_class, '') if not (type_class_namespace.startswith('Products.ERP5Type') or portal_type_name in core_portal_type_class_dict): module = None if portal_type_name.endswith('Tool'): import erp5.component.tool module = erp5.component.tool.find_load_module(type_class) # Tool Component was introduced recently and some Tool have already been # migrated as Document Component if module is None: import erp5.component.document module = erp5.component.document.find_load_module(type_class) if module is not None: try: klass = getattr(module, type_class) except AttributeError: LOG( "ERP5Type.dynamic", WARNING, "Could not get class '%s' in Component module %r, fallback on filesystem" % (type_class, module)) if klass is None: type_class_path = document_class_registry.get(type_class) if type_class_path is None: raise AttributeError( 'Document class %s has not been registered:' ' cannot import it as base of Portal Type %s' % (type_class, portal_type_name)) if klass is None: try: klass = _importClass(type_class_path) except ImportError: error_msg = 'Could not import %s of Portal Type %s' % ( type_class, portal_type_name) LOG("ERP5Type.Dynamic", WARNING, error_msg, error=True) raise AttributeError(error_msg) global property_sheet_generating_portal_type_set accessor_holder_list = [] if portal_type_name not in property_sheet_generating_portal_type_set: # LOG("ERP5Type.dynamic", INFO, # "Filling accessor holder list for portal_type " + portal_type_name) property_sheet_generating_portal_type_set.add(portal_type_name) try: # Initialize ZODB Property Sheets accessor holders accessor_holder_list = createAllAccessorHolderList( site, portal_type_name, portal_type, klass) base_category_set = set(attribute_dict['_categories']) for accessor_holder in accessor_holder_list: base_category_set.update(accessor_holder._categories) attribute_dict['constraints'].extend( accessor_holder.constraints) attribute_dict['_categories'] = list(base_category_set) finally: property_sheet_generating_portal_type_set.remove(portal_type_name) # LOG("ERP5Type.dynamic", INFO, # "Filled accessor holder list for portal_type %s (%s)" % \ # (portal_type_name, accessor_holder_list)) mixin_class_list = [] if mixin_list: # Only one Mixin class per ZODB Component (!= FS) where module_name == # class_name, name ending with 'Mixin'. # # Rationale: same as Document/Interface; consistent naming; avoid a # registry like there used to be with FS. import erp5.component.mixin for mixin in mixin_list: mixin_module = erp5.component.mixin.find_load_module(mixin) mixin_class = None if mixin_module is not None: try: mixin_class = getattr(mixin_module, mixin) except AttributeError: LOG( "ERP5Type.dynamic", WARNING, "Could not get class '%s' in Component module %r, fallback on filesystem" % (mixin, mixin_module)) if mixin_class is None: mixin_class = _importClass(mixin_class_registry[mixin]) mixin_class_list.append(mixin_class) base_class_list = [klass] + accessor_holder_list + mixin_class_list + [ # _getAcquireLocalRoles is accessed by security machinery, so it needs to # be fast: make it a ConstantGetter while we have access to portal_type # configuration. ACQUIRE_LOCAL_ROLE_GETTER_DICT[acquire_local_role], ] interface_class_list = [] if interface_list: # Filesystem Interfaces may have defined several Interfaces in one file # but only *one* Interface per ZODB Component where module_name == # class_name, name starting with 'I'. # # Rationale: same as Document/Mixin; consistent naming; avoid a registry # like there used to be for Mixin or importing all class in # Products.ERP5Type.interfaces.__init__.py. import erp5.component.interface from Products.ERP5Type import interfaces as filesystem_interfaces for interface in interface_list: interface_module = erp5.component.interface.find_load_module( interface) interface_class = None if interface_module is not None: try: interface_class = getattr(interface_module, interface) except AttributeError: LOG( "ERP5Type.dynamic", WARNING, "Could not get class '%s' in Component module %r, fallback on filesystem" % (interface, interface_module)) if interface_class is None: interface_class = getattr(filesystem_interfaces, interface) interface_class_list.append(interface_class) if portal_type_name in core_portal_type_class_dict: core_portal_type_class_dict[portal_type_name]['generating'] = False attribute_dict['_restricted_setter_set'] = { method for ancestor in base_class_list for permissions in getattr(ancestor, '__ac_permissions__', ()) if permissions[0] not in ('Access contents information', 'Modify portal content') for method in permissions[1] if method.startswith('set') } attribute_dict['_restricted_getter_set'] = { method for ancestor in base_class_list for permissions in getattr(ancestor, '__ac_permissions__', ()) if permissions[0] not in ('Access contents information', ) for method in permissions[1] if method.startswith('get') } #LOG("ERP5Type.dynamic", INFO, # "Portal type %s loaded with bases %s" \ # % (portal_type_name, repr(base_class_list))) return (tuple(base_class_list), portal_type_category_list, interface_class_list, attribute_dict)
def generatePortalTypeClass(site, portal_type_name): """ Given a portal type, look up in Types Tool the corresponding Base Type object holding the definition of this portal type, and computes __bases__ and __dict__ for the class that will be created to represent this portal type Returns tuple with 4 items: - base_tuple: a tuple of classes to be used as __bases__ - base_category_list: categories defined on the portal type (and portal type only: this excludes property sheets) - interface_list: list of zope interfaces the portal type implements - attribute dictionary: any additional attributes to put on the class """ # LOG("ERP5Type.dynamic", INFO, "Loading portal type " + portal_type_name) global core_portal_type_class_dict portal_type_category_list = [] attribute_dict = dict(portal_type=portal_type_name, _categories=[], constraints=[]) if portal_type_name in core_portal_type_class_dict: if not core_portal_type_class_dict[portal_type_name]['generating']: # Loading the (full) outer portal type class core_portal_type_class_dict[portal_type_name]['generating'] = True else: # Loading the inner portal type class without any mixin, # interface or Property Sheet klass = _importClass(document_class_registry.get( core_portal_type_class_dict[portal_type_name]['type_class'])) # LOG("ERP5Type.dynamic", INFO, # "Loaded portal type %s (INNER)" % portal_type_name) # Don't do anything else, just allow to load fully the outer # portal type class return ((klass,), [], [], attribute_dict) # Do not use __getitem__ (or _getOb) because portal_type may exist in a # type provider other than Types Tool. portal_type = getattr(site.portal_types, portal_type_name, None) type_class = None if portal_type is not None: # type_class has a compatibility getter that should return # something even if the field is not set (i.e. Base Type object # was not migrated yet). It only works if factory_method_id is set. type_class = portal_type.getTypeClass() # The Tools used to have 'Folder' or None as type_class instead of # 'NAME Tool', so make sure the type_class is correct # # NOTE: under discussion so might be removed later on if portal_type_name.endswith('Tool') and type_class in ('Folder', None): type_class = portal_type_name.replace(' ', '') mixin_list = portal_type.getTypeMixinList() interface_list = portal_type.getTypeInterfaceList() portal_type_category_list = portal_type.getTypeBaseCategoryList() attribute_dict['_categories'] = portal_type_category_list[:] else: LOG("ERP5Type.dynamic", WARNING, "Cannot find a portal type definition for '%s', trying to guess..." % portal_type_name) # But if neither factory_init_method_id nor type_class are set on # the portal type, we have to try to guess, for compatibility. # Moreover, some tools, such as 'Activity Tool', don't have any # portal type if type_class is None: if portal_type_name in core_portal_type_class_dict: # Only happen when portal_types is empty (e.g. when creating a # new ERP5Site) type_class = core_portal_type_class_dict[portal_type_name]['type_class'] else: # Try to figure out a coresponding document class from the # document side. This can happen when calling newTempAmount for # instance: # Amount has no corresponding Base Type and will never have one # But the semantic of newTempXXX requires us to create an # object using the Amount Document, so we promptly do it: type_class = portal_type_name.replace(' ', '') mixin_list = [] interface_list = [] if type_class is None: raise AttributeError('Document class is not defined on Portal Type %s' \ % portal_type_name) if '.' in type_class: type_class_path = type_class else: type_class_path = document_class_registry.get(type_class) if type_class_path is None: raise AttributeError('Document class %s has not been registered:' ' cannot import it as base of Portal Type %s' % (type_class, portal_type_name)) klass = _importClass(type_class_path) global property_sheet_generating_portal_type_set accessor_holder_list = [] if portal_type_name not in property_sheet_generating_portal_type_set: # LOG("ERP5Type.dynamic", INFO, # "Filling accessor holder list for portal_type " + portal_type_name) property_sheet_generating_portal_type_set.add(portal_type_name) try: # Initialize ZODB Property Sheets accessor holders accessor_holder_list = createAllAccessorHolderList(site, portal_type_name, portal_type, klass) base_category_set = set(attribute_dict['_categories']) for accessor_holder in accessor_holder_list: base_category_set.update(accessor_holder._categories) attribute_dict['constraints'].extend(accessor_holder.constraints) attribute_dict['_categories'] = list(base_category_set) finally: property_sheet_generating_portal_type_set.remove(portal_type_name) # LOG("ERP5Type.dynamic", INFO, # "Filled accessor holder list for portal_type %s (%s)" % \ # (portal_type_name, accessor_holder_list)) mixin_path_list = [] if mixin_list: mixin_path_list = map(mixin_class_registry.__getitem__, mixin_list) mixin_class_list = map(_importClass, mixin_path_list) base_class_list = [klass] + accessor_holder_list + mixin_class_list interface_class_list = [] if interface_list: from Products.ERP5Type import interfaces interface_class_list = [getattr(interfaces, name) for name in interface_list] if portal_type_name in core_portal_type_class_dict: core_portal_type_class_dict[portal_type_name]['generating'] = False #LOG("ERP5Type.dynamic", INFO, # "Portal type %s loaded with bases %s" \ # % (portal_type_name, repr(base_class_list))) return (tuple(base_class_list), portal_type_category_list, interface_class_list, attribute_dict)
def generatePortalTypeClass(site, portal_type_name): """ Given a portal type, look up in Types Tool the corresponding Base Type object holding the definition of this portal type, and computes __bases__ and __dict__ for the class that will be created to represent this portal type Returns tuple with 4 items: - base_tuple: a tuple of classes to be used as __bases__ - base_category_list: categories defined on the portal type (and portal type only: this excludes property sheets) - interface_list: list of zope interfaces the portal type implements - attribute dictionary: any additional attributes to put on the class """ # LOG("ERP5Type.dynamic", INFO, "Loading portal type " + portal_type_name) global core_portal_type_class_dict portal_type_category_list = [] attribute_dict = dict(portal_type=portal_type_name, _categories=[], constraints=[]) if portal_type_name in core_portal_type_class_dict: if not core_portal_type_class_dict[portal_type_name]['generating']: # Loading the (full) outer portal type class core_portal_type_class_dict[portal_type_name]['generating'] = True else: # Loading the inner portal type class without any mixin, # interface or Property Sheet klass = _importClass(document_class_registry.get( core_portal_type_class_dict[portal_type_name]['type_class'])) # LOG("ERP5Type.dynamic", INFO, # "Loaded portal type %s (INNER)" % portal_type_name) # Don't do anything else, just allow to load fully the outer # portal type class return ((klass,), [], [], attribute_dict) # Do not use __getitem__ (or _getOb) because portal_type may exist in a # type provider other than Types Tool. portal_type = getattr(site.portal_types, portal_type_name, None) type_class = None if portal_type is not None: # type_class has a compatibility getter that should return # something even if the field is not set (i.e. Base Type object # was not migrated yet). It only works if factory_method_id is set. type_class = portal_type.getTypeClass() # The Tools used to have 'Folder' or None as type_class instead of # 'NAME Tool', so make sure the type_class is correct # # NOTE: under discussion so might be removed later on if portal_type_name.endswith('Tool') and type_class in ('Folder', None): type_class = portal_type_name.replace(' ', '') mixin_list = portal_type.getTypeMixinList() interface_list = portal_type.getTypeInterfaceList() portal_type_category_list = portal_type.getTypeBaseCategoryList() attribute_dict['_categories'] = portal_type_category_list[:] else: LOG("ERP5Type.dynamic", WARNING, "Cannot find a portal type definition for '%s', trying to guess..." % portal_type_name) # But if neither factory_init_method_id nor type_class are set on # the portal type, we have to try to guess, for compatibility. # Moreover, some tools, such as 'Activity Tool', don't have any # portal type if type_class is None: if portal_type_name in core_portal_type_class_dict: # Only happen when portal_types is empty (e.g. when creating a # new ERP5Site) type_class = core_portal_type_class_dict[portal_type_name]['type_class'] else: # Try to figure out a coresponding document class from the # document side. This can happen when calling newTempAmount for # instance: # Amount has no corresponding Base Type and will never have one # But the semantic of newTempXXX requires us to create an # object using the Amount Document, so we promptly do it: type_class = portal_type_name.replace(' ', '') mixin_list = [] interface_list = [] if type_class is None: raise AttributeError('Document class is not defined on Portal Type ' + \ portal_type_name) klass = None if '.' in type_class: type_class_path = type_class else: type_class_path = None # Skip any document within ERP5Type Product as it is needed for # bootstrapping anyway type_class_namespace = document_class_registry.get(type_class, '') if not (type_class_namespace.startswith('Products.ERP5Type') or portal_type_name in core_portal_type_class_dict): import erp5.component.document module = erp5.component.document.find_load_module(type_class) if module is not None: try: klass = getattr(module, type_class) except AttributeError: LOG("ERP5Type.dynamic", WARNING, "Could not get class '%s' in Component module '%s'" % \ (type_class, module_fullname)) if klass is None: type_class_path = document_class_registry.get(type_class) if type_class_path is None: raise AttributeError('Document class %s has not been registered:' ' cannot import it as base of Portal Type %s' % (type_class, portal_type_name)) if klass is None: klass = _importClass(type_class_path) global property_sheet_generating_portal_type_set accessor_holder_list = [] if portal_type_name not in property_sheet_generating_portal_type_set: # LOG("ERP5Type.dynamic", INFO, # "Filling accessor holder list for portal_type " + portal_type_name) property_sheet_generating_portal_type_set.add(portal_type_name) try: # Initialize ZODB Property Sheets accessor holders accessor_holder_list = createAllAccessorHolderList(site, portal_type_name, portal_type, klass) base_category_set = set(attribute_dict['_categories']) for accessor_holder in accessor_holder_list: base_category_set.update(accessor_holder._categories) attribute_dict['constraints'].extend(accessor_holder.constraints) attribute_dict['_categories'] = list(base_category_set) finally: property_sheet_generating_portal_type_set.remove(portal_type_name) # LOG("ERP5Type.dynamic", INFO, # "Filled accessor holder list for portal_type %s (%s)" % \ # (portal_type_name, accessor_holder_list)) mixin_path_list = [] if mixin_list: mixin_path_list = map(mixin_class_registry.__getitem__, mixin_list) mixin_class_list = map(_importClass, mixin_path_list) base_class_list = [klass] + accessor_holder_list + mixin_class_list interface_class_list = [] if interface_list: from Products.ERP5Type import interfaces interface_class_list = [getattr(interfaces, name) for name in interface_list] if portal_type_name in core_portal_type_class_dict: core_portal_type_class_dict[portal_type_name]['generating'] = False #LOG("ERP5Type.dynamic", INFO, # "Portal type %s loaded with bases %s" \ # % (portal_type_name, repr(base_class_list))) return (tuple(base_class_list), portal_type_category_list, interface_class_list, attribute_dict)