Commit a5c3cb77 authored by Arnaud Fontaine's avatar Arnaud Fontaine

Add Category Membership Arity Constraint for ZODB Property Sheets


git-svn-id: https://svn.erp5.org/repos/public/erp5/trunk@40763 20353a03-c40f-0410-a6d1-a30d3c3de9de
parent d1ae37aa
##############################################################################
#
# 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.mixin.constraint import ConstraintMixin
from AccessControl import ClassSecurityInfo
from Products.ERP5Type import Permissions, PropertySheet
class CategoryMembershipArityConstraint(ConstraintMixin):
"""
This constraint checks if an object respects the arity.
This is only relevant for ZODB Property Sheets (filesystem Property
Sheets rely on Products.ERP5Type.Constraint.CategoryMembershipArity
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 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 Membership Arity Constraint'
portal_type = 'Category Membership Arity Constraint'
# Declarative security
security = ClassSecurityInfo()
security.declareObjectProtected(Permissions.AccessContentsInformation)
property_sheets = (PropertySheet.SimpleItem,
PropertySheet.Predicate,
PropertySheet.Reference,
PropertySheet.CategoryMembershipArityConstraint)
def _calculateArity(self, obj, base_category_list, portal_type_list):
return len(obj.getCategoryMembershipList(base_category_list,
portal_type=portal_type_list))
def checkConsistency(self, obj, fixit=0):
"""
Check the object's consistency. We are looking at the definition
of the constraint where the minimum and the maximum arities are
defined, and the list of objects we wants to check the arity for
"""
if not self.test(obj):
return []
# Retrieve configuration values from PropertySheet
base_category_list = self.getConstraintBaseCategoryList()
min_arity = self.getMinArity()
max_arity = self.getMaxArity()
portal_type_list = self.getConstraintPortalTypeList()
# Check arity and compare it with the min and max
arity = self._calculateArity(obj, base_category_list, portal_type_list)
if (max_arity is None and min_arity <= arity) or \
min_arity <= arity <= max_arity:
return []
mapping = dict(base_category=base_category_list,
portal_type=str(portal_type_list),
current_arity=arity,
min_arity=min_arity,
max_arity=max_arity)
# Generate an error message
if len(portal_type_list) == 0:
if max_arity is None:
message_id = 'message_arity_with_portal_type_too_small'
else:
message_id = 'message_arity_with_portal_type_not_in_range'
else:
if max_arity is None:
message_id = 'message_arity_too_small'
else:
message_id = 'message_arity_not_in_range'
return [self._generateError(obj,
self._getMessage(message_id),
mapping)]
##############################################################################
#
# Copyright (c) 2010 Nexedi SARL and Contributors. All Rights Reserved.
# 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.
#
##############################################################################
class CategoryMembershipArityConstraint:
"""
Define a Category Membership Arity Constraint for ZODB Property Sheets
"""
_properties = (
{ 'id': 'min_arity',
'type': 'int',
'description' : 'Minimum arity' },
{ 'id': 'max_arity',
'type': 'int',
'description' : 'Maximum arity' },
{ 'id': 'constraint_portal_type',
'type': 'lines',
'description' : 'Portal types',
'default': () },
{ 'id': 'constraint_base_category',
'type': 'lines',
'description' : 'Base categories',
'default': () },
{ 'id': 'message_arity_too_small',
'type': 'string',
'description' : 'Error message when the arity for the relation '\
'is too small',
'default': 'Arity Error for Relation ${base_category}, arity is '\
'equal to ${current_arity} but should be at least '\
'${min_arity}' },
{ 'id': 'message_arity_not_in_range',
'type': 'string',
'description' : 'Error message when the arity for the relation '\
'is out of bounds',
'default': 'Arity Error for Relation ${base_category}, arity is '\
'equal to ${current_arity} but should be between '\
'${min_arity} and ${max_arity}' },
{ 'id': 'message_arity_with_portal_type_too_small',
'type': 'string',
'description' : 'Error message when the arity for the relation '\
'and portal type is too small',
'default': 'Arity Error for Relation ${base_category} and Type '\
'${portal_type}, arity is equal to ${current_arity} '\
'but should be at least ${min_arity}' },
{ 'id': 'message_arity_with_portal_type_not_in_range',
'type': 'string',
'description' : 'Error message when the arity for the relation '\
'and portal type is out of bounds',
'default': '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}' },
)
...@@ -21,3 +21,4 @@ from CategoryExistenceConstraint import CategoryExistenceConstraint ...@@ -21,3 +21,4 @@ from CategoryExistenceConstraint import CategoryExistenceConstraint
from PropertyExistenceConstraint import PropertyExistenceConstraint from PropertyExistenceConstraint import PropertyExistenceConstraint
from AttributeEqualityConstraint import AttributeEqualityConstraint from AttributeEqualityConstraint import AttributeEqualityConstraint
from ContentExistenceConstraint import ContentExistenceConstraint from ContentExistenceConstraint import ContentExistenceConstraint
from CategoryMembershipArityConstraint import CategoryMembershipArityConstraint
...@@ -366,10 +366,32 @@ class TestZodbPropertySheet(ERP5TypeTestCase): ...@@ -366,10 +366,32 @@ class TestZodbPropertySheet(ERP5TypeTestCase):
constraint_attribute_value='python: ("sub_category1", "sub_category2")') constraint_attribute_value='python: ("sub_category1", "sub_category2")')
def _newContentExistenceConstraint(self): def _newContentExistenceConstraint(self):
"""
Create a new Content Existence Constraint within test Property
Sheet
"""
self.test_property_sheet.newContent( self.test_property_sheet.newContent(
reference='test_content_existence_constraint', reference='test_content_existence_constraint',
portal_type='Content Existence Constraint', portal_type='Content Existence Constraint',
constraint_portal_type='python: ("Folder")') constraint_portal_type='python: ("Content Existence Constraint")')
def _newCategoryMembershipArityConstraint(self):
"""
Create a new Category Membership Arity Constraint within test
Property Sheet
"""
reference = 'test_category_membership_arity_constraint'
self.getPortal().portal_categories.newContent(
id=reference, portal_type='Base Category')
self.test_property_sheet.newContent(
reference=reference,
portal_type='Category Membership Arity Constraint',
min_arity=1,
max_arity=1,
constraint_portal_type=('Test Migration',),
constraint_base_category=(reference,))
def afterSetUp(self): def afterSetUp(self):
""" """
...@@ -405,6 +427,10 @@ class TestZodbPropertySheet(ERP5TypeTestCase): ...@@ -405,6 +427,10 @@ class TestZodbPropertySheet(ERP5TypeTestCase):
# Sheet # Sheet
self._newContentExistenceConstraint() self._newContentExistenceConstraint()
# Create a Category Membership Arity Constraint in the test
# Property Sheet
self._newCategoryMembershipArityConstraint()
# Create all the test Properties # Create all the test Properties
for operation_type in ('change', 'delete', 'assign'): for operation_type in ('change', 'delete', 'assign'):
self._newStandardProperty(operation_type) self._newStandardProperty(operation_type)
...@@ -430,7 +456,7 @@ class TestZodbPropertySheet(ERP5TypeTestCase): ...@@ -430,7 +456,7 @@ class TestZodbPropertySheet(ERP5TypeTestCase):
# the migration has been finished # the migration has been finished
type_zodb_property_sheet_list=('TestMigration',), type_zodb_property_sheet_list=('TestMigration',),
type_base_category_list=('test_category_existence_constraint',), type_base_category_list=('test_category_existence_constraint',),
type_allowed_content_type_list=('Folder',)) type_allowed_content_type_list=('Content Existence Constraint',))
# Create a test module, meaningful to force generation of # Create a test module, meaningful to force generation of
# TestMigration accessor holders and check the constraints # TestMigration accessor holders and check the constraints
...@@ -836,7 +862,18 @@ class TestZodbPropertySheet(ERP5TypeTestCase): ...@@ -836,7 +862,18 @@ class TestZodbPropertySheet(ERP5TypeTestCase):
self._checkConstraint('test_content_existence_constraint', self._checkConstraint('test_content_existence_constraint',
self.test_module.newContent, self.test_module.newContent,
id='Test Content Existence Constraint', id='Test Content Existence Constraint',
portal_type='Folder') portal_type='Content Existence Constraint')
def testCategoryMembershipArityConstraint(self):
"""
Take the test module and check whether the Category Membership
Arity Constraint is there. Until a Base Category is set on the
Test Module, the constraint should fail
"""
self._checkConstraint('test_category_membership_arity_constraint',
self.test_module.setCategoryList,
('test_category_membership_arity_constraint/'\
'Test Migration',))
TestZodbPropertySheet = skip("ZODB Property Sheets code is not enabled yet")( TestZodbPropertySheet = skip("ZODB Property Sheets code is not enabled yet")(
TestZodbPropertySheet) TestZodbPropertySheet)
......
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