Commit f9081640 authored by Arnaud Fontaine's avatar Arnaud Fontaine

* Allow import of Filesystem Constraints to ZODB Constraints for

  Property Sheets and export of ZODB Constraints to Filesystem
  Constraints.
* Merge CategoryAcquiredExistenceConstraint into CategoryExistenceConstraint
  and CategoryAcquiredMembershipArityConstraint into 
  CategoryMembershipArityConstraint.
* Fix constraint_portal_type for CategoryExistenceConstraint and
  CategoryMembershipArityConstraint for ZODB Constraints which must be
  a TALES Expression, like filesystem Constraints.
* Add tests to check import and export of ZODB Constraints.



git-svn-id: https://svn.erp5.org/repos/public/erp5/trunk@42929 20353a03-c40f-0410-a6d1-a30d3c3de9de
parent 611b7dcd
......@@ -32,7 +32,7 @@ from Products.ERP5Type import PropertySheet
class AccountingTransactionBalanceConstraint(ConstraintMixin):
"""
Check that accounting transaction total debit and total credit are equals.
Check that accounting transaction total debit and total credit are equal.
This is only relevant for ZODB Property Sheets (filesystem Property
Sheets rely on Products.ERP5.Constraint.AccountingTransactionBalance
......@@ -41,6 +41,8 @@ class AccountingTransactionBalanceConstraint(ConstraintMixin):
meta_type = 'ERP5 Accounting Transaction Balance Constraint'
portal_type = 'Accounting Transaction Balance Constraint'
__compatibility_class_name__ = 'AccountingTransactionBalance'
property_sheets = ConstraintMixin.property_sheets + \
(PropertySheet.AccountingTransactionBalanceConstraint,)
......@@ -89,3 +91,6 @@ class AccountingTransactionBalanceConstraint(ConstraintMixin):
break
return error_list
_message_id_tuple = ('message_transaction_not_balanced_for_source',
'message_transaction_not_balanced_for_destination')
......@@ -43,6 +43,8 @@ class ResourceMeasuresConsistencyConstraint(ConstraintMixin):
meta_type = 'ERP5 Resource Measures Consistency Constraint'
portal_type = 'Resource Measures Consistency Constraint'
__compatibility_class_name__ = 'ResourceMeasuresConsistency'
property_sheets = ConstraintMixin.property_sheets + \
(PropertySheet.ResourceMeasuresConsistencyConstraint,)
......@@ -103,3 +105,9 @@ class ResourceMeasuresConsistencyConstraint(ConstraintMixin):
error('message_missing_metric_type', metric_type=quantity)
return error_list
_message_id_tuple = ('message_measure_no_quantity_unit',
'message_measure_no_quantity',
'message_duplicate_metric_type',
'message_duplicate_default_measure',
'message_missing_metric_type')
......@@ -42,6 +42,8 @@ class TradeModelLineCellConsistencyConstraint(ConstraintMixin):
meta_type = 'ERP5 Trade Model Line Cell Consistency Constraint'
portal_type = 'Trade Model Line Cell Consistency Constraint'
__compatibility_class_name__ = 'TradeModelLineCellConsistency'
property_sheets = ConstraintMixin.property_sheets + \
(PropertySheet.TradeModelLineCellConsistencyConstraint,)
......@@ -57,3 +59,20 @@ class TradeModelLineCellConsistencyConstraint(ConstraintMixin):
mapping=dict(line=document.getTitle()))]
return []
_message_id_tuple = ('message_cell_inexistance',)
@staticmethod
def _convertFromFilesystemDefinition(base_id):
"""
@see ERP5Type.mixin.constraint.ConstraintMixin._convertFromFilesystemDefinition
"""
yield dict(base_id=base_id)
def exportToFilesystemDefinitionDict(self):
filesystem_definition_dict = super(TradeModelLineCellConsistencyConstraint,
self).exportToFilesystemDefinitionDict()
filesystem_definition_dict['base_id'] = self.getBaseId()
return filesystem_definition_dict
......@@ -41,6 +41,8 @@ class TransactionQuantityValueFeasabilityConstraint(ConstraintMixin):
meta_type = 'ERP5 Transaction Quantity Value Feasability Constraint'
portal_type = 'Transaction Quantity Value Feasability Constraint'
__compatibility_class_name__ = 'TransactionQuantityValueFeasability'
def _checkConsistency(self, object, fixit=0):
"""
Check if the quantity of the transaction is possible
......
......@@ -38,6 +38,8 @@ class TransactionQuantityValueValidityConstraint(ConstraintMixin):
meta_type = 'ERP5 Transaction Quantity Value Validity Constraint'
portal_type = 'Transaction Quantity Value Validity Constraint'
__compatibility_class_name__ = 'TransactionQuantityValueValidity'
def _checkConsistency(self, object, fixit=0):
"""
Check if the quantity of the transaction is greater than the
......
......@@ -35,6 +35,7 @@ from Products.ERP5Type.Core.PropertyExistenceConstraint import \
from Products.ERP5Type.mixin.constraint import ConstraintMixin
from AccessControl import ClassSecurityInfo
from Products.ERP5Type import Permissions, PropertySheet
from Products.CMFCore.Expression import Expression
class AttributeEqualityConstraint(PropertyExistenceConstraint):
"""
......@@ -58,6 +59,8 @@ class AttributeEqualityConstraint(PropertyExistenceConstraint):
meta_type = 'ERP5 Attribute Equality Constraint'
portal_type = 'Attribute Equality Constraint'
__compatibility_class_name__ = 'AttributeEquality'
# Declarative security
security = ClassSecurityInfo()
security.declareObjectProtected(Permissions.AccessContentsInformation)
......@@ -67,7 +70,7 @@ class AttributeEqualityConstraint(PropertyExistenceConstraint):
def _checkConsistency(self, obj, fixit=False):
"""
Check the object's consistency.
Check the object's consistency
"""
attribute_name = self.getConstraintAttributeName()
......@@ -113,3 +116,32 @@ class AttributeEqualityConstraint(PropertyExistenceConstraint):
return [error]
return []
_message_id_tuple = ('message_property_not_set',
'message_invalid_attribute_value',
'message_invalid_attribute_value_fixed')
@staticmethod
def _convertFromFilesystemDefinition(**property_dict):
"""
@see ERP5Type.mixin.constraint.ConstraintMixin._convertFromFilesystemDefinition
One constraint per property is created. Filesystem definition example:
{ 'id' : 'title',
'description' : 'Title must be "ObjectTitle"',
'type' : 'AttributeEquality',
'title' : 'ObjectTitle',
'condition' : 'python: object.getPortalType() == 'Foo',
}
"""
for name, value in property_dict.iteritems():
yield dict(name=value)
def exportToFilesystemDefinitionDict(self):
filesystem_definition_dict = super(AttributeEqualityConstraint,
self).exportToFilesystemDefinitionDict()
filesystem_definition_dict[self.getConstraintAttributeName()] = \
Expression(self.getConstraintAttributeValue())
return filesystem_definition_dict
##############################################################################
#
# Copyright (c) 2002-2010 Nexedi SARL and Contributors. All Rights Reserved.
# Sebastien Robin <seb@nexedi.com>
# Romain Courteaud <romain@nexedi.com>
# Arnaud Fontaine <arnaud.fontaine@nexedi.com>
#
# WARNING: This program as such is intended to be used by professional
# programmers who take the whole responsability of assessing all potential
# consequences resulting from its eventual inadequacies and bugs
# End users who are looking for a ready-to-use solution with commercial
# garantees and support are strongly adviced to contract a Free Software
# Service Company
#
# This program is Free Software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#
##############################################################################
from Products.ERP5Type.Core.CategoryMembershipArityConstraint \
import CategoryMembershipArityConstraint
class CategoryAcquiredMembershipArityConstraint(CategoryMembershipArityConstraint):
"""
This constraint checks if an object respects the arity with
Acquisition.
This is only relevant for ZODB Property Sheets (filesystem Property
Sheets rely on Products.ERP5Type.Constraint.CategoryAcquiredMembershipArity
instead).
For example, if we would like to check whether the object respects a
minimum arity of '1' and a maximum arity of '1 for the Portal Type
'Organisation' and the Base Category 'source', then we would create
a 'Category Acquired Membership Arity Constraint' within that
Property Sheet and set 'Minimum arity' to '1', 'Maximum Arity' to
'1', 'Portal Types' to 'Organisation', 'Base Categories' to
'source', then set the 'Predicate' if necessary (known as
'condition' for filesystem Property Sheets).
"""
meta_type = 'ERP5 Category Acquired Membership Arity Constraint'
portal_type = 'Category Acquired Membership Arity Constraint'
def _calculateArity(self, obj, base_category_list, portal_type_list):
return len(obj.getAcquiredCategoryMembershipList(
base_category_list, portal_type=portal_type_list))
......@@ -29,13 +29,15 @@
##############################################################################
from Products.ERP5Type.mixin.constraint import ConstraintMixin
from Products.CMFCore.Expression import Expression
from AccessControl import ClassSecurityInfo
from Products.ERP5Type import Permissions, PropertySheet
class CategoryExistenceConstraint(ConstraintMixin):
"""
This constraint checks whether a category has been defined on this
object (without acquisition).
object (with or without acquisition depending on use_acquisition
value).
This is only relevant for ZODB Property Sheets (filesystem Property
Sheets rely on Products.ERP5Type.Constraint.CategoryExistence
......@@ -44,6 +46,8 @@ class CategoryExistenceConstraint(ConstraintMixin):
meta_type = 'ERP5 Category Existence Constraint'
portal_type = 'Category Existence Constraint'
__compatibility_class_name__ = 'CategoryExistence'
# Declarative security
security = ClassSecurityInfo()
security.declareObjectProtected(Permissions.AccessContentsInformation)
......@@ -52,7 +56,12 @@ class CategoryExistenceConstraint(ConstraintMixin):
(PropertySheet.CategoryExistenceConstraint,)
def _calculateArity(self, obj, base_category, portal_type_list):
return len(obj.getCategoryMembershipList(base_category,
if self.getUseAcquisition():
category_membership_list_function = obj.getAcquiredCategoryMembershipList
else:
category_membership_list_function = obj.getCategoryMembershipList
return len(category_membership_list_function(base_category,
portal_type=portal_type_list))
def _checkConsistency(self, obj, fixit=0):
......@@ -81,13 +90,54 @@ class CategoryExistenceConstraint(ConstraintMixin):
return error_list
class CategoryAcquiredExistenceConstraint(CategoryExistenceConstraint):
_message_id_tuple = ('message_category_not_set',
'message_category_not_associated_with_portal_type')
@staticmethod
def _preConvertBaseFromFilesystemDefinition(filesystem_definition_dict):
"""
This constraint checks whether a category has been defined on this
object (with acquisition). This is only relevant for ZODB Property
Sheets (filesystem Property Sheets rely on
Products.ERP5Type.Constraint.CategoryExistence instead).
CategoryExistence and CategoryAcquiredExistence filesystem
Constraints have been merged into a single Document for ZODB
Constraint by adding 'use_acquisition' attribute
"""
def _calculateArity(self, obj, base_category, portal_type_list):
return len(obj.getAcquiredCategoryMembershipList(
base_category, portal_type=portal_type_list))
return dict(use_acquisition=(filesystem_definition_dict['type'] == \
'CategoryAcquiredExistence'))
@staticmethod
def _convertFromFilesystemDefinition(portal_type=(),
**base_category_dict):
"""
@see ERP5Type.mixin.constraint.ConstraintMixin._convertFromFilesystemDefinition
Filesystem definition example:
{ 'id' : 'category_existence',
'description' : 'Category causality must be defined',
'type' : 'CategoryExistence',
'portal_type' : ('Person', 'Organisation'),
'causality' : None,
'condition' : 'python: object.getPortalType() == 'Foo',
}
"""
constraint_portal_type_str = isinstance(portal_type, Expression) and \
portal_type.text or 'python: ' + repr(portal_type)
yield dict(constraint_base_category_list=base_category_dict.keys(),
constraint_portal_type=constraint_portal_type_str)
def exportToFilesystemDefinitionDict(self):
filesystem_definition_dict = super(CategoryExistenceConstraint,
self).exportToFilesystemDefinitionDict()
# There is only one ZODB Constraint class for filesystem
# Constraints CategoryExistence and CategoryAcquiredExistence
# constraints
if self.getUseAcquisition():
filesystem_definition_dict['type'] = 'CategoryAcquiredExistence'
filesystem_definition_dict['portal_type'] = \
Expression(self.getConstraintPortalType())
for category in self.getConstraintBaseCategoryList():
filesystem_definition_dict[category] = 1
return filesystem_definition_dict
......@@ -29,12 +29,14 @@
##############################################################################
from Products.ERP5Type.mixin.constraint import ConstraintMixin
from Products.CMFCore.Expression import Expression
from AccessControl import ClassSecurityInfo
from Products.ERP5Type import Permissions, PropertySheet
class CategoryMembershipArityConstraint(ConstraintMixin):
"""
This constraint checks if an object respects the arity.
This constraint checks if an object respects the arity (with or
without acquisition depending on use_acquisition value).
This is only relevant for ZODB Property Sheets (filesystem Property
Sheets rely on Products.ERP5Type.Constraint.CategoryMembershipArity
......@@ -52,6 +54,8 @@ class CategoryMembershipArityConstraint(ConstraintMixin):
meta_type = 'ERP5 Category Membership Arity Constraint'
portal_type = 'Category Membership Arity Constraint'
__compatibility_class_name__ = 'CategoryMembershipArity'
# Declarative security
security = ClassSecurityInfo()
security.declareObjectProtected(Permissions.AccessContentsInformation)
......@@ -102,3 +106,73 @@ class CategoryMembershipArityConstraint(ConstraintMixin):
return [self._generateError(obj,
self._getMessage(message_id),
mapping)]
_message_id_tuple = ('message_arity_too_small',
'message_arity_not_in_range',
'message_arity_with_portal_type_too_small',
'message_arity_with_portal_type_not_in_range')
@staticmethod
def _preConvertBaseFromFilesystemDefinition(filesystem_definition_dict):
"""
CategoryAcquiredMembershipArity and CategoryMembershipArity
filesystem Constraints have been merged into a single Document for
ZODB Constraint by adding 'use_acquisition' attribute
"""
return dict(use_acquisition=(filesystem_definition_dict['type'] == \
'CategoryAcquiredMembershipArity'))
@staticmethod
def _convertFromFilesystemDefinition(min_arity,
portal_type=(),
max_arity=None,
base_category=()):
"""
@see ERP5Type.mixin.constraint.ConstraintMixin._convertFromFilesystemDefinition
Filesystem definition example:
{ 'id' : 'source',
'description' : '',
'type' : 'CategoryMembershipArity',
'min_arity' : '1',
'max_arity' : '1',
'portal_type' : ('Organisation', ),
'base_category' : ('source',)
'condition' : 'python: object.getPortalType() == 'Foo',
}
"""
constraint_portal_type_str = isinstance(portal_type, Expression) and \
portal_type.text or 'python: ' + repr(portal_type)
zodb_property_dict = dict(
min_arity=int(min_arity),
constraint_portal_type=constraint_portal_type_str,
constraint_base_category_list=base_category)
if max_arity is not None:
zodb_property_dict['max_arity'] = int(max_arity)
yield zodb_property_dict
def exportToFilesystemDefinitionDict(self):
filesystem_definition_dict = super(CategoryMembershipArityConstraint,
self).exportToFilesystemDefinitionDict()
# There is only one ZODB Constraint class for filesystem
# Constraints CategoryMembershipArity and
# CategoryAcquiredMembershipArity constraints
if self.getUseAcquisition():
filesystem_definition_dict['type'] = 'CategoryAcquiredMembershipArity'
filesystem_definition_dict['min_arity'] = str(self.getMinArity())
if self.hasMaxArity():
filesystem_definition_dict['max_arity'] = str(self.getMaxArity())
filesystem_definition_dict['portal_type'] = \
Expression(self.getConstraintPortalType())
filesystem_definition_dict['base_category'] = self.getConstraintBaseCategoryList()
return filesystem_definition_dict
......@@ -57,6 +57,8 @@ class CategoryRelatedMembershipArityConstraint(CategoryMembershipArityConstraint
meta_type = 'ERP5 Category Related Membership Arity Constraint'
portal_type = 'Category Related Membership Arity Constraint'
__compatibility_class_name__ = 'CategoryRelatedMembershipArity'
property_sheets = CategoryMembershipArityConstraint.property_sheets + \
(PropertySheet.CategoryRelatedMembershipArityConstraint,)
......
......@@ -28,6 +28,7 @@
##############################################################################
from Products.ERP5Type.mixin.constraint import ConstraintMixin
from Products.CMFCore.Expression import Expression
from AccessControl import ClassSecurityInfo
from Products.ERP5Type import Permissions, PropertySheet
......@@ -53,6 +54,8 @@ class ContentExistenceConstraint(ConstraintMixin):
meta_type = 'ERP5 Content Existence Constraint'
portal_type = 'Content Existence Constraint'
__compatibility_class_name__ = 'ContentExistence'
# Declarative security
security = ClassSecurityInfo()
security.declareObjectProtected(Permissions.AccessContentsInformation)
......@@ -87,3 +90,32 @@ class ContentExistenceConstraint(ConstraintMixin):
return [self._generateError(obj,
self._getMessage(message_id),
mapping)]
_message_id_tuple = ('message_no_subobject',
'message_no_subobject_portal_type')
@staticmethod
def _convertFromFilesystemDefinition(portal_type=()):
"""
@see ERP5Type.mixin.constraint.ConstraintMixin._convertFromFilesystemDefinition
Filesystem definition example:
{ 'id' : 'line',
'description' : 'Object have to contain a Line',
'type' : 'ContentExistence',
'portal_type' : ('Line', ),
}
"""
constraint_portal_type_str = isinstance(portal_type, Expression) and \
portal_type.text or 'python: ' + repr(portal_type)
yield dict(constraint_portal_type=constraint_portal_type_str)
def exportToFilesystemDefinitionDict(self):
filesystem_definition_dict = super(ContentExistenceConstraint,
self).exportToFilesystemDefinitionDict()
filesystem_definition_dict['portal_type'] = \
Expression(self.getConstraintPortalType())
return filesystem_definition_dict
......@@ -50,6 +50,8 @@ class PropertyExistenceConstraint(ConstraintMixin):
meta_type = 'ERP5 Property Existence Constraint'
portal_type = 'Property Existence Constraint'
__compatibility_class_name__ = 'PropertyExistence'
# Declarative security
security = ClassSecurityInfo()
security.declareObjectProtected(Permissions.AccessContentsInformation)
......@@ -71,7 +73,7 @@ class PropertyExistenceConstraint(ConstraintMixin):
def _checkConsistency(self, obj, fixit=False):
"""
Check the object's consistency.
Check the object's consistency
"""
error_list = []
# For each attribute name, we check if defined
......@@ -83,3 +85,29 @@ class PropertyExistenceConstraint(ConstraintMixin):
dict(property_id=property_id)))
return error_list
_message_id_tuple = ('message_no_such_property',)
@staticmethod
def _convertFromFilesystemDefinition(**property_dict):
"""
@see ERP5Type.mixin.constraint.ConstraintMixin._convertFromFilesystemDefinition
Filesystem definition example:
{ 'id' : 'property_existence',
'description' : 'Property price must be defined',
'type' : 'PropertyExistence',
'price' : None,
'condition' : 'python: object.getPortalType() == 'Foo',
}
"""
yield dict(constraint_property_list=property_dict.keys())
def exportToFilesystemDefinitionDict(self):
filesystem_definition_dict = super(PropertyExistenceConstraint,
self).exportToFilesystemDefinitionDict()
for constraint_property in self.getConstraintPropertyList():
filesystem_definition_dict[constraint_property] = None
return filesystem_definition_dict
......@@ -44,6 +44,8 @@ class PropertyTypeValidityConstraint(ConstraintMixin):
meta_type = 'ERP5 Property Type Validity Constraint'
portal_type = 'Property Type Validity Constraint'
__compatibility_class_name__ = 'PropertyTypeValidity'
property_sheets = ConstraintMixin.property_sheets + \
(PropertySheet.PropertyTypeValidityConstraint,)
......@@ -133,3 +135,8 @@ class PropertyTypeValidityConstraint(ConstraintMixin):
obj.setProperty(property_id, oldvalue)
return error_list
_message_id_tuple = ('message_unknown_type',
'message_incorrect_type',
'message_incorrect_type_fix_failed',
'message_incorrect_type_fixed')
......@@ -61,6 +61,8 @@ class TALESConstraint(ConstraintMixin):
meta_type = 'ERP5 TALES Constraint'
portal_type = 'TALES Constraint'
__compatibility_class_name__ = 'TALESConstraint'
property_sheets = ConstraintMixin.property_sheets + \
(PropertySheet.TALESConstraint,)
......@@ -86,3 +88,18 @@ class TALESConstraint(ConstraintMixin):
mapping=dict(error=str(e)))]
return []
_message_id_tuple = ('message_expression_false',
'message_expression_error')
@staticmethod
def _convertFromFilesystemDefinition(expression):
yield dict(expression=expression)
def exportToFilesystemDefinitionDict(self):
filesystem_definition_dict = super(TALESConstraint,
self).exportToFilesystemDefinitionDict()
filesystem_definition_dict['expression'] = self.getExpression()
return filesystem_definition_dict
......@@ -78,6 +78,10 @@ class PropertySheetTool(BaseTool):
return 'Standard Property'
_merged_portal_type_dict = {
'CategoryAcquiredExistence': 'Category Existence Constraint',
'CategoryAcquiredMembershipArity': 'Category Membership Arity Constraint'}
security.declareProtected(Permissions.ModifyPortalContent,
'createPropertySheetFromFilesystemClass')
def createPropertySheetFromFilesystemClass(self, klass):
......@@ -85,7 +89,9 @@ class PropertySheetTool(BaseTool):
Create a new Property Sheet in portal_property_sheets from a given
filesystem-based Property Sheet definition.
"""
new_property_sheet = self.newContent(id=klass.__name__,
new_property_sheet_name = klass.__name__
new_property_sheet = self.newContent(id=new_property_sheet_name,
portal_type='Property Sheet')
types_tool = self.getPortalObject().portal_types
......@@ -112,6 +118,41 @@ class PropertySheetTool(BaseTool):
portal_type_class.importFromFilesystemDefinition(new_property_sheet,
category)
# Get filesystem Constraint names to be able to map them properly
# to ZODB Constraint Portal Types as some filesystem constraint
# names are 'NAMEConstraint' or 'NAME'
from Products.ERP5Type import Constraint as FilesystemConstraint
filesystem_constraint_class_name_list = [
class_name for class_name in FilesystemConstraint.__dict__ \
if class_name[0] != '_' ]
# Mapping between the filesystem 'type' field and Portal Types ID
portal_type_dict = {}
for search_result in types_tool.searchFolder(id='% Constraint'):
portal_type_id = search_result.getId()
constraint_class_name = portal_type_id.replace(' ', '')
if constraint_class_name not in filesystem_constraint_class_name_list:
constraint_class_name = constraint_class_name.replace('Constraint', '')
if constraint_class_name not in filesystem_constraint_class_name_list:
raise ValueError, "PropertySheet %s: Constraint %s: No Portal " \
"Type defined for type '%s'" % (new_property_sheet_name,
constraint['id'],
constraint['type'])
portal_type_dict[constraint_class_name] = portal_type_id
portal_type_dict.update(self._merged_portal_type_dict)
for constraint in getattr(klass, '_constraints', ()):
portal_type = portal_type_dict[constraint['type']]
portal_type_class = types_tool.getPortalTypeClass(portal_type)
# Create the new constraint
portal_type_class.importFromFilesystemDefinition(new_property_sheet,
constraint)
return new_property_sheet
security.declareProtected(Permissions.ManagePortal,
......
......@@ -52,6 +52,15 @@ class ConstraintMixin(Predicate):
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 = ()
# Store the filesystem name of the ZODB Constraint as there are
# several different naming for filesystem constraints (*only* useful
# for exportToFilesystemDefinitionDict)
__compatibility_class_name__ = None
__allow_access_to_unprotected_subobjects__ = 1
implements( IConstraint, )
......@@ -104,6 +113,15 @@ class ConstraintMixin(Predicate):
wrapping removed) in order to maintain compatibility and be able
to use setDefaultProperties
NOTE: A filesystem constraint is defined by a dict, then depending
on the 'type' of the constraint, the appropriate class in
ERP5Type.Constraint is instanciated with the dict. This is no
longer needed for ZODB Constraints because they are already
Documents.
@see exportToFilesystemDefinitionDict() to export a ZODB
Constraint as a dict.
XXX: remove as soon as the code is stable
"""
return self.asContext()
......@@ -118,3 +136,106 @@ class ConstraintMixin(Predicate):
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 dict()
@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 dict()
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['reference'] = \
filesystem_definition_copy_dict.pop('id')
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,
'exportToFilesystemDefinitionDict')
def exportToFilesystemDefinitionDict(self):
"""
Export the ZODB Constraint as a filesystem definition dict,
meaningful *only* for testing that a filesystem Constraint has
been properly converted
@see exportToFilesystemDefinition()
"""
filesystem_definition_dict = dict(
id=self.getReference(),
description=self.getDescription(),
type=self.__compatibility_class_name__)
if self.hasTestTalesExpression():
filesystem_definition_dict['condition'] = self.getTestTalesExpression()
for message_id in self._message_id_tuple:
filesystem_definition_dict[message_id] = self._getMessage(message_id)
return filesystem_definition_dict
......@@ -1029,16 +1029,17 @@ class TestZodbImportFilesystemPropertySheet(ERP5TypeTestCase):
# Some fields may not defined a default value (such as 'id')
continue
def _checkPropertyField(self,
def _checkPropertyOrConstraintField(self,
property_sheet_name,
field_name,
filesystem_value,
zodb_value):
"""
Check whether the given filesystem property value and the given
ZODB property value are equal
Check whether the given filesystem property or constraint value
and the given ZODB property value are equal
"""
if isinstance(zodb_value, (list, tuple)):
if isinstance(zodb_value, (list, tuple)) and \
isinstance(filesystem_value, (list, tuple)):
self.failIfDifferentSet(
zodb_value, filesystem_value,
msg="%s: %s: filesystem value: %s, ZODB value: %s" % \
......@@ -1067,43 +1068,44 @@ class TestZodbImportFilesystemPropertySheet(ERP5TypeTestCase):
(property_sheet_name, field_name, filesystem_value,
zodb_value))
def _checkPropertyDefinitionTuple(self,
def _checkPropertyOrConstraintDefinitionTuple(self,
property_sheet_name,
filesystem_property_tuple,
zodb_property_tuple):
"""
Check whether all properties have been properly converted from
the filesystem to the ZODB Property Sheet
Check whether all properties or constraints have been properly
converted from the filesystem to the ZODB Property Sheet
"""
# Check whether all the properties are present in the given ZODB
# Property Sheet
# Check whether all the properties or constraints are present in
# the given ZODB Property Sheet
self.assertEqual(
len(filesystem_property_tuple), len(zodb_property_tuple),
msg="%s: too many properties: filesystem: %s, ZODB: %s" % \
(property_sheet_name, filesystem_property_tuple, zodb_property_tuple))
# Map filesystem property IDs to their definition
# Map filesystem property or constraint IDs to their definition
filesystem_property_id_dict = {}
for property_dict in filesystem_property_tuple:
filesystem_property_id_dict[property_dict['id']] = property_dict
# Check each property defined in ZODB against the filesystem dict
# defined before
# Check each property or constraint defined in ZODB against the
# filesystem dict defined before
for zodb_property_dict in zodb_property_tuple:
# Meaningful to ensure that there is no missing field within a
# property
# property or constraint
validated_field_counter = 0
filesystem_property_dict = \
filesystem_property_id_dict[zodb_property_dict['id']]
# Check each property field
# Check each property or constraint field
for field_name, zodb_value in zodb_property_dict.iteritems():
if field_name in filesystem_property_dict:
self._checkPropertyField(property_sheet_name,
field_name,
self._checkPropertyOrConstraintField(
property_sheet_name, field_name,
filesystem_property_dict[field_name],
zodb_value)
# As we are using accessors when exporting the ZODB Property
# Sheet to its filesystem definition, there may be additional
# fields set to their default value
......@@ -1162,8 +1164,6 @@ class TestZodbImportFilesystemPropertySheet(ERP5TypeTestCase):
Create Property Sheets on portal_property_sheets from their
definition on the filesystem and then test that they are
equivalent
TODO: Constraints
"""
portal = self.getPortalObject().portal_property_sheets
......@@ -1172,6 +1172,7 @@ class TestZodbImportFilesystemPropertySheet(ERP5TypeTestCase):
for name, klass in PropertySheet.__dict__.iteritems():
if name[0] == '_' or isinstance(klass, basestring):
continue
filesystem_property_sheet = klass
property_sheet_name = name
......@@ -1183,13 +1184,13 @@ class TestZodbImportFilesystemPropertySheet(ERP5TypeTestCase):
zodb_property_sheet = portal.createPropertySheetFromFilesystemClass(
filesystem_property_sheet)
zodb_property_tuple, zodb_category_tuple, zodb_constraint_tuple = \
zodb_property_tuple, zodb_category_tuple, zodb_constraint_class_tuple = \
portal.exportPropertySheetToFilesystemDefinitionTuple(
zodb_property_sheet)
self._checkPropertyDefinitionTuple(property_sheet_name,
getattr(filesystem_property_sheet,
'_properties', []),
self._checkPropertyOrConstraintDefinitionTuple(
property_sheet_name,
getattr(filesystem_property_sheet, '_properties', []),
zodb_property_tuple)
self._checkCategoryTuple(property_sheet_name,
......@@ -1197,6 +1198,12 @@ class TestZodbImportFilesystemPropertySheet(ERP5TypeTestCase):
'_categories', []),
zodb_category_tuple)
self._checkPropertyOrConstraintDefinitionTuple(
property_sheet_name, getattr(filesystem_property_sheet,
'_constraints', []),
[ zodb_constraint_class.exportToFilesystemDefinitionDict() \
for zodb_constraint_class in zodb_constraint_class_tuple ])
def test_suite():
suite = unittest.TestSuite()
suite.addTest(unittest.makeSuite(TestPortalTypeClass))
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment