class Worklist(IdAsReferenceMixin("worklist_"), GuardableMixin, Predicate): """ ERP5 Worklist. Variables: - Workflow Variable where for_catalog == 1. - State Variable. - SECURITY_PARAMETER_ID (local_roles). """ meta_type = 'ERP5 Worklist' portal_type = 'Worklist' add_permission = Permissions.AddPortalContent security = ClassSecurityInfo() security.declareObjectProtected(Permissions.AccessContentsInformation) property_sheets = ( 'Base', 'XMLObject', 'CategoryCore', 'DublinCore', 'Reference', 'Comment', 'Guard', 'ActionInformation', 'Predicate', ) security.declareProtected(Permissions.AccessContentsInformation, 'getIdentityCriterionDict') def getIdentityCriterionDict(self): """ XXX: Move this to Predicate class? """ try: return dict(self._identity_criterion) except AttributeError: return {} # XXX(PERF): hack to see Category Tool responsability in new workflow slowness security.declareProtected(Permissions.AccessContentsInformation, 'getActionType') def getActionType(self): prefix_length = len('action_type/') action_type_list = [ path[prefix_length:] for path in self.getCategoryList() if path.startswith('action_type/') ] try: return action_type_list[0] except IndexError: return None
class WorkflowVariable(IdAsReferenceMixin("variable_"), XMLObject, GuardableMixin, ExpressionMixin('variable_default_expression')): """ A ERP5 Workflow Variable. """ meta_type = 'ERP5 Variable' portal_type = 'Workflow Variable' add_permission = Permissions.AddPortalContent security = ClassSecurityInfo() security.declareObjectProtected(Permissions.AccessContentsInformation) property_sheets = ( 'Base', 'XMLObject', 'CategoryCore', 'DublinCore', 'Reference', 'Comment', 'Guard', 'WorkflowVariable', )
class WorkflowScript(IdAsReferenceMixin(SCRIPT_PREFIX), PythonScript): """ Script within a Workflow taking state_change as a parameter. Difference with Python Script: Reference added and ID automatically set to reference prefixed with SCRIPT_PREFIX (Workflow Sctrips are at the same level as Transitions, States, Variables and Worklists). """ meta_type = 'ERP5 Workflow Script' portal_type = 'Workflow Script' add_permission = Permissions.AddPortalContent security = ClassSecurityInfo() security.declareObjectProtected(Permissions.AccessContentsInformation) property_sheets = ( 'Base', 'XMLObject', 'CategoryCore', 'DublinCore', 'PythonScript', 'Reference', )
class StandardProperty(IdAsReferenceMixin('_property'), XMLObject): """ Define a Standard Property Document for a ZODB Property Sheet A Standard Property contains the following attributes: - reference: string - description: string - elementary_type: string - storage_id: string - multivalued: boolean (default: False) - property_default: TALES Expression as a string - range: boolean (default: False) - preference: boolean (default: False) - read_permission: string (default: Permissions.AccessContentsInformation) - write_permission: string (default: Permissions.ModifyPortalContent) - translatable: boolean (default: False) - translation_domain: string """ meta_type = 'ERP5 Standard Property' portal_type = 'Standard Property' # Declarative security security = ClassSecurityInfo() security.declareObjectProtected(Permissions.AccessContentsInformation) property_sheets = (PropertySheet.SimpleItem, PropertySheet.StandardProperty, PropertySheet.Reference, PropertySheet.TranslatableProperty) # Names mapping between filesystem to ZODB property, only meaningful # when importing a property from its filesystem definition _name_mapping_filesystem_to_zodb_dict = { 'type': 'elementary_type', 'default': 'property_default' } # ZODB name of attributes whose value is a TALES Expression string _expression_attribute_tuple = ('property_default', ) # Define getters for the property. This is necessary for bootstrap # as a Standard Property is defined by Standard Properties which # also depends on Property Sheets defined by Standard Properties. # # There is no need to define the setter as this static definition of # the getter is only meaningful for the Standard Properties defined # within an Standard Property. getDescription = Base.Getter('getDescription', 'description', 'string', default='') security.declareProtected(Permissions.AccessContentsInformation, 'getElementaryType') def getElementaryType(self): """ Define this getter manually as it is not possible to rely on CategoryTool during the bootstrap """ for category in self.__dict__.get('categories', ()): if category.startswith('elementary_type/'): return category.split('elementary_type/')[1] return getattr(self, 'elementary_type', None) # The following getters have been defined to address bootstrap # issues getStorageId = Base.Getter('getStorageId', 'storage_id', 'string') getMultivalued = Base.Getter('getMultivalued', 'multivalued', 'boolean', default=False) # Define as a TALES expression string, use for Expression # instanciation when exporting the property to the filesystem # definition getPropertyDefault = Base.Getter('getPropertyDefault', 'property_default', 'string') getRange = Base.Getter('getRange', 'range', 'boolean', default=False) getPreference = Base.Getter('getPreference', 'preference', 'boolean', default=False) getReadPermission = Base.Getter( 'getReadPermission', 'read_permission', 'string', default=Permissions.AccessContentsInformation) getWritePermission = Base.Getter('getWritePermission', 'write_permission', 'string', default=Permissions.ModifyPortalContent) getTranslatable = Base.Getter('getTranslatable', 'translatable', 'boolean', default=False) getTranslationDomain = Base.Getter('getTranslationDomain', 'translation_domain', 'string') getSelectVariable = Base.Getter('getSelectVariable', 'select_variable', 'string') @classmethod def _asPropertyMap(cls, property_dict): """ Return the Zope definition of this ZODB property, as used by the PropertyManager (for the ZMI for example). ERP5 _properties and Zope _properties are somehow different. The id is converted to the Zope standard - we keep the original id as base_id. @param property_dict: ZODB property dict @type property_dict: dict @return: PropertyManager definition @rtype: dict """ property_dict['type'] = property_dict.pop('elementary_type') property_dict['default'] = property_dict.pop('property_default') property_dict['id'] = property_dict.pop('reference') # In case, this property is a list, then display it as a list if property_dict['type'] in list_types or property_dict['multivalued']: property_dict['base_id'] = property_dict['id'] property_dict['id'] = property_dict['id'] + '_list' # Maintain consistency while displaying properties form. # Addition of select_variable property is required for 'selection' # and 'multiple selection' property type as while rendering properties # dtml file, it asks for 'select_variable' property if property_dict['type'] in ['selection', 'multiple selection']: property_dict['select_variable'] = property_dict.pop( 'select_variable') return property_dict @staticmethod def _applyDefinitionFormatDictOnAccessorHolder(reference, definition_dict, accessor_holder, argument_list, permission): """ Apply a definition dict, a format string defining the accessor name as the key (formatted with the given reference) and the accessor class to be used as the value, on the given accessor holder class. The accessor class given in the definition dict is instanciated with the given argument list and the the accessor is protected using the given permission (which may be either read if it is a getter or tester, or write if it is a setter), it is then registered on the accessor holder. For most cases, the reference is used in the accessor name but there are exceptions, such as translation accessors. @param reference: Reference to be used to format accessor name @type reference: str @param definition_dict: Definition of accessors being created @type definition_dict: dict @param accessor_holder: Accessor holder to applied the accessors on @type accessor_holder: Products.ERP5Type.dynamic.accessor_holder.AccessorHolderType @param argument_list: Arguments to be given to the accessor class constructor @type argument_list: list @param permission: Permission to be applied on the accessor @type permission: str """ uppercase_reference = UpperCase(reference) for format, klass in definition_dict.iteritems(): name = format % uppercase_reference instance = klass(name, reference, *argument_list) accessor_holder.registerAccessor(instance, permission) # Public setters actually just calls the private one and then # perform a re-indexing if name.startswith('_set'): instance = Alias.Reindex(name[1:], name) accessor_holder.registerAccessor(instance, permission) @classmethod def _applyRangeOnAccessorHolder(cls, property_dict, accessor_holder, kind, portal): """ Apply range accessors. @param property_dict: Property to generate getter for @type property_dict: dict @param accessor_holder: Accessor holder to applied the accessors on @type accessor_holder: Products.ERP5Type.dynamic.accessor_holder.AccessorHolderType @param kind: 'min' or 'max' @type kind: string @param portal: Portal object @type portal: Products.ERP5.ERP5Site.ERP5Site """ property_dict['reference'] = '%s_range_%s' % ( property_dict['reference'], kind) # Override storage_id to not store the value on the same attribute # as the "normal" accessor property_dict['storage_id'] = None # Set range to False to avoid infinite recursion upon # applyDefinitionOnAccessorHolder call property_dict['range'] = False cls.applyDefinitionOnAccessorHolder(property_dict, accessor_holder, portal, do_register=False) _translation_language_getter_definition_dict = { 'get%s': Translation.TranslatedPropertyGetter, '_baseGet%s': Translation.TranslatedPropertyGetter } _translation_language_getter_definition_dict = { 'get%s': Translation.TranslatedPropertyGetter, '_baseGet%s': Translation.TranslatedPropertyGetter } _translation_language_tester_definition_dict = { 'has%s': Translation.TranslatedPropertyTester } _translation_language_setter_definition_dict = { '_set%s': Translation.TranslationPropertySetter } @classmethod def _applyTranslationLanguageOnAccessorHolder(cls, property_dict, accessor_holder, portal): """ Apply translation language accessors. @param property_dict: Property to generate getter for @type property_dict: dict @param accessor_holder: Accessor holder to applied the accessors on @type accessor_holder: Products.ERP5Type.dynamic.accessor_holder.AccessorHolderType @param portal: Portal object @type portal: Products.ERP5.ERP5Site.ERP5Site """ try: localizer = portal._getOb('Localizer') except AttributeError: if not getattr(portal, '_v_bootstrapping', False): LOG("ERP5Type.Core.StandardProperty", WARNING, "Localizer is missing. Accessors can not be generated") return # Apply language-specific accessors for language in localizer.get_languages(): translation_language_id = '%s_translated_%s' % \ (language.replace('-', '_'), property_dict['reference']) # Prepare accessor arguments for getters getter_argument_list = (property_dict['reference'], property_dict['elementary_type'], language, property_dict['property_default']) cls._applyDefinitionFormatDictOnAccessorHolder( translation_language_id, cls._translation_language_getter_definition_dict, accessor_holder, getter_argument_list, property_dict['read_permission']) # Prepare accessor arguments for testers and setters tester_setter_argument_list = (property_dict['reference'], property_dict['elementary_type'], language) cls._applyDefinitionFormatDictOnAccessorHolder( translation_language_id, cls._translation_language_tester_definition_dict, accessor_holder, tester_setter_argument_list, property_dict['read_permission']) cls._applyDefinitionFormatDictOnAccessorHolder( translation_language_id, cls._translation_language_setter_definition_dict, accessor_holder, tester_setter_argument_list, property_dict['write_permission']) _primitive_getter_definition_dict = { 'get%s': Base.Getter, '_baseGet%s': Base.Getter } _list_getter_definition_dict = { 'get%s': List.Getter, '_baseGet%s': List.Getter, 'getDefault%s': List.DefaultGetter, '_baseGetDefault%s': List.DefaultGetter, 'get%sList': List.ListGetter, '_baseGet%sList': List.ListGetter, 'get%sSet': List.SetGetter, '_baseGet%sSet': List.SetGetter } @classmethod def _applyGetterDefinitionDictOnAccessorHolder(cls, property_dict, accessor_holder): """ Apply getters for the given property on the given accessor holder. This method is overriden in AcquiredProperty for example to add accessors specific to Acquired Properties. @param property_dict: Property to generate getter for @type property_dict: dict @param accessor_holder: Accessor holder to applied the accessors on @type accessor_holder: Products.ERP5Type.dynamic.accessor_holder.AccessorHolderType @see _applyDefinitionFormatDictOnAccessorHolder """ argument_list = (property_dict['elementary_type'], property_dict['property_default'], property_dict['storage_id']) if property_dict['elementary_type'] in list_types or \ property_dict['multivalued']: definition_dict = cls._list_getter_definition_dict else: definition_dict = cls._primitive_getter_definition_dict cls._applyDefinitionFormatDictOnAccessorHolder( property_dict['reference'], definition_dict, accessor_holder, argument_list, property_dict['read_permission']) _list_setter_definition_dict = { '_set%s': List.Setter, '_baseSet%s': List.Setter, '_setDefault%s': List.DefaultSetter, '_baseSetDefault%s': List.DefaultSetter, '_set%sList': List.ListSetter, '_baseSet%sList': List.ListSetter, '_set%sSet': List.SetSetter, '_baseSet%sSet': List.SetSetter } _primitive_setter_definition_dict = { '_set%s': Base.Setter, '_baseSet%s': Base.Setter } @classmethod def _applySetterDefinitionDictOnAccessorHolder(cls, property_dict, accessor_holder): """ Apply setters for the given property on the given accessor holder. @param property_dict: Property to generate getter for @type property_dict: dict @param accessor_holder: Accessor holder to applied the accessors on @type accessor_holder: Products.ERP5Type.dynamic.accessor_holder.AccessorHolderType @see _applyGetterDefinitionDictOnAccessorHolder """ argument_list = (property_dict['elementary_type'], property_dict['storage_id']) if property_dict['elementary_type'] in list_types or \ property_dict['multivalued']: definition_dict = cls._list_setter_definition_dict else: definition_dict = cls._primitive_setter_definition_dict cls._applyDefinitionFormatDictOnAccessorHolder( property_dict['reference'], definition_dict, accessor_holder, argument_list, property_dict['write_permission']) _tester_definition_dict = { 'has%s': Base.Tester, '_baseHas%s': Base.Tester, 'has%sList': List.Tester, '_baseHas%sList': List.Tester, 'hasDefault%s': List.Tester, '_baseHasDefault%s': List.Tester } _boolean_definition_dict = {'is%s': Base.Getter, '_baseIs%s': Base.Getter} @classmethod def _applyTesterDefinitionDictOnAccessorHolder(cls, property_dict, accessor_holder): """ Apply testers and boolean accessors for the given property on the given accessor holder. @param property_dict: Property to generate getter for @type property_dict: dict @param accessor_holder: Accessor holder to applied the accessors on @type accessor_holder: Products.ERP5Type.dynamic.accessor_holder.AccessorHolderType @see _applyGetterDefinitionDictOnAccessorHolder """ tester_argument_list = (property_dict['elementary_type'], property_dict['storage_id']) cls._applyDefinitionFormatDictOnAccessorHolder( property_dict['reference'], cls._tester_definition_dict, accessor_holder, tester_argument_list, property_dict['read_permission']) boolean_argument_list = (property_dict['elementary_type'], property_dict['property_default'], property_dict['storage_id']) cls._applyDefinitionFormatDictOnAccessorHolder( property_dict['reference'], cls._boolean_definition_dict, accessor_holder, boolean_argument_list, property_dict['read_permission']) _translated_getter_definition_dict = { 'get%s': Translation.TranslatedPropertyGetter, '_baseGet%s': Translation.TranslatedPropertyGetter } security.declareProtected(Permissions.ModifyPortalContent, 'applyDefinitionOnAccessorHolder') @classmethod def applyDefinitionOnAccessorHolder(cls, property_dict, accessor_holder, portal, do_register=True): """ Apply getters, setters and testers for a list property or a primitive property. This class method may be called to apply a property dictionnary on an accessor holder. While applyOnAccessorHolder is commonly used to apply a property on an *existing* ZODB Property Sheet, this method can be used to apply accessors from a Property not defined in a ZODB Property Sheet. The property dictionnary must define all the attributes listed in the class docstring. The TALES Expression in the given property dict are considered to have been already evaluated, as performed through applyOnAccessorHolder by asDict method. @param property_dict: Property to generate getter for @type property_dict: dict @param accessor_holder: Accessor holder to applied the accessors on @type accessor_holder: Products.ERP5Type.dynamic.accessor_holder.AccessorHolderType @param portal: Portal object @type portal: Products.ERP5.ERP5Site.ERP5Site @param do_register: Register the property in the Zope property map @type do_register: bool """ # Some attributes are required to generate accessors, if they have # not been set properly, then don't generate them at all for this # Property if property_dict['reference'] is None or \ property_dict['elementary_type'] is None or \ property_dict['elementary_type'] not in type_definition: raise ValueError("Invalid type or reference") # Create range accessors if relevant if property_dict['range']: for kind in ('min', 'max'): cls._applyRangeOnAccessorHolder(property_dict.copy(), accessor_holder, kind, portal) # Create translation accessors if relevant if property_dict['translatable']: translated_property_dict = property_dict.copy() if translated_property_dict['property_default'] is None: translated_property_dict['property_default'] = '' # Make accessors such as getTranslatedProperty translated_reference = 'translated_' + property_dict['reference'] argument_list = (translated_property_dict['reference'], translated_property_dict['elementary_type'], None, translated_property_dict['property_default']) cls._applyDefinitionFormatDictOnAccessorHolder( translated_reference, cls._translated_getter_definition_dict, accessor_holder, argument_list, translated_property_dict['read_permission']) cls._applyTranslationLanguageOnAccessorHolder( translated_property_dict, accessor_holder, portal) # make accessor to translation_domain # first create default one as a normal property translation_domain_reference = translated_property_dict['reference'] + \ '_translation_domain' translation_domain_property_dict = { 'reference': translation_domain_reference, 'elementary_type': 'string', 'property_default': '', 'multivalued': False, 'storage_id': None, 'range': False, 'translatable': False, 'read_permission': translated_property_dict['read_permission'], 'write_permission': translated_property_dict['write_permission'] } # This will always be a StandardProperty, so avoid calling # super() here StandardProperty.applyDefinitionOnAccessorHolder( translation_domain_property_dict, accessor_holder, portal, do_register=False) # Then override getPropertyTranslationDomain accessor accessor = Translation.PropertyTranslationDomainGetter( 'get' + UpperCase(translation_domain_reference), translation_domain_reference, 'string', property_dict['translation_domain']) accessor_holder.registerAccessor( accessor, translated_property_dict['read_permission']) # After applying specific getters, setters and testers, apply # common getters, setters and testers cls._applyGetterDefinitionDictOnAccessorHolder(property_dict, accessor_holder) cls._applySetterDefinitionDictOnAccessorHolder(property_dict, accessor_holder) cls._applyTesterDefinitionDictOnAccessorHolder(property_dict, accessor_holder) # By default, register the property as a Zope property map, by # adding it to _properties, which will be later used by # PropertyManager if do_register: property_map = cls._asPropertyMap(property_dict) if property_map: accessor_holder._properties.append(property_map) security.declareProtected(Permissions.AccessContentsInformation, 'asDict') def asDict(self, expression_context=None): """ Convert the current property to a dict, which is then applied on the accessor holder. @param expression_context: Expression context for TALES Expression @type expression_context: Products.PageTemplates.Expressions.ZopeContext @return: The current property as a dict @rtype: dict """ # If no expression context has been given, create one, meaningful # when being called from the browser for example if expression_context is None: expression_context = createExpressionContext( self.getPortalObject()) property_default = evaluateExpressionFromString( expression_context, self.getPropertyDefault()) return { 'reference': self.getReference(), 'description': self.getDescription(), 'elementary_type': self.getElementaryType(), 'storage_id': self.getStorageId(), 'multivalued': self.getMultivalued(), 'property_default': property_default, 'range': self.getRange(), 'preference': self.getPreference(), 'read_permission': self.getReadPermission(), 'write_permission': self.getWritePermission(), 'translatable': self.getTranslatable(), 'translation_domain': self.getTranslationDomain(), 'select_variable': self.getSelectVariable() } security.declareProtected(Permissions.ModifyPortalContent, 'applyOnAccessorHolder') def applyOnAccessorHolder(self, accessor_holder, expression_context, portal): """ Apply the ZODB Property to the given accessor holder @param accessor_holder: Accessor holder to applied the accessors on @type accessor_holder: Products.ERP5Type.dynamic.accessor_holder.AccessorHolderType @param expression_context: Expression context for TALES Expression @type expression_context: Products.PageTemplates.Expressions.ZopeContext @param portal: Portal object @type portal: Products.ERP5.ERP5Site.ERP5Site @see applyDefinitionOnAccessorHolder """ self.applyDefinitionOnAccessorHolder(self.asDict(expression_context), accessor_holder, portal) @classmethod def _convertFromFilesystemPropertyDict(cls, filesystem_property_dict): """ Convert a property dict coming from a Property Sheet on the filesystem to a ZODB property dict. This method is just kept for backward-compatibility with filesystem Property Sheets used before ZODB Property Sheets. @param filesystem_property_dict: Filesystem property definition @type filesystem_property_dict: dict @return: ZODB property definition @rtype: dict """ # Prepare a dictionnary of the ZODB property zodb_property_dict = {} for fs_property_name, value in filesystem_property_dict.iteritems(): # Convert filesystem property name to ZODB if necessary zodb_property_name = \ fs_property_name in cls._name_mapping_filesystem_to_zodb_dict and \ cls._name_mapping_filesystem_to_zodb_dict[fs_property_name] or \ fs_property_name # Convert existing TALES expression class or primitive type to a # TALES expression string if zodb_property_name in cls._expression_attribute_tuple: value = isinstance(value, Expression) and \ value.text or 'python: ' + repr(value) # set correctly the id by following naming conventions if zodb_property_name == 'id': value += cls.getIdAsReferenceSuffix() zodb_property_dict[zodb_property_name] = value return zodb_property_dict security.declareProtected(Permissions.ModifyPortalContent, 'importFromFilesystemDefinition') @classmethod def importFromFilesystemDefinition(cls, context, filesystem_property_dict): """ Create a new property on the given context from the given filesystem definition dict. This method is just kept for backward-compatibility with filesystem Property Sheets used before ZODB Property Sheets. @param context: Context to create the property in @type context: Products.ERP5Type.Core.PropertySheet @param filesystem_property_dict: Filesystem definition @param filesystem_property_dict: dict @return: The new Standard Property @rtype: StandardProperty """ return context.newContent( portal_type=cls.portal_type, **cls._convertFromFilesystemPropertyDict(filesystem_property_dict))
class WorkflowState(IdAsReferenceMixin("state_"), XMLObject): """ A ERP5 State. """ meta_type = 'ERP5 Workflow State' portal_type = 'Workflow State' add_permission = Permissions.AddPortalContent state_permission_role_list_dict = None security = ClassSecurityInfo() security.declareObjectProtected(Permissions.AccessContentsInformation) property_sheets = ( 'Base', 'XMLObject', 'CategoryCore', 'DublinCore', 'Reference', 'Comment', 'SortIndex', 'WorkflowState', ) def addPossibleTransition(self, tr_ref): possible_transition_list = self.getCategoryList() transition = self.getParentValue()._getOb('transition_' + tr_ref, None) if transition is not None: tr_path = 'destination/' + '/'.join( transition.getPath().split('/')[2:]) possible_transition_list.append(tr_path) self.setCategoryList(possible_transition_list) # XXX(PERF): hack to see Category Tool responsability in new workflow slowness security.declareProtected(Permissions.AccessContentsInformation, 'getDestinationList') def getDestinationList(self): """ this getter is redefined to improve performance: instead of getting all the transition objects from the destination list to then use their ids, extract the information from the string """ prefix_length = len('destination/') return [ path[prefix_length:] for path in self.getCategoryList() if path.startswith('destination/') ] security.declareProtected(Permissions.AccessContentsInformation, 'getDestinationIdList') def getDestinationIdList(self): """ this getter is redefined to improve performance: instead of getting all the transition objects from the destination list to then use their ids, extract the information from the string """ return [ path.split('/')[-1] for path in self.getCategoryList() if path.startswith('destination/') ] security.declareProtected(Permissions.AccessContentsInformation, 'getDestinationValueList') def getDestinationValueList(self): """ this getter is redefined to improve performance: instead of getting all the transition objects from the destination list to then use their ids, extract the information from the string """ parent = self.getParentValue() return [ parent._getOb(destination_id) for destination_id in self.getDestinationIdList() ] security.declareProtected(Permissions.ModifyPortalContent, 'setStatePermissionRoleListDict') def setStatePermissionRoleListDict(self, permission_roles): """ create a dict containing the state's permission (as key) and its associated role list (value) use a PersistentMapping so that the ZODB is updated when this dict is changed """ self.state_permission_role_list_dict = PersistentMapping( {k: tuple(v) for (k, v) in permission_roles.items()}) security.declareProtected(Permissions.AccessContentsInformation, 'getStatePermissionRoleListDict') def getStatePermissionRoleListDict(self): """ return the permission/roles dict """ if self.state_permission_role_list_dict is None: return {} return dict(self.state_permission_role_list_dict.items()) security.declareProtected(Permissions.ModifyPortalContent, 'setPermission') def setPermission(self, permission, roles, REQUEST=None): """ Set a permission for this State. """ if self.state_permission_role_list_dict is None: self.state_permission_role_list_dict = PersistentMapping() self.state_permission_role_list_dict[permission] = tuple(roles) security.declareProtected(Permissions.AccessContentsInformation, 'getAvailableTypeList') def getAvailableTypeList(self): """ This is a method specific to ERP5. This returns a list of state types, which are used for portal methods. """ return ('draft_order', 'planned_order', 'future_inventory', 'reserved_inventory', 'transit_inventory', 'current_inventory')
class ConstraintMixin(IdAsReferenceMixin('_constraint'), Predicate): """ Mixin Constraint implementation (only relevant for ZODB Property sheets, use Products.ERP5Type.Constraint instead for filesystem Property Sheets) relying on Predicate @todo: Add code to import constraints requiring a new TALES Expression field in predicate to be able to import 'condition' properly """ # Declarative security security = ClassSecurityInfo() security.declareObjectProtected(Permissions.AccessContentsInformation) # IDs of error messages defined in each Constraint, only used when # importing or exporting from/to filesystem Constraint _message_id_tuple = () __allow_access_to_unprotected_subobjects__ = 1 implements(IConstraint, ) property_sheets = (PropertySheet.SimpleItem, PropertySheet.Reference, PropertySheet.Predicate) def _getMessage(self, message_id): """ Get the message corresponding to this message_id. """ return getattr(self, 'get' + UpperCase(message_id))() def _generateError(self, obj, error_message, mapping={}): """ Generic method used to generate error in checkConsistency. """ if error_message is not None: msg = ConsistencyMessage(self, object_relative_url=obj.getRelativeUrl(), message=error_message, mapping=mapping) return msg security.declareProtected(Permissions.AccessContentsInformation, 'checkConsistency') def checkConsistency(self, obj, fixit=0, **kw): """ Check the pre-condition before checking the consistency. _checkConsistency() must be define in the child class. """ if not self.test(obj): return [] return self._checkConsistency(obj, fixit, **kw) security.declareProtected(Permissions.AccessContentsInformation, 'fixConsistency') def fixConsistency(self, obj, **kw): """ Default method is to call checkConsistency with fixit set to 1 """ return self.checkConsistency(obj, fixit=1, **kw) def _getExpressionValue(self, obj, expression_string): """ Get the Python value from an Expression string, but check before whether it is None as a getter may returns the default value which could be None """ if expression_string is None: return None return Expression(expression_string)(createExpressionContext(obj)) @staticmethod def _preConvertBaseFromFilesystemDefinition(filesystem_definition_dict): """ Call before actually converting the attributes common to all constraints """ return {} @staticmethod def _convertFromFilesystemDefinition(*args, **kw): """ Convert a filesystem property dict to a ZODB Property dict which will be given to newContent(). Only attributes specific to this constraint will be given as parameters, e.g. not the ones common to all constraints such as 'id', 'description', 'type' and 'condition' and error messages defined in '_message_id_tuple' class attribute. @see importFromFilesystemDefinition """ yield {} security.declareProtected(Permissions.AccessContentsInformation, 'importFromFilesystemDefinition') @classmethod def importFromFilesystemDefinition(cls, context, filesystem_definition_dict): """ Import the filesystem definition to a ZODB Constraint, without its condition as it is now a Predicate and has to be converted manually. Several ZODB Constraints may be created to handle Constraints such as Attribute Equality which defines an attribute name and its expected value, thus ending up creating one ZODB Constraint per attribute/expected value """ # Copy the dictionnary as it is going to be modified to remove all # the common properties in order to have a dictionnary containing # only properties specific to the current constraint filesystem_definition_copy_dict = filesystem_definition_dict.copy() # This dict only contains definition attributes common to *all* # ZODB Constraints base_constraint_definition_dict = \ cls._preConvertBaseFromFilesystemDefinition(filesystem_definition_copy_dict) base_constraint_definition_dict['portal_type'] = cls.portal_type base_constraint_definition_dict['id'] = \ filesystem_definition_copy_dict.pop('id') + \ cls.getIdAsReferenceSuffix() base_constraint_definition_dict['description'] = \ filesystem_definition_copy_dict.pop('description', '') if 'condition' in filesystem_definition_copy_dict: base_constraint_definition_dict['test_tales_expression'] = \ filesystem_definition_copy_dict.pop('condition') # The type is meaningless for ZODB Constraints as it is the portal # type itself filesystem_definition_copy_dict.pop('type') # Add specific error messages defined on the constraint document for message_name in cls._message_id_tuple: if message_name in filesystem_definition_copy_dict: base_constraint_definition_dict[message_name] = \ filesystem_definition_copy_dict.pop(message_name) # Call the method defined in the Constraint document which returns # N dictionnaries containing only attributes specific to the # Constraint constraint_definition_generator = \ cls._convertFromFilesystemDefinition(**filesystem_definition_copy_dict) # Create all the constraint in the current ZODB Property Sheet for constraint_definition_dict in constraint_definition_generator: constraint_definition_dict.update(base_constraint_definition_dict) context.newContent(**constraint_definition_dict) security.declareProtected(Permissions.AccessContentsInformation, 'applyOnAccessorHolder') def applyOnAccessorHolder(self, accessor_holder, expression_context, portal): # Do not use asContext. Temporary object generated by asContext may # contain persistent object. If persistent object is stored on memory like # this(accessor_holder is on-memory object and is kept beyond transaction) # then ConnectionStateError occurs after a long time. import copy constraint_definition = copy.deepcopy(self.aq_base) # note the relative url of this constraint to display it later in # checkConsistency messages constraint_definition.relative_url = self.getRelativeUrl() accessor_holder.constraints.append(constraint_definition)
class CategoryProperty(IdAsReferenceMixin('_category'), XMLObject): """ Define a Category Property Document for a ZODB Property Sheets """ meta_type = 'ERP5 Category Property' portal_type = 'Category Property' # Declarative security security = ClassSecurityInfo() security.declareObjectProtected(Permissions.AccessContentsInformation) property_sheets = (PropertySheet.SimpleItem, PropertySheet.Reference) security.declareProtected(Permissions.AccessContentsInformation, 'importFromFilesystemDefinition') @classmethod def importFromFilesystemDefinition(cls, context, category_name): """ Set the Reference from a filesystem definition of a property """ return context.newContent(portal_type=cls.portal_type, id=category_name + cls.getIdAsReferenceSuffix()) getter_definition_dict = { # normal accessors 'get%sList': Category.ListGetter, 'get%sSet': Category.SetGetter, 'get%sItemList': Category.ItemListGetter, 'getDefault%s': Category.DefaultGetter, 'get%s': Category.DefaultGetter, # value accessors 'get%sValueList': Value.ListGetter, 'get%sValueSet': Value.SetGetter, 'get%sTitleList': Value.TitleListGetter, 'get%sTitleSet': Value.TitleSetGetter, 'get%sTranslatedTitleList': Value.TranslatedTitleListGetter, 'get%sTranslatedTitleSet': Value.TranslatedTitleSetGetter, 'get%sReferenceList': Value.ReferenceListGetter, 'get%sReferenceSet': Value.ReferenceSetGetter, 'get%sShortTitleList': Value.ShortTitleListGetter, 'get%sShortTitleSet': Value.ShortTitleSetGetter, 'get%sIdList': Value.IdListGetter, 'get%sIdSet': Value.IdSetGetter, 'get%sLogicalPathList': Value.LogicalPathListGetter, 'get%sLogicalPathSet': Value.LogicalPathSetGetter, 'get%sUidList': Value.UidListGetter, 'get%sUidSet': Value.UidSetGetter, 'get%sPropertyList': Value.PropertyListGetter, 'get%sPropertySet': Value.PropertySetGetter, 'getDefault%sValue': Value.DefaultGetter, 'get%sValue': Value.DefaultGetter, 'getDefault%sTitle': Value.DefaultTitleGetter, 'get%sTitle': Value.DefaultTitleGetter, 'getDefault%sTranslatedTitle': Value.DefaultTranslatedTitleGetter, 'get%sTranslatedTitle': Value.DefaultTranslatedTitleGetter, 'getDefault%sReference': Value.DefaultReferenceGetter, 'get%sReference': Value.DefaultReferenceGetter, 'getDefault%sShortTitle': Value.DefaultShortTitleGetter, 'get%sShortTitle': Value.DefaultShortTitleGetter, 'getDefault%sUid': Value.DefaultUidGetter, 'get%sUid': Value.DefaultUidGetter, 'getDefault%sId': Value.DefaultIdGetter, 'get%sId': Value.DefaultIdGetter, 'getDefault%sTitleOrId': Value.DefaultTitleOrIdGetter, 'get%sTitleOrId': Value.DefaultTitleOrIdGetter, 'getDefault%sProperty': Value.DefaultPropertyGetter, 'get%sProperty': Value.DefaultPropertyGetter, 'getDefault%sLogicalPath': Value.DefaultLogicalPathGetter, 'get%sLogicalPath': Value.DefaultLogicalPathGetter, 'get%sTranslatedLogicalPath': Value.DefaultTranslatedLogicalPathGetter, } setter_definition_dict = { # setters '_set%s': Category.Setter, '_categorySet%s': Category.Setter, '_set%sList': Category.ListSetter, '_categorySet%sList': Category.ListSetter, '_setDefault%s': Category.DefaultSetter, '_categorySetDefault%s': Category.DefaultSetter, '_set%sSet': Category.SetSetter, '_categorySet%sSet': Category.SetSetter, '_set%sValue': Value.Setter, '_categorySet%sValue': Value.Setter, '_set%sValueList': Value.ListSetter, '_categorySet%sValueList': Value.ListSetter, '_set%sValueSet': Value.SetSetter, '_categorySet%sValueSet': Value.SetSetter, '_setDefault%sValue': Value.DefaultSetter, '_categorySetDefault%sValue': Value.DefaultSetter, # uid setters '_set%sUid': Value.UidSetter, '_categorySet%sUid': Value.UidSetter, '_set%sUidList': Value.UidListSetter, '_categorySet%sUidList': Value.UidListSetter, '_set%sUidSet': Value.UidSetSetter, '_categorySet%sUidSet': Value.UidSetSetter, '_setDefault%sUid': Value.UidDefaultSetter, '_categorySetDefault%sUid': Value.UidDefaultSetter, } @classmethod def applyDefinitionOnAccessorHolder(cls, accessor_holder, category_id, portal): try: cat_object = portal.portal_categories._getOb(category_id) except KeyError: if portal.hasObject('portal_categories'): LOG("ERP5Type.Core.CategoryProperty", WARNING, "Base Category %r is missing. Accessors can not be generated." % \ category_id) return except TypeError: # category_id is None raise ValueError("Invalid category reference") # Create free text accessors. # XXX These are only for backward compatibility. storage_id = None if category_id == 'group': storage_id = 'group' elif category_id == 'site': storage_id = 'location' property_dict = { 'reference': '%s_free_text' % category_id, 'elementary_type': 'text', 'property_default': '', 'multivalued': False, 'storage_id': storage_id, 'range': False, 'translatable': False, 'description': 'free text to specify %s' % category_id, 'read_permission': Permissions.AccessContentsInformation, 'write_permission': Permissions.ModifyPortalContent } StandardProperty.applyDefinitionOnAccessorHolder(property_dict, accessor_holder, portal, do_register=False) # Get read and write permission read_permission = Permissions.__dict__.get( cat_object.getReadPermission(), Permissions.AccessContentsInformation) write_permission = Permissions.__dict__.get( cat_object.getWritePermission(), Permissions.ModifyPortalContent) # Actually create accessors uppercase_category_id = UpperCase(category_id) # three special cases accessor = Category.Tester('has' + uppercase_category_id, category_id) accessor_holder.registerAccessor(accessor, read_permission) accessor_name = uppercase_category_id[0].lower( ) + uppercase_category_id[1:] accessor = Value.ListGetter(accessor_name + 'Values', category_id) accessor_holder.registerAccessor(accessor, read_permission) accessor = Value.IdListGetter(accessor_name + 'Ids', category_id) accessor_holder.registerAccessor(accessor, read_permission) # then getters for id_format, accessor_class in cls.getter_definition_dict.iteritems( ): accessor_name = id_format % uppercase_category_id # XXX getSourceReference/getDestinationReference are already generated by # 'source_reference' and 'destination_reference' standard properties. To # prevent name conflict, we don't generate them as a category accessor. if not (SOURCE_DESTINATION_REFERENCE_LEGACY and accessor_name in ('getSourceReference', 'getDestinationReference')): public_accessor = accessor_class(accessor_name, category_id) accessor_holder.registerAccessor(public_accessor, read_permission) # create the private getter on the fly instead of having a definition dict # that's twice the size for the same info accessor_name = '_category' + accessor_name[0].upper( ) + accessor_name[1:] private_accessor = accessor_class(accessor_name, category_id) accessor_holder.registerAccessor(private_accessor, read_permission) # and setters for id_format, accessor_class in cls.setter_definition_dict.iteritems( ): accessor_name = id_format % uppercase_category_id # XXX setSourceReference/setDestinationReference are already generated by # 'source_reference' and 'destination_reference' standard properties. To # prevent name conflict, we don't generate them as a category accessor. if SOURCE_DESTINATION_REFERENCE_LEGACY and accessor_name in ( 'setSourceReference', 'setDestinationReference'): continue accessor = accessor_class(accessor_name, category_id) accessor_holder.registerAccessor(accessor, write_permission) # TODO: merge with StandardProperty if accessor_name.startswith('_set'): accessor = Alias.Reindex(accessor_name[1:], accessor_name) accessor_holder.registerAccessor(accessor, write_permission) # Only add the category ID if it is not already in _categories, # which may happen when getting the categories with acquisition if category_id not in accessor_holder._categories: accessor_holder._categories.append(category_id) security.declareProtected(Permissions.AccessContentsInformation, 'applyOnAccessorHolder') def applyOnAccessorHolder(self, accessor_holder, expression_context, portal): self.applyDefinitionOnAccessorHolder(accessor_holder, self.getReference(), portal)
class WorkflowTransition(IdAsReferenceMixin("transition_"), XMLObject, GuardableMixin): """ A ERP5 Transition. """ meta_type = 'ERP5 Workflow Transition' portal_type = 'Workflow Transition' add_permission = Permissions.AddPortalContent security = ClassSecurityInfo() security.declareObjectProtected(Permissions.AccessContentsInformation) property_sheets = ( 'Base', 'XMLObject', 'CategoryCore', 'DublinCore', 'Reference', 'Comment', 'SortIndex', 'WorkflowTransition', 'Guard', 'ActionInformation', ) # following getters are redefined for performance improvements # they use the categories paths directly and string operations # instead of traversing from the portal to get the objects # in order to have their id or value # XXX(PERF): hack to see Category Tool responsability in new workflow slowness security.declareProtected(Permissions.AccessContentsInformation, 'getActionType') def getActionType(self): for path in self.getCategoryList(): if path.startswith('action_type/'): return path[12:] # 12 is len('action_type/') return None security.declareProtected(Permissions.AccessContentsInformation, 'getBeforeScriptList') def getBeforeScriptList(self): """ returns the list of before script """ prefix_length = len('before_script/') return [ path[prefix_length:] for path in self.getCategoryList() if path.startswith('before_script/') ] security.declareProtected(Permissions.AccessContentsInformation, 'getAfterScriptList') def getAfterScriptList(self): """ returns the list of after script """ prefix_length = len('after_script/') return [ path[prefix_length:] for path in self.getCategoryList() if path.startswith('after_script/') ] security.declareProtected(Permissions.AccessContentsInformation, 'getBeforeScriptIdList') def getBeforeScriptIdList(self): """ returns the list of before script ids """ return [path.split('/')[-1] for path in self.getBeforeScriptList()] security.declareProtected(Permissions.AccessContentsInformation, 'getBeforeScriptValueList') def getBeforeScriptValueList(self): """ returns the list of before script values """ parent = self.getParentValue() return [ parent._getOb(transition_id) for transition_id in self.getBeforeScriptIdList() ] security.declareProtected(Permissions.AccessContentsInformation, 'getAfterScriptIdList') def getAfterScriptIdList(self): """ returns the list of after script ids """ return [path.split('/')[-1] for path in self.getAfterScriptList()] security.declareProtected(Permissions.AccessContentsInformation, 'getAfterScriptValueList') def getAfterScriptValueList(self): """ returns the list of after script values """ parent = self.getParentValue() return [ parent._getOb(transition_id) for transition_id in self.getAfterScriptIdList() ] security.declareProtected(Permissions.AccessContentsInformation, 'getDestinationValue') def getDestinationValue(self): """ returns the destination object """ destination_path_list = [ path for path in self.getCategoryList() if path.startswith('destination/') ] if destination_path_list: destination_id = destination_path_list[0].split('/')[-1] parent = self.getParentValue() return parent._getOb(destination_id) return None security.declareProtected(Permissions.AccessContentsInformation, 'getTransitionVariableValueList') def getTransitionVariableValueList(self): """ Return Transition Variables """ return self.objectValues(portal_type='Workflow Transition Variable')
class InteractionWorkflowInteraction(IdAsReferenceMixin('interaction_'), XMLObject, GuardableMixin): """ An ERP5 Workflow Interaction (Interaction Workflow) """ meta_type = 'ERP5 Interaction' portal_type = 'Interaction Workflow Interaction' add_permission = Permissions.AddPortalContent security = ClassSecurityInfo() security.declareObjectProtected(Permissions.AccessContentsInformation) # Declarative properties property_sheets = ( 'Base', 'XMLObject', 'CategoryCore', 'DublinCore', 'Reference', 'InteractionWorkflowInteraction', 'Guard', ) # following getters are redefined for performance improvements # they use the categories paths directly and string operations # instead of traversing from the portal to get the objects # in order to have their id or value security.declareProtected(Permissions.AccessContentsInformation, 'getBeforeCommitScriptIdList') def getBeforeCommitScriptIdList(self): """ returns the list of before commit script ids """ return [path.split('/')[-1] for path in self.getBeforeCommitScriptList()] security.declareProtected(Permissions.AccessContentsInformation, 'getBeforeCommitScriptValueList') def getBeforeCommitScriptValueList(self): """ returns the list of before commit script values """ parent = self.getParentValue() return [parent._getOb(transition_id) for transition_id in self.getBeforeCommitScriptIdList()] security.declareProtected(Permissions.AccessContentsInformation, 'getActivateScriptIdList') def getActivateScriptIdList(self): """ returns the list of activate script ids """ return [path.split('/')[-1] for path in self.getActivateScriptList()] security.declareProtected(Permissions.AccessContentsInformation, 'getActivateScriptValueList') def getActivateScriptValueList(self): """ returns the list of activate script values """ parent = self.getParentValue() return [parent._getOb(transition_id) for transition_id in self.getActivateScriptIdList()] # XXX(PERF): hack to see Category Tool responsability in new workflow slowness security.declareProtected(Permissions.AccessContentsInformation, 'getActivateScriptList') def getActivateScriptList(self): """ returns the list of activate script """ prefix_length = len('activate_script/') return [path[prefix_length:] for path in self.getCategoryList() if path.startswith('activate_script/')] # XXX(PERF): hack to see Category Tool responsability in new workflow slowness security.declareProtected(Permissions.AccessContentsInformation, 'getBeforeCommitScriptList') def getBeforeCommitScriptList(self): """ returns the list of before commit script """ prefix_length = len('before_commit_script/') return [path[prefix_length:] for path in self.getCategoryList() if path.startswith('before_commit_script/')]