[Erp5-report] r18364 - in /erp5/trunk/products/ERP5Type: Constraint/ Interface/ tests/
nobody at svn.erp5.org
nobody at svn.erp5.org
Mon Dec 17 17:19:36 CET 2007
Author: jerome
Date: Mon Dec 17 17:19:35 2007
New Revision: 18364
URL: http://svn.erp5.org?rev=18364&view=rev
Log:
Change the way messages are generated in constraints: possible messages in
each constraint class are predefined on the class and constraint users can
override the message in the propertysheet.
The motivation is to be able to provide user friendly context dependant
messages, eg. "Please Enter the Shipping Date" instead of technical messages
like "Property start_date is not defined".
See test_OverrideMessage for an example use, and the interface for more info.
(Also add a new CategoryAcquiredExistence constraint)
Modified:
erp5/trunk/products/ERP5Type/Constraint/AttributeEquality.py
erp5/trunk/products/ERP5Type/Constraint/CategoryExistence.py
erp5/trunk/products/ERP5Type/Constraint/CategoryMembershipArity.py
erp5/trunk/products/ERP5Type/Constraint/Constraint.py
erp5/trunk/products/ERP5Type/Constraint/ContentExistence.py
erp5/trunk/products/ERP5Type/Constraint/PortalTypeClass.py
erp5/trunk/products/ERP5Type/Constraint/PropertyExistence.py
erp5/trunk/products/ERP5Type/Constraint/PropertyTypeValidity.py
erp5/trunk/products/ERP5Type/Constraint/StringAttributeMatch.py
erp5/trunk/products/ERP5Type/Constraint/TALESConstraint.py
erp5/trunk/products/ERP5Type/Constraint/__init__.py
erp5/trunk/products/ERP5Type/Interface/Constraint.py
erp5/trunk/products/ERP5Type/tests/testConstraint.py
Modified: erp5/trunk/products/ERP5Type/Constraint/AttributeEquality.py
URL: http://svn.erp5.org/erp5/trunk/products/ERP5Type/Constraint/AttributeEquality.py?rev=18364&r1=18363&r2=18364&view=diff
==============================================================================
--- erp5/trunk/products/ERP5Type/Constraint/AttributeEquality.py (original)
+++ erp5/trunk/products/ERP5Type/Constraint/AttributeEquality.py Mon Dec 17 17:19:35 2007
@@ -43,6 +43,13 @@
},
"""
+ _message_id_list = ['message_invalid_attribute_value',
+ 'message_invalid_attribute_value_fixed']
+ message_invalid_attribute_value = "Attribute ${attribute_name} "\
+ "value is ${current_value} but should be ${expected_value}"
+ message_invalid_attribute_value_fixed = "Attribute ${attribute_name} "\
+ "value is ${current_value} but should be ${expected_value} (Fixed)"
+
def checkConsistency(self, obj, fixit=0):
"""
This is the check method, we return a list of string,
@@ -54,9 +61,9 @@
return []
errors = PropertyExistence.checkConsistency(self, obj, fixit=fixit)
for attribute_name, expected_value in self.constraint_definition.items():
- error_message = None
+ message_id = None
mapping = dict()
- # If property does not exist, error will be raise by
+ # If property does not exist, error will be raised by
# PropertyExistence Constraint.
if obj.hasProperty(attribute_name):
identical = 1
@@ -73,18 +80,16 @@
# Other type
identical = (expected_value == obj.getProperty(attribute_name))
if not identical:
- # Generate error_message
- error_message = "Attribute ${attribute_name} value is "\
- "${current_value} but should be ${expected_value}"
+ message_id = 'message_invalid_attribute_value'
mapping(attribute_name=attribute_name,
attribute_value=obj.getProperty(attribute_name),
expected_value=expected_value)
# Generate error
- if error_message is not None:
+ if message_id is not None:
if fixit:
obj._setProperty(attribute_name, expected_value)
- error_message = "Attribute ${attribute_name} value is "\
- "${current_value} but should be ${expected_value} (Fixed)"
- errors.append(self._generateError(obj, error_message, mapping))
+ message_id = 'message_invalid_attribute_value_fixed'
+ errors.append(self._generateError(obj,
+ self._getMessage(message_id), mapping))
return errors
Modified: erp5/trunk/products/ERP5Type/Constraint/CategoryExistence.py
URL: http://svn.erp5.org/erp5/trunk/products/ERP5Type/Constraint/CategoryExistence.py?rev=18364&r1=18363&r2=18364&view=diff
==============================================================================
--- erp5/trunk/products/ERP5Type/Constraint/CategoryExistence.py (original)
+++ erp5/trunk/products/ERP5Type/Constraint/CategoryExistence.py Mon Dec 17 17:19:35 2007
@@ -30,18 +30,30 @@
from Constraint import Constraint
class CategoryExistence(Constraint):
- """
- This method check and fix if an object respects the existence of
- a category.
+ """This constraint checks if an object respects the existence of
+ a category, without acquisition.
+
Configuration example:
{ 'id' : 'category_existence',
'description' : 'Category causality must be defined',
'type' : 'CategoryExistence',
- 'portal_type' : ('Person', 'Organisation')
+ 'portal_type' : ('Person', 'Organisation'),
'causality' : None,
'condition' : 'python: object.getPortalType() == 'Foo',
},
"""
+
+ _message_id_list = [ 'message_category_not_set',
+ 'message_category_not_associated_with_portal_type' ]
+ message_category_not_set = "Category existence error for base"\
+ " category ${base_category}, this category is not defined"
+ message_category_not_associated_with_portal_type = "Category existence"\
+ " error for base category ${base_category}, this"\
+ " document has no such category"
+
+ def _calculateArity(self, obj, base_category, portal_type):
+ return len(obj.getCategoryMembershipList(base_category,
+ portal_type=portal_type))
def checkConsistency(self, obj, fixit=0):
"""
@@ -50,7 +62,8 @@
"""
if not self._checkConstraintCondition(obj):
return []
- errors = []
+ error_list = []
+ portal_type = self.constraint_definition.get('portal_type', ())
# For each attribute name, we check if defined
for base_category in self.constraint_definition.keys():
if base_category in ('portal_type', ):
@@ -58,18 +71,36 @@
mapping = dict(base_category=base_category)
# Check existence of base category
if base_category not in obj.getBaseCategoryList():
- error_message = "Category existence error for base category "\
- "${base_category}, this document has no such category"
- elif len(obj.getCategoryMembershipList(base_category,
- portal_type = self.constraint_definition\
- .get('portal_type', ()))) == 0:
- error_message = "Category existence error for base category "\
- "${base_category}, this category is not defined"
+ error_message = 'message_category_not_associated_with_portal_type'
+ elif self._calculateArity(obj, base_category, portal_type) == 0:
+ error_message = 'message_category_not_set'
+ if base_category == 'destination_section':
+ import pdb; pdb.set_trace()
else:
error_message = None
# Raise error
if error_message:
- errors.append(self._generateError(obj, error_message, mapping))
- return errors
+ error_list.append(self._generateError(obj,
+ self._getMessage(error_message), mapping))
+ return error_list
+
+class CategoryAcquiredExistence(CategoryExistence):
+ """This constraint check an object respects the existence of a category, with
+ acquisition.
+
+ Configuration example:
+ { 'id' : 'category_existence',
+ 'description' : 'Category causality must be defined',
+ 'type' : 'CategoryExistence',
+ 'portal_type' : ('Person', 'Organisation'),
+ 'causality' : None,
+ 'condition' : 'python: object.getPortalType() == 'Foo',
+ },
+ """
+
+ def _calculateArity(self, obj, base_category, portal_type):
+ return len(obj.getAcquiredCategoryMembershipList(base_category,
+ portal_type=portal_type))
+
Modified: erp5/trunk/products/ERP5Type/Constraint/CategoryMembershipArity.py
URL: http://svn.erp5.org/erp5/trunk/products/ERP5Type/Constraint/CategoryMembershipArity.py?rev=18364&r1=18363&r2=18364&view=diff
==============================================================================
--- erp5/trunk/products/ERP5Type/Constraint/CategoryMembershipArity.py (original)
+++ erp5/trunk/products/ERP5Type/Constraint/CategoryMembershipArity.py Mon Dec 17 17:19:35 2007
@@ -45,6 +45,26 @@
'condition' : 'python: object.getPortalType() == 'Foo',
},
"""
+ _message_id_list = ['message_arity_too_small',
+ 'message_arity_not_in_range',
+ 'message_arity_with_portal_type_to_small',
+ 'message_arity_with_portal_type_not_in_range']
+
+ message_arity_too_small = "Arity Error for Relation ${base_category}"\
+ ", arity is equal to ${current_arity} but "\
+ "should be at least ${min_arity}"
+ message_arity_not_in_range = "Arity Error for Relation ${base_category}"\
+ ", arity is equal to ${current_arity} but "\
+ "should be between ${min_arity} and ${max_arity}"
+ message_arity_with_portal_type_to_small = "Arity Error for Relation"\
+ " ${base_category} and Type ${portal_type}"\
+ ", arity is equal to ${current_arity} but "\
+ "should be at least ${min_arity}"
+
+ message_arity_with_portal_type_not_in_range = "Arity Error for Relation"\
+ " ${base_category} and Type ${portal_type}"\
+ ", arity is equal to ${current_arity} but "\
+ "should be between ${min_arity} and ${max_arity}"
def _calculateArity(self, obj):
base_category = self.constraint_definition['base_category']
@@ -62,7 +82,7 @@
"""
if not self._checkConstraintCondition(obj):
return []
- errors = []
+ error_list = []
# Retrieve configuration values from PropertySheet (_constraints)
base_category = self.constraint_definition['base_category']
min_arity = int(self.constraint_definition['min_arity'])
@@ -82,25 +102,16 @@
# Generate error message
if portal_type is not ():
if max_arity is None:
- error_message = "Arity Error for Relation ${base_category}"\
- " and Type ${portal_type}"\
- ", arity is equal to ${current_arity} but "\
- "should be at least ${min_arity}"
+ message_id = 'message_arity_with_portal_type_to_small'
else:
- error_message = "Arity Error for Relation ${base_category}"\
- " and Type ${portal_type}"\
- ", arity is equal to ${current_arity} but "\
- "should be between ${min_arity} and ${max_arity}"
+ message_id = 'message_arity_with_portal_type_not_in_range'
else:
if max_arity is None:
- error_message = "Arity Error for Relation ${base_category}"\
- ", arity is equal to ${current_arity} but "\
- "should be at least ${min_arity}"
+ message_id = 'message_arity_too_small'
else:
- error_message = "Arity Error for Relation ${base_category}"\
- ", arity is equal to ${current_arity} but "\
- "should be between ${min_arity} and ${max_arity}"
+ message_id = 'message_arity_not_in_range'
# Add error
- errors.append(self._generateError(obj, error_message, mapping))
- return errors
+ error_list.append(self._generateError(obj,
+ self._getMessage(message_id), mapping))
+ return error_list
Modified: erp5/trunk/products/ERP5Type/Constraint/Constraint.py
URL: http://svn.erp5.org/erp5/trunk/products/ERP5Type/Constraint/Constraint.py?rev=18364&r1=18363&r2=18364&view=diff
==============================================================================
--- erp5/trunk/products/ERP5Type/Constraint/Constraint.py (original)
+++ erp5/trunk/products/ERP5Type/Constraint/Constraint.py Mon Dec 17 17:19:35 2007
@@ -38,23 +38,22 @@
"""
__implements__ = (IConstraint, )
+ _message_id_list = []
+
def __init__(self, id=None, description=None, type=None,
condition=None, **constraint_definition):
- """
- Remove unwanted attributes from constraint definition and keep
- them as instance attributes
+ """Initialize a constraint.
"""
self.id = id
self.description = description
self.type = type
- self.condition = condition
- self.constraint_definition = constraint_definition
+ self.constraint_definition = dict()
+ self.message_id_dict = dict()
+ self.edit(id, description, type, condition, **constraint_definition)
- def edit(self, id=None, description=None, type=None,
- **constraint_definition):
- """
- Remove unwanted attributes from constraint definition and keep
- them as instance attributes
+ def edit(self, id=None, description=None, type=None, condition=None,
+ **constraint_definition):
+ """Edit the constraint instance.
"""
if id is not None:
self.id = id
@@ -62,11 +61,22 @@
self.description = description
if type is not None:
self.type = type
- self.constraint_definition.update(constraint_definition)
+ self.condition = condition
+ for key, value in constraint_definition.items():
+ if key in self._message_id_list:
+ self.message_id_dict[key] = value
+ else:
+ self.constraint_definition[key] = value
+ def _getMessage(self, message_id):
+ """Get the message corresponding to this message_id.
+ """
+ if message_id in self.message_id_dict:
+ return self.message_id_dict[message_id]
+ return getattr(self, message_id)
+
def _generateError(self, obj, error_message, mapping={}):
- """
- Generic method used to generate error in checkConsistency.
+ """Generic method used to generate error in checkConsistency.
"""
if error_message is not None:
msg = ConsistencyMessage(self, obj.getRelativeUrl(),
Modified: erp5/trunk/products/ERP5Type/Constraint/ContentExistence.py
URL: http://svn.erp5.org/erp5/trunk/products/ERP5Type/Constraint/ContentExistence.py?rev=18364&r1=18363&r2=18364&view=diff
==============================================================================
--- erp5/trunk/products/ERP5Type/Constraint/ContentExistence.py (original)
+++ erp5/trunk/products/ERP5Type/Constraint/ContentExistence.py Mon Dec 17 17:19:35 2007
@@ -40,6 +40,13 @@
},
"""
+ _message_id_list = [ 'message_no_subobject',
+ 'message_no_subobject_portal_type' ]
+
+ message_no_subobject = "The document does not contain any subobject"
+ message_no_subobject_portal_type = "The document does not contain any"\
+ " subobject of portal portal type ${portal_type}"
+
def checkConsistency(self, object, fixit=0):
"""
This is the check method, we return a list of string,
@@ -48,22 +55,23 @@
"""
from Products.ERP5Type.Message import Message
obj = object
- errors = []
+ error_list = []
if self._checkConstraintCondition(object):
# Retrieve configuration values from PropertySheet (_constraints)
portal_type = self.constraint_definition.get('portal_type', ())
if not len(obj.contentValues(portal_type=portal_type)):
# Generate error message
mapping = {}
- error_message = "The document does not contain any subobject"
+ message_id = 'message_no_subobject'
if portal_type is not ():
- error_message += " of portal type ${portal_type}"
+ message_id = 'message_no_subobject_portal_type'
# XXX maybe this could be factored out
if isinstance(portal_type, basestring):
portal_type = (portal_type, )
mapping['portal_type'] = str(Message('erp5_ui', ' or ')).join(
[str(Message('erp5_ui', pt)) for pt in portal_type])
# Add error
- errors.append(self._generateError(obj, error_message, mapping))
- return errors
+ error_list.append(self._generateError(obj,
+ self._getMessage(message_id), mapping))
+ return error_list
Modified: erp5/trunk/products/ERP5Type/Constraint/PortalTypeClass.py
URL: http://svn.erp5.org/erp5/trunk/products/ERP5Type/Constraint/PortalTypeClass.py?rev=18364&r1=18363&r2=18364&view=diff
==============================================================================
--- erp5/trunk/products/ERP5Type/Constraint/PortalTypeClass.py (original)
+++ erp5/trunk/products/ERP5Type/Constraint/PortalTypeClass.py Mon Dec 17 17:19:35 2007
@@ -47,6 +47,19 @@
},
"""
+ _message_id_list = [ 'message_type_not_registred',
+ 'message_inconsistent_meta_type',
+ 'message_inconsistent_class' ]
+
+ message_type_not_registred = "Type Information ${type_name} not "\
+ "registred with the TypeTool"
+ message_inconsistent_meta_type = "Meta type is inconsistant with portal"\
+ " type definition. Portal type meta type is ${portal_type_meta_type}"\
+ " class meta type is ${class_meta_type}"
+ message_inconsistent_class = "__class__ is inconsistant with portal type"\
+ " definition. Portal Type class is ${portal_type_class},"\
+ " document class is ${document_class}"
+
def checkConsistency(self, obj, fixit=0):
"""
This is the check method, we return a list of string,
@@ -54,32 +67,28 @@
"""
if not self._checkConstraintCondition(obj):
return []
- errors = []
+ error_list = []
types_tool = getToolByName(obj, 'portal_types')
type_info = types_tool._getOb(obj.getPortalType(), None)
if type_info is None :
- errors.append(self._generateError(obj,
- "Type Information ${type_name} not registred with the TypeTool",
+ error_list.append(self._generateError(obj,
+ self._getMessage('message_type_not_registred'),
mapping=dict(type_name=obj.getPortalType())))
elif type_info.content_meta_type != obj.meta_type :
- errors.append(self._generateError(obj,
- "Meta type is inconsistant with portal type definition."\
- " Portal type meta type is ${portal_type_meta_type}"\
- " class meta type is ${class_meta_type} ",
+ error_list.append(self._generateError(obj,
+ self._getMessage('message_inconsistent_meta_type'),
mapping=dict(portal_type_meta_type=type_info.content_meta_type,
class_meta_type=obj.meta_type)))
else :
portal_type_class = self._getClassForPortalType(obj, type_info)
obj_class = str(obj.__class__)
if portal_type_class != obj_class :
- errors.append(self._generateError(obj,
- "__class__ is inconsistant with portal type definition."\
- " Portal Type class is ${portal_type_class},"
- " document class is ${document_class}",
+ error_list.append(self._generateError(obj,
+ self._getMessage('message_inconsistent_class'),
mapping=dict(portal_type_class=portal_type_class,
document_class=obj_class)))
# TODO fixit argument can be implemented here.
- return errors
+ return error_list
def _getClassForPortalType(self, obj, type_info):
Modified: erp5/trunk/products/ERP5Type/Constraint/PropertyExistence.py
URL: http://svn.erp5.org/erp5/trunk/products/ERP5Type/Constraint/PropertyExistence.py?rev=18364&r1=18363&r2=18364&view=diff
==============================================================================
--- erp5/trunk/products/ERP5Type/Constraint/PropertyExistence.py (original)
+++ erp5/trunk/products/ERP5Type/Constraint/PropertyExistence.py Mon Dec 17 17:19:35 2007
@@ -44,6 +44,13 @@
},
"""
+ _message_id_list = ['message_no_such_propery',
+ 'message_property_not_set']
+ message_no_such_propery = "Property existence error for property "\
+ "${property_id}, this document has no such property"
+ message_property_not_set = "Property existence error for property "\
+ "${property_id}, this property is not defined"
+
def checkConsistency(self, obj, fixit=0):
"""
This is the check method, we return a list of string,
@@ -51,23 +58,21 @@
"""
if not self._checkConstraintCondition(obj):
return []
- errors = []
+ error_list = []
# For each attribute name, we check if defined
for property_id in self.constraint_definition.keys():
# Check existence of property
mapping = dict(property_id=property_id)
if not obj.hasProperty(property_id):
- error_message = "Property existence error for property "\
- "${property_id}, this document has no such property"
+ error_message_id = "message_no_such_propery"
elif obj.getProperty(property_id) is None:
# If value is '', attribute is considered a defined
# XXX is this the default API ?
- error_message = "Property existence error for property "\
- "${property_id}, this property is not defined"
+ error_message_id = "message_property_not_set"
else:
- error_message = None
- # Return error
- error = self._generateError(obj, error_message, mapping)
- if error is not None:
- errors.append(error)
- return errors
+ error_message_id = None
+
+ if error_message_id:
+ error_list.append(self._generateError(obj,
+ self._getMessage(error_message_id), mapping))
+ return error_list
Modified: erp5/trunk/products/ERP5Type/Constraint/PropertyTypeValidity.py
URL: http://svn.erp5.org/erp5/trunk/products/ERP5Type/Constraint/PropertyTypeValidity.py?rev=18364&r1=18363&r2=18364&view=diff
==============================================================================
--- erp5/trunk/products/ERP5Type/Constraint/PropertyTypeValidity.py (original)
+++ erp5/trunk/products/ERP5Type/Constraint/PropertyTypeValidity.py Mon Dec 17 17:19:35 2007
@@ -62,12 +62,28 @@
# Properties of type eg. "object" can hold anything
_permissive_type_list = ('object', )
+ _message_id_list = [ 'message_unknown_type',
+ 'message_incorrect_type',
+ 'message_incorrect_type_fix_failed',
+ 'message_incorrect_type_fixed']
+
+ message_unknown_type = "Attribute ${attribute_name} is defined with"\
+ " an unknown type ${type_name}"
+ message_incorrect_type = "Attribute ${attribute_name}"\
+ " should be of type ${expected_type} but is of type ${actual_type} (Fixed)"
+ message_incorrect_type_fix_failed = "Attribute ${attribute_name}"\
+ " should be of type ${expected_type} but is of type ${actual_type}"\
+ " (Type cast failed with error ${type_cast_error})"
+ message_incorrect_type_fixed = "Attribute ${attribute_name}"\
+ " should be of type ${expected_type} but is of type ${actual_type} (Fixed)"
+
+
def checkConsistency(self, obj, fixit=0):
"""
This is the check method, we return a list of string,
each string corresponds to an error.
"""
- errors = []
+ error_list = []
# For each attribute name, we check type
for prop in obj.propertyMap():
property_id = prop['id']
@@ -88,16 +104,14 @@
wrong_type = not isinstance(value, self._type_dict[property_type])
except KeyError:
wrong_type = 0
- errors.append(self._generateError(obj,
- "Attribute ${attribute_name} is defined with "
- "an unknown type ${type_name}",
+ error_list.append(self._generateError(obj,
+ self._getMessage('message_unknown_type'),
mapping=dict(attribute_name=property_id,
type_name=property_type)))
if wrong_type:
# Type is wrong, so, raise constraint error
- error_message = "Attribute ${attribute_name} should be of type"\
- " ${expected_type} but is of type ${actual_type}"
+ error_message = 'message_incorrect_type'
mapping = dict(attribute_name=property_id,
expected_type=property_type,
actual_type=str(type(value)))
@@ -107,19 +121,17 @@
try:
value = self._type_dict[property_type][0](value)
except (KeyError, ValueError), error:
- error_message = "Attribute ${attribute_name} should be of type"\
- " ${expected_type} but is of type ${actual_type} (Type cast"\
- " failed with error ${type_cast_error}"
+ error_message = 'message_incorrect_type_fix_failed'
mapping['type_cast_error'] = str(error)
else:
obj.setProperty(property_id, value)
- error_message = "Attribute ${attribute_name} should be of type"\
- " ${expected_type} but is of type ${actual_type} (Fixed)"
+ error_message = 'message_incorrect_type_fixed'
- errors.append(self._generateError(obj, error_message, mapping))
+ error_list.append(self._generateError(obj,
+ self._getMessage(error_message), mapping))
elif fixit:
oldvalue = getattr(obj, property_id, value)
if oldvalue != value:
obj.setProperty(property_id, oldvalue)
- return errors
+ return error_list
Modified: erp5/trunk/products/ERP5Type/Constraint/StringAttributeMatch.py
URL: http://svn.erp5.org/erp5/trunk/products/ERP5Type/Constraint/StringAttributeMatch.py?rev=18364&r1=18363&r2=18364&view=diff
==============================================================================
--- erp5/trunk/products/ERP5Type/Constraint/StringAttributeMatch.py (original)
+++ erp5/trunk/products/ERP5Type/Constraint/StringAttributeMatch.py Mon Dec 17 17:19:35 2007
@@ -41,6 +41,12 @@
},
"""
+ _message_id_list = PropertyExistence._message_id_list +\
+ ['message_attribute_does_not_match']
+
+ message_attribute_does_not_match = "Attribute ${attribute_name} is "\
+ "${attribute_value} and does not match ${regular_expression}."
+
def checkConsistency(self, object, fixit=0):
"""
This is the check method, we return a list of string,
@@ -62,8 +68,7 @@
# Generate error
error_list.append(self._generateError(object,
- "Attribute ${attribute_name} is ${attribute_value} and"
- " does not match ${regular_expression}.",
+ self._getMessage('message_attribute_does_not_match'),
mapping=dict(attribute_name=attribute_name,
attribute_value=repr(current_value),
regular_expression=repr(regular_expression))))
Modified: erp5/trunk/products/ERP5Type/Constraint/TALESConstraint.py
URL: http://svn.erp5.org/erp5/trunk/products/ERP5Type/Constraint/TALESConstraint.py?rev=18364&r1=18363&r2=18364&view=diff
==============================================================================
--- erp5/trunk/products/ERP5Type/Constraint/TALESConstraint.py (original)
+++ erp5/trunk/products/ERP5Type/Constraint/TALESConstraint.py Mon Dec 17 17:19:35 2007
@@ -50,6 +50,13 @@
For readability, please don't abuse this constraint to evaluate complex
things. If necessary, write your own constraint class.
"""
+
+ _message_id_list = [ 'message_expression_false',
+ 'message_expression_error' ]
+
+ message_expression_false = "Expression was false"
+ message_expression_error = \
+ "Error while evaluating expression: ${error_text}"
def checkConsistency(self, obj, fixit=0):
"""See Interface """
@@ -57,20 +64,21 @@
from Products.ERP5Type.Utils import createExpressionContext
if not self._checkConstraintCondition(obj):
return []
- errors = []
+ error_list = []
expression_text = self.constraint_definition['expression']
expression = Expression(expression_text)
econtext = createExpressionContext(obj)
try:
if not expression(econtext):
- errors.append(self._generateError(obj, 'Expression was false'))
+ error_list.append(self._generateError(obj,
+ self._getMessage('message_expression_false')))
except (ConflictError, CompilerError):
raise
except Exception, e:
LOG('ERP5Type', PROBLEM, 'TALESConstraint error on "%s" on %s' %
(self.constraint_definition['expression'], obj), error=sys.exc_info())
- errors.append(self._generateError(obj,
- 'Error while evaluating expression: ${error_text}',
- mapping=dict(error_text=str(e))))
- return errors
+ error_list.append(self._generateError(obj,
+ self._getMessage('message_expression_error'),
+ mapping=dict(error_text=str(e))))
+ return error_list
Modified: erp5/trunk/products/ERP5Type/Constraint/__init__.py
URL: http://svn.erp5.org/erp5/trunk/products/ERP5Type/Constraint/__init__.py?rev=18364&r1=18363&r2=18364&view=diff
==============================================================================
--- erp5/trunk/products/ERP5Type/Constraint/__init__.py (original)
+++ erp5/trunk/products/ERP5Type/Constraint/__init__.py Mon Dec 17 17:19:35 2007
@@ -5,6 +5,7 @@
from AttributeEquality import AttributeEquality
from PropertyExistence import PropertyExistence
from CategoryExistence import CategoryExistence
+from CategoryExistence import CategoryAcquiredExistence
from PortalTypeClass import PortalTypeClass
from CategoryAcquiredMembershipArity import CategoryAcquiredMembershipArity
from TALESConstraint import TALESConstraint
Modified: erp5/trunk/products/ERP5Type/Interface/Constraint.py
URL: http://svn.erp5.org/erp5/trunk/products/ERP5Type/Interface/Constraint.py?rev=18364&r1=18363&r2=18364&view=diff
==============================================================================
--- erp5/trunk/products/ERP5Type/Interface/Constraint.py (original)
+++ erp5/trunk/products/ERP5Type/Interface/Constraint.py Mon Dec 17 17:19:35 2007
@@ -29,11 +29,8 @@
"""Constraint Interface.
"""
-try:
- from Interface import Interface
-except ImportError:
- # for Zope versions before 2.6.0
- from Interface import Base as Interface
+from Interface import Interface
+from Interface import Attribute
class Constraint(Interface):
"""ERP5 Constraints are classes that are in charge of checking wether an
@@ -60,12 +57,22 @@
# XXX condition is a TALES Expression; is it part of the API ?
# how to use condition based on a workflow state in a workflow before
- # script, where the document is not in that state yet ?
- 'condition': 'python: object.getPortalType() == "Foo"'
+ # script, where the document is not in that state yet ? /XXX
+
+ # You can add a condition, and this constraint will only be checked
+ # if the condition evaluates to a true value.
+ 'condition': 'python: object.getPortalType() == "Foo"',
# Additional Constraint parameters are configured here.
# Constraint docstring should provide a configuration example and a
# documentation on parameter they accept.
+
+ # Here is also the place where Constraint users may override message
+ # existing for this constraint. For instance, you can use a
+ # CategoryExistence constraint to check if a `source` property is
+ # defined, and return a nice "Please set the Supplier" (translated in
+ # the user language) as workflow validation failure message.
+ 'message_category_not_set': "Please set the Supplier",
}
)
@@ -78,12 +85,23 @@
recursivly.
"""
-
+
def checkConsistency(obj, fixit=0):
"""This method checks the consistency of object 'obj', and fix errors if
the argument 'fixit' is true. Not all constraint have to support error
- repairing, in that case, simply ignore the fixit parameter.
- This method should return a list of errors, which are a list for now.
+ repairing, in that case, simply ignore the fixit parameter. This method
+ should return a list of errors, which are a list of `ConsistencyMessage`,
+ with a `getTranslatedMessage` method for user interaction.
+ """
+
+ _message_id_list = Attribute("The list of messages IDs that can be "
+ "overriden for this constraint.")
+
+ def _getMessage(message_id):
+ """Returns the message for this message_id.
+
+ A message_id can be overriden in the property sheet using this constraint.
+ Default message values are defined in the constraint class.
"""
def _generateError(obj, error_message, mapping={}):
@@ -96,15 +114,28 @@
Then this message ("Something is wrong !") will be translated when the
caller of document.checkConsistency() calls getTranslatedMessage() on
- ConsistencyMessage instances returned by checkConsistency.
+ a ConsistencyMessage instance returned by checkConsistency.
+ Possible messages should be defined in constraint definition, in the list
+ _message_id_list, and a default message value should be defined as class
+ attribute.
+
+ In the example, you would have in the constraint class definition::
+
+ # list of existing messages
+ _message_id_list = ['message_something_wrong']
+ # messages default value
+ message_something_wrong = 'Something is wrong: ${what}'
+
+ We'll use _getMessage to get the corresponding message.
The implementation uses ERP5Type's Messages, so it's possible to use a
'mapping' for substitution, like this::
>>> if something_is_wrong:
>>> error_list.append(self._generateError(obj,
- ... 'Something is wrong: ${wrong_thing}',
- ... mapping=dict(wrong_thing=obj.getTheWrongThing())))
-
+ ... self._getMessage('message_something_wrong'),
+ ... mapping=dict(what=obj.getTheWrongThing())))
+
"""
+
Modified: erp5/trunk/products/ERP5Type/tests/testConstraint.py
URL: http://svn.erp5.org/erp5/trunk/products/ERP5Type/tests/testConstraint.py?rev=18364&r1=18363&r2=18364&view=diff
==============================================================================
--- erp5/trunk/products/ERP5Type/tests/testConstraint.py (original)
+++ erp5/trunk/products/ERP5Type/tests/testConstraint.py Mon Dec 17 17:19:35 2007
@@ -32,6 +32,7 @@
from Products.ERP5Type.tests.testERP5Type import PropertySheetTestCase
from AccessControl.SecurityManagement import newSecurityManager
from Products.ERP5Type.tests.Sequence import Sequence, SequenceList
+
class TestConstraint(PropertySheetTestCase):
@@ -1368,7 +1369,46 @@
sequence_list.play(self, quiet=quiet)
-
+ def test_RegisterWithPropertySheet(self):
+ # constraint are registred in property sheets
+ obj = self._makeOne()
+ obj.setTitle('b')
+ self._addPropertySheet(obj.getPortalType(),
+ '''class TestPropertySheet:
+ _constraints = (
+ { 'id': 'testing_constraint',
+ 'type': 'StringAttributeMatch',
+ 'title': 'a.*', },)
+ ''')
+ consistency_message_list = obj.checkConsistency()
+ self.assertEquals(1, len(consistency_message_list))
+ message = consistency_message_list[0]
+ from Products.ERP5Type.ConsistencyMessage import ConsistencyMessage
+ self.assertTrue(isinstance(message, ConsistencyMessage))
+ self.assertEquals(message.class_name, 'StringAttributeMatch')
+ obj.setTitle('a')
+ self.assertEquals(obj.checkConsistency(), [])
+
+ def test_OverrideMessage(self):
+ # messages can be overriden in property sheet
+ obj = self._makeOne()
+ obj.setTitle('b')
+ self._addPropertySheet(obj.getPortalType(),
+ '''class TestPropertySheet:
+ _constraints = (
+ { 'id': 'testing_constraint',
+ 'message_attribute_does_not_match':
+ 'Attribute ${attribute_name} does not match',
+ 'type': 'StringAttributeMatch',
+ 'title': 'a.*', },)
+ ''')
+ consistency_message_list = obj.checkConsistency()
+ self.assertEquals(1, len(consistency_message_list))
+ message = consistency_message_list[0]
+ self.assertEquals('Attribute title does not match',
+ str(message.getTranslatedMessage()))
+
+
def test_suite():
suite = unittest.TestSuite()
suite.addTest(unittest.makeSuite(TestConstraint))
More information about the Erp5-report
mailing list