def __call__(self, *args, **kw): fti = getUtility(IDexterityFTI, name=self.portal_type) klass = resolveDottedName(fti.klass) if klass is None or not callable(klass): raise ValueError( 'Content class {0:s} set for type {1:s} is not valid' .format(fti.klass, self.portal_type) ) try: obj = klass(*args, **kw) except TypeError as e: raise ValueError( 'Error whilst constructing content for {0:s}' 'using class {1:s}: {2:s}' .format(self.portal_type, fti.klass, str(e)) ) # Set portal_type if not set, but avoid creating an instance variable # if possible if getattr(obj, 'portal_type', '') != self.portal_type: obj.portal_type = self.portal_type return obj
def _absModelFile(self): colons = self.model_file.count(':') model_file = self.model_file # We have a package and not an absolute Windows path if colons == 1 and self.model_file[1:3] != ':\\': package, filename = self.model_file.split(':') mod = utils.resolveDottedName(package) # let / work as path separator on all platforms filename = filename.replace('/', os.path.sep) model_file = os.path.join(os.path.split(mod.__file__)[0], filename) else: if not os.path.isabs(model_file): raise ValueError( u"Model file name %s is not an absolute path and does " u"not contain a package name in %s" % (model_file, self.getId(),) ) if not os.path.isfile(model_file): raise ValueError( u"Model file %s in %s cannot be found" % (model_file, self.getId(),) ) return model_file
def populate_dexterity_type(obj, data): request = getRequest() for schema in get_dexterity_schemas(context=obj): for name in getFieldNames(schema): field = schema[name] autoform_widgets = schema.queryTaggedValue(WIDGETS_KEY, default={}) if name in autoform_widgets: widgetclass = utils.resolveDottedName(autoform_widgets[name]) widget = widgetclass(field, request) else: widget = component.getMultiAdapter((field, request), IFieldWidget) widget.context = obj widget.ignoreRequest = True widget.update() value = widget.value if not value or value in [NOT_CHANGED, NO_VALUE] or \ not IDataConverter(widget).toFieldValue(widget.value): value = get_dummy_dexterity_value(obj, widget, data) if value: dm = component.getMultiAdapter((obj, field), IDataManager) try: dm.set(value) except WrongType: value = IDataConverter(widget).toFieldValue(value) dm.set(value)
def reattachReferencedAttributes(self, obj, attrs_dict): obj = aq_base(obj) for name, blob in attrs_dict.iteritems(): iface = resolveDottedName('.'.join(name.split('.')[:-1])) fname = name.split('.')[-1] field = iface.get(fname) field.get(iface(obj))._blob = blob
def _absModelFile(self): colons = self.model_file.count(':') model_file = self.model_file # We have a package and not an absolute Windows path if colons == 1 and self.model_file[1:3] != ':\\': package, filename = self.model_file.split(':') mod = utils.resolveDottedName(package) # let / work as path separator on all platforms filename = filename.replace('/', os.path.sep) model_file = os.path.join(os.path.split(mod.__file__)[0], filename) else: if not os.path.isabs(model_file): raise ValueError( u"Model file name %s is not an absolute path and does not contain a package name in %s" % ( model_file, self.getId(), )) if not os.path.isfile(model_file): raise ValueError(u"Model file %s in %s cannot be found" % ( model_file, self.getId(), )) return model_file
def finish_edit(task, obj, task_id): task_record = utils.get_task(task_id) changes = task_record["changes"] descriptions = [] for interface, names in changes: interface = dexterity_utils.resolveDottedName(interface) descriptions.append(zope.lifecycleevent.Attributes(interface, *names)) try: zope.event.notify( zope.lifecycleevent.ObjectModifiedEvent(obj, *descriptions)) zope.event.notify(dexterity_events.EditFinishedEvent(obj)) record_task_result.apply_async([task_id, constants.SUCCESS], dict()) transaction.commit() except ConflictError: retries = task.request.retries + 1 max_retries = task.max_retries if max_retries is not None and retries > max_retries: tb = traceback.format_exc() record_task_result.apply_async( [task_id, constants.ERROR], dict(tb=tb), without_transaction=True, ) raise except Exception as e: exc = str(e) tb = traceback.format_exc() record_task_result.apply_async( [task_id, constants.ERROR], dict(message=exc, tb=tb), without_transaction=True, ) raise
def __init__(self, id, *args, **kwargs): self.id = id if 'schema' in kwargs: self.schema = kwargs['schema'] if 'klass' in kwargs: self.klass = kwargs['klass'] # if 'aliases' not in kwargs: # self.setMethodAliases(self.default_aliases) # if 'actions' not in kwargs: # for action in self.default_actions: # self.addAction(id=action['id'], # name=action['title'], # action=action['action'], # condition=action.get('condition'), # permission=action.get('permissions', ()), # category=action.get('category', 'object'), # visible=action.get('visible', True)) # Default factory name to be the FTI name if not self.factory: self.factory = self.id # In CMF (2.2+, but we've backported it) the property add_view_expr is # used to construct an action in the 'folder/add' category. The # portal_types tool loops over all FTIs and lets them provide such # actions. # # By convention, the expression is string:${folder_url}/++add++my.type # # The ++add++ traverser will find the FTI with name my.type, and then # looks up an adapter for (context, request, fti) with a name equal # to fti.factory, falling back on an unnamed adapter. The result is # assumed to be an add view. # # Dexterity provides a default (unnamed) adapter for any IFolderish # context, request and IDexterityFTI that can construct an add view # for any Dexterity schema. # if not self.add_view_expr: # add_view_expr = kwargs.get( # 'add_view_expr', # 'string:${folder_url}/++add++{0:s}'.format(self.getId()) # ) # self._setPropValue('add_view_expr', add_view_expr) # Set the content_meta_type from the klass klass = utils.resolveDottedName(self.klass) if klass is not None: self.content_meta_type = getattr(klass, 'meta_type', None) if 'behaviors' in kwargs: self.behaviors = kwargs['behaviors'] if 'add_permission' in kwargs: self.add_permission = kwargs['add_permission']
def Metatype(self): if self.content_meta_type: return self.content_meta_type # BBB - this didn't use to be set klass = utils.resolveDottedName(self.klass) if klass is not None: self.content_meta_type = getattr(klass, 'meta_type', None) return self.content_meta_type
def reattachReferencedAttributes(self, obj, attrs_dict): obj = aq_base(obj) for name, blob in attrs_dict.iteritems(): iface = resolveDottedName(".".join(name.split(".")[:-1])) fname = name.split(".")[-1] field = iface.get(fname) if field is not None: # Field may have been removed from schema field.get(iface(obj))._blob = blob
def reattachReferencedAttributes(self, obj, attrs_dict): obj = aq_base(obj) for name, blob in attrs_dict.iteritems(): iface = resolveDottedName('.'.join(name.split('.')[:-1])) fname = name.split('.')[-1] field = iface.get(fname) if field is not None: # Field may have been removed from schema field.get(iface(obj))._blob = blob
def __call__(self, context): sm = getSiteManager(context) ftis = sm.getAllUtilitiesRegisteredFor(IDexterityFTI) terms = [] for fti in ftis: cls = resolveDottedName(fti.klass) if self.interface.implementedBy(cls): terms.append(SimpleTerm(fti, fti.id, fti.title)) return SimpleVocabulary(terms)
def __call__(self, *args, **kw): fti = getUtility(IDexterityFTI, name=self.portal_type) klass = resolveDottedName(fti.klass) if klass is None or not callable(klass): raise ValueError("Content class %s set for type %s is not valid" % (fti.klass, self.portal_type)) try: obj = klass(*args, **kw) except TypeError, e: raise ValueError("Error whilst constructing content for %s using class %s: %s" % (self.portal_type, fti.klass, str(e)))
def populate_dexterity(obj, data): request = getRequest() for schema in get_dexterity_schemas(context=obj): for name in getFieldNames(schema): field = schema[name] if name in ['expires', 'effective', 'relatedItems']: # skip some fields continue if getattr(field, 'readonly', False): continue autoform_widgets = schema.queryTaggedValue(WIDGETS_KEY, default={}) if name in autoform_widgets: try: widgetclass = utils.resolveDottedName( autoform_widgets[name]) except AttributeError: # XXX: Investigate: # AttributeError: 'ParameterizedWidget' object has no # attribute 'split' continue widget = widgetclass(field, request) else: widget = component.getMultiAdapter( (field, request), IFieldWidget) widget.context = obj widget.ignoreRequest = True widget.update() if HAS_RECURRENCE_WIDGET and IRecurrenceWidget.providedBy(widget): # We cannot yet deal with the recurrence widget continue if name == 'title': value = unicode(data['title']) else: value = widget.value if not value or value in [NOT_CHANGED, NO_VALUE] or \ not IDataConverter(widget).toFieldValue(value): value = get_dummy_dexterity_value(obj, widget, data) if value is None: continue if interfaces.ICollection.providedBy(widget.field) or \ interfaces.IChoice.providedBy(widget.field): value = [value] if value: dm = component.getMultiAdapter((obj, field), IDataManager) try: dm.set(IDataConverter(widget).toFieldValue(value)) except TypeError: dm.set(value)
def __call__(self, *args, **kw): fti = getUtility(IDexterityFTI, name=self.portal_type) klass = resolveDottedName(fti.klass) if klass is None or not callable(klass): raise ValueError("Content class %s set for type %s is not valid" % (fti.klass, self.portal_type)) try: obj = klass(*args, **kw) except TypeError, e: raise ValueError( "Error whilst constructing content for %s using class %s: %s" % (self.portal_type, fti.klass, str(e)))
def lookupSchema(self): # If a specific schema is given, use it if self.schema: schema = utils.resolveDottedName(self.schema) if schema is None: raise ValueError(u"Schema %s set for type %s cannot be resolved" % (self.schema, self.getId())) return schema # Otherwise, look up a dynamic schema. This will query the model for # an unnamed schema if it is the first time it is looked up. # See schema.py schemaName = utils.portalTypeToSchemaName(self.getId()) return getattr(plone.dexterity.schema.generated, schemaName)
def __init__(self, *args, **kwargs): super(DexterityFTI, self).__init__(*args, **kwargs) if 'aliases' not in kwargs: self.setMethodAliases(self.default_aliases) if 'actions' not in kwargs: for action in self.default_actions: self.addAction(id=action['id'], name=action['title'], action=action['action'], condition=action.get('condition'), permission=action.get('permissions', ()), category=action.get('category', 'object'), visible=action.get('visible', True)) # Default factory name to be the FTI name if not self.factory: self.factory = self.getId() # In CMF (2.2+, but we've backported it) the property add_view_expr is # used to construct an action in the 'folder/add' category. The # portal_types tool loops over all FTIs and lets them provide such # actions. # # By convention, the expression is string:${folder_url}/++add++my.type # # The ++add++ traverser will find the FTI with name my.type, and then # looks up an adapter for (context, request, fti) with a name equal # to fti.factory, falling back on an unnamed adapter. The result is # assumed to be an add view. # # Dexterity provides a default (unnamed) adapter for any IFolderish # context, request and IDexterityFTI that can construct an add view # for any Dexterity schema. if not self.add_view_expr: add_view_expr = kwargs.get( 'add_view_expr', "string:${folder_url}/++add++%s" % self.getId() ) self._setPropValue('add_view_expr', add_view_expr) # Set the content_meta_type from the klass klass = utils.resolveDottedName(self.klass) if klass is not None: self.content_meta_type = getattr(klass, 'meta_type', None)
def getDexterityFolderTypes(): try: from plone.dexterity.interfaces import IDexterityFTI from plone.dexterity.utils import resolveDottedName except ImportError: return set([]) portal_types = getToolByName(portal, "portal_types") types = [fti for fti in portal_types.listTypeInfo() if IDexterityFTI.providedBy(fti)] ftypes = set([]) for _type in types: klass = resolveDottedName(_type.klass) if ISyndicatable.implementedBy(klass): ftypes.add(_type.getId()) return ftypes
def lookupSchema(self): # If a specific schema is given, use it if self.schema: schema = utils.resolveDottedName(self.schema) if schema is None: raise ValueError( u"Schema %s set for type %s cannot be resolved" % (self.schema, self.getId())) return schema # Otherwise, look up a dynamic schema. This will query the model for # an unnamed schema if it is the first time it is looked up. # See schema.py schemaName = utils.portalTypeToSchemaName(self.getId()) return getattr(plone.dexterity.schema.generated, schemaName)
def initialize(self): """Initialize the view class.""" fieldId = self.request.get('fieldId', '').split('-')[-1] typeOrDottedname = self.request.get('typeOrDottedname') context = aq_inner(self.context) if typeOrDottedname == context.portal_type and shasattr( context, 'Schema'): # Archetype field = context.Schema().getField(fieldId) self.multivalued = field.multiValued self.widget = field.widget else: fti = queryUtility(IDexterityFTI, name=typeOrDottedname) if fti is None: # Must be a standalone z3c.form forms then. klass = utils.resolveDottedName(typeOrDottedname) field = klass(self.context, self.request).fields.get(fieldId).field self.widget = FieldWidget( field, UserAndGroupSelectionWidget(field, self.request)) self.multivalued = ICollection.providedBy(field) else: # Dexterity schema = fti.lookupSchema() field = schema.get(fieldId) if field is None: # The field might be defined in a behavior schema. # Get the behaviors from either the context or the # portal_type (but not both at the same time). if self.request.get('ignoreContext'): context = None portal_type = typeOrDottedname else: portal_type = None for behavior_schema in \ utils.getAdditionalSchemata(context, portal_type): if behavior_schema is not None: field = behavior_schema.get(fieldId) if field is not None: break self.widget = FieldWidget( field, UserAndGroupSelectionWidget(field, self.request)) self.multivalued = ICollection.providedBy(field) self.memberlookup = MemberLookup(self.context, self.request, self.widget)
def getInterfacesFor(context=None, portal_type=None): if context is None and portal_type is None: return if context is None: kwargs = {'portal_type': portal_type} fti = queryUtility(IDexterityFTI, name=portal_type) else: kwargs = {'context': context} fti = queryUtility(IDexterityFTI, name=context.portal_type) if fti is None: return for interface in implementedBy(resolveDottedName(fti.klass)): yield interface for schema in getBehaviorsFor(**kwargs): yield schema yield fti.lookupSchema()
def iterSchemataForType(portal_type): # BBB: merge this with plone.dexterity.utils.iterSchemata, which should # really call this function fti = queryUtility(IDexterityFTI, name=portal_type) if fti is None: return yield fti.lookupSchema() for behavior in fti.behaviors: try: behaviorInterface = resolveDottedName(behavior) except ValueError: continue if behaviorInterface is not None: behaviorSchema = IFormFieldProvider(behaviorInterface, None) if behaviorSchema is not None: yield behaviorSchema
def _getDexterityFolderTypes(portal): try: from plone.dexterity.interfaces import IDexterityFTI from plone.dexterity.utils import resolveDottedName from Products.CMFPlone.interfaces.syndication import ISyndicatable except ImportError: return set([]) portal_types = getToolByName(portal, 'portal_types') types = [fti for fti in portal_types.listTypeInfo() if IDexterityFTI.providedBy(fti)] ftypes = set([]) for _type in types: klass = resolveDottedName(_type.klass) if ISyndicatable.implementedBy(klass): ftypes.add(_type.getId()) return ftypes
def _updateProperty(self, id, value): """Allow property to be updated, and fire a modified event. We do this on a per-property basis and invalidate selectively based on the id of the property that was changed. """ oldValue = getattr(self, id, None) super(DexterityFTI, self)._updateProperty(id, value) new_value = getattr(self, id, None) if oldValue != new_value: modified(self, DexterityFTIModificationDescription(id, oldValue)) # Update meta_type from klass if id == 'klass': klass = utils.resolveDottedName(new_value) if klass is not None: self.content_meta_type = getattr(klass, 'meta_type', None)
def filter_request(self): """ Manipulate the request to support the fallback theme: - Remove the 'normal' theme layer from the request to disable the diazo transform of that theme and fully fall back to the underlying configured theme, typically barceloneta. - Enable/disable resource bundles to restore the fallback theme. Typically involves removing the 'normal' theme bundle(s). Some control panels add a bundle to the request on rendering, and we make sure to play nice with that and extend rather than replace such request bundle settings. """ if self.request.get('ploneintranet.themeswitcher.marker'): return # manipulate the same request only once self.request.set('ploneintranet.themeswitcher.marker', True) if not self.isFallbackActive(): return # only on fallback, remove current theme browser layer(s) switcher = self.getSwitcherSettings() remove_layers = [ resolveDottedName(x) for x in switcher.browserlayer_filterlist ] active_layers = [ x for x in directlyProvidedBy(self.request) if x not in remove_layers ] directlyProvides(self.request, *active_layers) # CMFPlone/resource/browser/resource # supports enable/disable bundles directly on the request if switcher.fallback_enabled_bundles: for bundle in switcher.fallback_enabled_bundles: add_bundle_on_request(self.request, bundle) if switcher.fallback_disabled_bundles: for bundle in switcher.fallback_disabled_bundles: remove_bundle_on_request(self.request, bundle) else: log.warn("NO bundles disabled on fallback. That's weird.")
def __call__(self, context, request): if request.get("ploneintranet.layout.app.enabled"): return # manipulate the same request only once, and only for one app request.set("ploneintranet.layout.app.enabled", True) app_layers = list(context.app_layers) # do not undo themeswitching policy = theming_policy(request) if ISwitchableThemingPolicy.providedBy(policy) and policy.isFallbackActive(): # only applies to Barceloneta switcher = policy.getSwitcherSettings() # respect themeswitching blacklist remove_layers = [resolveDottedName(x) for x in switcher.browserlayer_filterlist] # enable only non-blacklisted IAppLayers app_layers = [x for x in app_layers if x not in remove_layers] active_layers = app_layers + get_layers(request) directlyProvides(request, *active_layers)
def initialize(self): """Initialize the view class.""" fieldId = self.request.get('fieldId','').split('-')[-1] typeOrDottedname = self.request.get('typeOrDottedname') context = aq_inner(self.context) if typeOrDottedname == context.portal_type and shasattr(context, 'Schema'): # Archetype field = context.Schema().getField(fieldId) self.multivalued = field.multiValued self.widget = field.widget else: fti = queryUtility(IDexterityFTI, name=typeOrDottedname) if fti is None: # Must be a standalone z3c.form forms then. klass = utils.resolveDottedName(typeOrDottedname) field = klass(self.context, self.request).fields.get(fieldId).field self.widget = FieldWidget(field, UserAndGroupSelectionWidget(field, self.request)) self.multivalued = ICollection.providedBy(field) else: # Dexterity schema = fti.lookupSchema() field = schema.get(fieldId) if field is None: # The field might be defined in a behavior schema. # Get the behaviors from either the context or the # portal_type (but not both at the same time). if self.request.get('ignoreContext'): context = None portal_type = typeOrDottedname else: portal_type = None for behavior_schema in \ utils.getAdditionalSchemata(context, portal_type): if behavior_schema is not None: field = behavior_schema.get(fieldId) if field is not None: break self.widget = FieldWidget(field, UserAndGroupSelectionWidget(field, self.request)) self.multivalued = ICollection.providedBy(field) self.memberlookup = MemberLookup(self.context, self.request, self.widget)
def getDexterityFields(idextif, portal_type=None): fields = [] ret = {} fields.extend(schema.getFieldsInOrder(idextif)) if portal_type: # stolen from plone.dexterity.utils fti = getUtility(IDexterityFTI, name=portal_type) for behavior_name in fti.behaviors: try: behavior_interface = resolveDottedName(behavior_name) except (ValueError, ImportError): continue if behavior_interface is not None: behavior_schema = IFormFieldProvider(behavior_interface, None) if behavior_schema is not None: fields.extend(schema.getFieldsInOrder(behavior_schema)) for k, value in fields: ret[k] = value return ret
def filter_request(self): """ Manipulate the request to support the fallback theme: - Remove the 'normal' theme layer from the request to disable the diazo transform of that theme and fully fall back to the underlying configured theme, typically barceloneta. - Enable/disable resource bundles to restore the fallback theme. Typically involves removing the 'normal' theme bundle(s). Some control panels add a bundle to the request on rendering, and we make sure to play nice with that and extend rather than replace such request bundle settings. """ if self.request.get('ploneintranet.themeswitcher.marker'): return # manipulate the same request only once self.request.set('ploneintranet.themeswitcher.marker', True) if not self.isFallbackActive(): return # only on fallback, remove current theme browser layer(s) switcher = self.getSwitcherSettings() remove_layers = [resolveDottedName(x) for x in switcher.browserlayer_filterlist] active_layers = [x for x in directlyProvidedBy(self.request) if x not in remove_layers] directlyProvides(self.request, *active_layers) # CMFPlone/resource/browser/resource # supports enable/disable bundles directly on the request if switcher.fallback_enabled_bundles: for bundle in switcher.fallback_enabled_bundles: add_bundle_on_request(self.request, bundle) if switcher.fallback_disabled_bundles: for bundle in switcher.fallback_disabled_bundles: remove_bundle_on_request(self.request, bundle) else: log.warn("NO bundles disabled on fallback. That's weird.")
def getBehaviorsFor(context=None, portal_type=None): if context is None and portal_type is None: return if context is None: fti = getUtility(IDexterityFTI, name=portal_type) for behavior_name in fti.behaviors: behavior_interface = None behavior_instance = queryUtility(IBehavior, name=behavior_name) if not behavior_instance: try: behavior_interface = resolveDottedName(behavior_name) except (ValueError, ImportError): continue else: behavior_interface = behavior_instance.interface if behavior_interface is not None: yield behavior_interface else: behavior_assignable = IBehaviorAssignable(context, None) for behavior_reg in behavior_assignable.enumerateBehaviors(): yield behavior_reg.interface
def lookupSchema(self): schema = None # If a specific schema is given, use it if self.schema: try: schema = utils.resolveDottedName(self.schema) except ImportError: logging.warning(u"Schema %s set for type %s cannot be resolved" % (self.schema, self.getId())) # fall through to return a fake class with no # fields so that end user code doesn't break if schema: return schema # Otherwise, look up a dynamic schema. This will query the model for # an unnamed schema if it is the first time it is looked up. # See schema.py schemaName = utils.portalTypeToSchemaName(self.getId()) return getattr(plone.dexterity.schema.generated, schemaName)
def __call__(self, *args, **kw): fti = getUtility(IDexterityFTI, name=self.portal_type) klass = resolveDottedName(fti.klass) if klass is None or not callable(klass): raise ValueError("Content class %s set for type %s is not valid" % (fti.klass, self.portal_type)) try: obj = klass(*args, **kw) except TypeError as e: raise ValueError( "Error whilst constructing content for %s using class %s: %s" % (self.portal_type, fti.klass, str(e))) # Set portal_type if not set, but avoid creating an instance variable # if possible if getattr(obj, 'portal_type', '') != self.portal_type: obj.portal_type = self.portal_type return obj
def __call__(self, context, request): if request.get('ploneintranet.layout.app.enabled'): return # manipulate the same request only once, and only for one app request.set('ploneintranet.layout.app.enabled', True) app_layers = list(context.app_layers) # do not undo themeswitching policy = theming_policy(request) if ISwitchableThemingPolicy.providedBy(policy) \ and policy.isFallbackActive(): # only applies to Barceloneta switcher = policy.getSwitcherSettings() # respect themeswitching blacklist remove_layers = [ resolveDottedName(x) for x in switcher.browserlayer_filterlist ] # enable only non-blacklisted IAppLayers app_layers = [x for x in app_layers if x not in remove_layers] active_layers = app_layers + get_layers(request) directlyProvides(request, *active_layers)
def reattachReferencedAttributes(self, obj, attrs_dict): obj = aq_base(obj) for name, blob in six.iteritems(attrs_dict): iface_name, f_name = name.rsplit('.', 1) # In case the field is provided via a behavior: # Look up the behavior via dotted name. # If the behavior's dotted name was changed, we might still have # the old name in our attrs_dict. # Use the fallback of plone.behavior, provided via the field # former_dotted_names, so that the correct behavior can still # be found. try: behavior = lookup_behavior_registration(iface_name) iface = behavior.interface except BehaviorRegistrationNotFound: # Not a behavior - fetch the interface directly iface = resolveDottedName(iface_name) field = iface.get(f_name) if field is not None: # Field may have been removed from schema adapted_field = field.get(iface(obj)) if adapted_field: adapted_field._blob = blob
def lookupSchema(self): schema = None # If a specific schema is given, use it if self.schema: try: schema = utils.resolveDottedName(self.schema) except ImportError: logging.warning( u"Schema %s set for type %s cannot be resolved" % (self.schema, self.getId())) # fall through to return a fake class with no # fields so that end user code doesn't break if schema: return schema # Otherwise, look up a dynamic schema. This will query the model for # an unnamed schema if it is the first time it is looked up. # See schema.py schemaName = utils.portalTypeToSchemaName(self.getId()) return getattr(plone.dexterity.schema.generated, schemaName)
def get_behav(name): behav = queryUtility(IBehavior, name=name) if behav is not None: return behav.interface else: return resolveDottedName(name)