From 7fe5df7a198f345a9a5597106099a702b577f89a Mon Sep 17 00:00:00 2001
From: Rafael Monnerat <rafael@nexedi.com>
Date: Mon, 22 Jan 2007 21:08:00 +0000
Subject: [PATCH] Added ContentExistence Constraint (Made by romain) Added
 StringAttributeMatch Constraint (made by Romain, modified by Me) Added tests
 for both Constraints.

git-svn-id: https://svn.erp5.org/repos/public/erp5/trunk@12216 20353a03-c40f-0410-a6d1-a30d3c3de9de
---
 .../ERP5Type/Constraint/ContentExistence.py   |  63 ++++++++++
 .../Constraint/StringAttributeMatch.py        |  65 ++++++++++
 product/ERP5Type/Constraint/__init__.py       |   2 +
 product/ERP5Type/tests/testConstraint.py      | 116 ++++++++++++++++++
 4 files changed, 246 insertions(+)
 create mode 100644 product/ERP5Type/Constraint/ContentExistence.py
 create mode 100644 product/ERP5Type/Constraint/StringAttributeMatch.py

diff --git a/product/ERP5Type/Constraint/ContentExistence.py b/product/ERP5Type/Constraint/ContentExistence.py
new file mode 100644
index 0000000000..6d35cd7b6b
--- /dev/null
+++ b/product/ERP5Type/Constraint/ContentExistence.py
@@ -0,0 +1,63 @@
+##############################################################################
+#
+# Copyright (c) 2006 Nexedi SARL and Contributors. All Rights Reserved.
+#                    Romain Courteaud <romain@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.Constraint import Constraint
+
+class ContentExistence(Constraint):
+  """
+    This constraint class allows to check / fix
+    that object contains one subobject.
+    Configuration example:
+    { 'id'            : 'line',
+      'description'   : 'Object have to contain a Line',
+      'type'          : 'ContentExistence',
+      'portal_type'   : ('Line', ),
+    },
+  """
+
+  def checkConsistency(self, object, fixit=0):
+    """
+      This is the check method, we return a list of string,
+      each string corresponds to an error.
+      We are checking that object contains a subobject.
+    """
+    obj = object
+    errors = []
+    if self._checkConstraintCondition(object):
+      # Retrieve values inside de PropertySheet (_constraints)
+      portal_type = self.constraint_definition['portal_type']
+      # Check arity and compare it with the min and max
+      arity = len(obj.contentValues(portal_type=portal_type))
+      if (arity == 0):
+        # Generate error message
+        error_message = "Does not contain any subobject"
+        if portal_type is not ():
+          error_message += " of portal type: '%s'" % str(portal_type)
+        # Add error
+        errors.append(self._generateError(obj, error_message))
+    return errors
diff --git a/product/ERP5Type/Constraint/StringAttributeMatch.py b/product/ERP5Type/Constraint/StringAttributeMatch.py
new file mode 100644
index 0000000000..80571ad1af
--- /dev/null
+++ b/product/ERP5Type/Constraint/StringAttributeMatch.py
@@ -0,0 +1,65 @@
+##############################################################################
+#
+# Copyright (c) 2006 Nexedi SARL and Contributors. All Rights Reserved.
+#                    Romain Courteaud <romain@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.
+#
+##############################################################################
+
+import re
+from Products.ERP5Type.Constraint.PropertyExistence import PropertyExistence
+
+class StringAttributeMatch(PropertyExistence):
+  """
+    This constraint class allows to check
+    that a string property match a regular 
+    expression. Configuration example:
+    { 'id'            : 'title_not_empty',
+      'description'   : 'Title must be defined',
+      'type'          : 'StringAttributeMatch',
+      'title'         : '^[^ ]'
+    },
+  """
+
+  def checkConsistency(self, object, fixit=0):
+    """
+    This is the check method, we return a list of string,
+    each string corresponds to an error.
+    Check that each attribute does not match the RE
+    """
+    errors = PropertyExistence.checkConsistency(self, object, fixit=fixit)
+    for attribute_name, attribute_value in self.constraint_definition.items():
+      error_message = None
+      # If property does not exist, error will be raise by 
+      # PropertyExistence Constraint.
+      current_value = object.getProperty(attribute_name)
+      regexp = re.compile(attribute_value)
+      if (current_value is not None) and \
+          (regexp.match(current_value) is None):
+        # Generate error_message
+        error_message =  "Attribute %s is '%s' and not match '%s'" % \
+            (attribute_name, current_value,
+             attribute_value)
+        # Generate error
+        errors.append(self._generateError(object, error_message))
+    return errors
diff --git a/product/ERP5Type/Constraint/__init__.py b/product/ERP5Type/Constraint/__init__.py
index 314b62836e..9387388719 100644
--- a/product/ERP5Type/Constraint/__init__.py
+++ b/product/ERP5Type/Constraint/__init__.py
@@ -8,3 +8,5 @@ from CategoryExistence import CategoryExistence
 from PortalTypeClass import PortalTypeClass
 from CategoryAcquiredMembershipArity import CategoryAcquiredMembershipArity
 from TALESConstraint import TALESConstraint
+from ContentExistence import ContentExistence
+from StringAttributeMatch import StringAttributeMatch
diff --git a/product/ERP5Type/tests/testConstraint.py b/product/ERP5Type/tests/testConstraint.py
index 2044099491..74f8ddb2c0 100644
--- a/product/ERP5Type/tests/testConstraint.py
+++ b/product/ERP5Type/tests/testConstraint.py
@@ -44,6 +44,7 @@ class TestConstraint(PropertySheetTestCase):
   quiet = 1
 
   object_portal_type = "Organisation"
+  object_content_portal_type = "Address"
   object_title = "Title test"
 
   def getTitle(self):
@@ -1174,6 +1175,121 @@ class TestConstraint(PropertySheetTestCase):
     # now we can use testing_category as any category accessor
     self.assertEquals(obj, obj.getTestingCategoryValue())
 
+  def stepCreateContentExistence(self, sequence=None, sequence_list=None, **kw):
+    """
+      Create a Content Existence Constraint
+    """
+    self._createGenericConstraint(
+                            sequence,
+                            klass_name='ContentExistence',
+                            id='ContentExistence',
+                            description='ContentExistence test',
+                            portal_type=(self.object_content_portal_type, )
+                            )
+
+  def stepCreateContentObject(self, sequence=None, sequence_list=None, **kw):
+    """
+      Create a Content Object inside one Object
+    """
+    object = sequence.get('object')
+    content_object = object.newContent(portal_type=self.object_content_portal_type)
+    sequence.edit(
+        content_object = content_object,
+    )
+
+  def test_ContentExistenceConstraint(self, quiet=quiet, run=run_all_test):
+    """
+      Tests Content Existence
+    """
+
+    if not run: return
+    sequence_list = SequenceList()
+    # Test Constraint without any content
+    sequence_string = '\
+              CreateObject \
+              CreateContentExistence \
+              CallCheckConsistency \
+              CheckIfConstraintFailed \
+              '
+    sequence_list.addSequenceString(sequence_string)
+    # Test Constraint with content
+    sequence_string = '\
+              CreateObject \
+              CreateContentExistence \
+              CreateContentObject \
+              CallCheckConsistency \
+              CheckIfConstraintSucceeded \
+              '
+    sequence_list.addSequenceString(sequence_string)
+    sequence_list.play(self, quiet=quiet)
+
+  def stepCreateStringAttributeMatch(self, sequence=None, sequence_list=None, **kw):
+    """
+      Create a String Atribute Match Constraint
+    """
+    self._createGenericConstraint(
+                            sequence,
+                            klass_name='StringAttributeMatch',
+                            id='StringAttributeMatch',
+                            description='StringAttributeMatch test',
+                            title='^[^ ]'
+                            )
+
+  def stepSetObjectTitle0(self, sequence=None, sequence_list=None, **kw):
+    """
+      Set valid Title to Object 
+    """
+    object = sequence.get('object')
+    object.setTitle(self.object_title)
+    sequence.edit(
+        object = object,
+    )
+
+  def stepSetObjectTitle1(self, sequence=None, sequence_list=None, **kw):
+    """
+      Set empty (or invalid string) to Object
+    """
+    object = sequence.get('object')
+    object.setTitle(' ')
+    sequence.edit(
+        object = object,
+    )
+
+  def test_StringAttributeMatchConstraint(self, quiet=quiet, run=run_all_test):
+    """
+      Tests Content Existence
+    """
+    if not run: return
+    sequence_list = SequenceList()
+    # Test Constraint with empty Title
+    sequence_string = '\
+              CreateObject \
+              CreateStringAttributeMatch \
+              CallCheckConsistency \
+              CheckIfConstraintFailed \
+              '
+    sequence_list.addSequenceString(sequence_string)
+    # Test Constraint with Title
+    sequence_string = '\
+              CreateObject \
+              CreateStringAttributeMatch \
+              SetObjectTitle0 \
+              CallCheckConsistency \
+              CheckIfConstraintSucceeded \
+              '
+    sequence_list.addSequenceString(sequence_string)
+    # Test Constraint with invalid Title 
+    # Not match with regex
+    sequence_string = '\
+              CreateObject \
+              CreateStringAttributeMatch \
+              SetObjectTitle1 \
+              CallCheckConsistency \
+              CheckIfConstraintFailed \
+              '
+    sequence_list.addSequenceString(sequence_string)
+
+    sequence_list.play(self, quiet=quiet)
 
 if __name__ == '__main__':
     framework()
-- 
2.30.9