Commit 7d9faf44 authored by Arnaud Fontaine's avatar Arnaud Fontaine

Perform revalidate automatically rather than requiring a manual action.

Also, save error message and non-validated text content in a workflow variable
rather than in a property.
parent ce0fac05
...@@ -106,7 +106,7 @@ ...@@ -106,7 +106,7 @@
<value> <value>
<list> <list>
<string>my_description</string> <string>my_description</string>
<string>my_translated_validation_state_title</string> <string>my_translated_validation_state_title_with_error_message</string>
</list> </list>
</value> </value>
</item> </item>
......
...@@ -14,7 +14,7 @@ ...@@ -14,7 +14,7 @@
</item> </item>
<item> <item>
<key> <string>id</string> </key> <key> <string>id</string> </key>
<value> <string>my_translated_validation_state_title</string> </value> <value> <string>my_translated_validation_state_title_with_error_message</string> </value>
</item> </item>
<item> <item>
<key> <string>message_values</string> </key> <key> <string>message_values</string> </key>
......
...@@ -58,7 +58,6 @@ ...@@ -58,7 +58,6 @@
<list> <list>
<string>validate</string> <string>validate</string>
<string>invalidate</string> <string>invalidate</string>
<string>revalidate</string>
</list> </list>
</value> </value>
</item> </item>
......
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="InteractionDefinition" module="Products.ERP5.Interaction"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>actbox_category</string> </key>
<value> <string>workflow</string> </value>
</item>
<item>
<key> <string>actbox_name</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>actbox_url</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>activate_script_name</string> </key>
<value>
<tuple/>
</value>
</item>
<item>
<key> <string>after_script_name</string> </key>
<value>
<tuple/>
</value>
</item>
<item>
<key> <string>before_commit_script_name</string> </key>
<value>
<tuple/>
</value>
</item>
<item>
<key> <string>description</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>guard</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>Component_revalidate</string> </value>
</item>
<item>
<key> <string>method_id</string> </key>
<value>
<list>
<string>revalidate</string>
</list>
</value>
</item>
<item>
<key> <string>once_per_transaction</string> </key>
<value> <int>1</int> </value>
</item>
<item>
<key> <string>portal_type_filter</string> </key>
<value>
<list>
<string>Document Component</string>
<string>Extension Component</string>
</list>
</value>
</item>
<item>
<key> <string>script_name</string> </key>
<value>
<list>
<string>revalidate</string>
</list>
</value>
</item>
<item>
<key> <string>temporary_document_disallowed</string> </key>
<value> <int>0</int> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>trigger_type</string> </key>
<value> <int>2</int> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="PythonScript" module="Products.PythonScripts.PythonScript"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>Script_magic</string> </key>
<value> <int>3</int> </value>
</item>
<item>
<key> <string>_bind_names</string> </key>
<value>
<object>
<klass>
<global name="NameAssignments" module="Shared.DC.Scripts.Bindings"/>
</klass>
<tuple/>
<state>
<dictionary>
<item>
<key> <string>_asgns</string> </key>
<value>
<dictionary>
<item>
<key> <string>name_container</string> </key>
<value> <string>container</string> </value>
</item>
<item>
<key> <string>name_context</string> </key>
<value> <string>context</string> </value>
</item>
<item>
<key> <string>name_m_self</string> </key>
<value> <string>script</string> </value>
</item>
<item>
<key> <string>name_subpath</string> </key>
<value> <string>traverse_subpath</string> </value>
</item>
</dictionary>
</value>
</item>
</dictionary>
</state>
</object>
</value>
</item>
<item>
<key> <string>_body</string> </key>
<value> <string>state_change[\'object\'].setTextContentAfterRevalidation()\n
</string> </value>
</item>
<item>
<key> <string>_params</string> </key>
<value> <string>state_change</string> </value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>revalidate</string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="StateDefinition" module="Products.DCWorkflow.States"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>description</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>modified</string> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string>Modified</string> </value>
</item>
<item>
<key> <string>transitions</string> </key>
<value>
<tuple>
<string>invalidate</string>
<string>invalidate_action</string>
<string>modified</string>
<string>validate</string>
</tuple>
</value>
</item>
<item>
<key> <string>type_list</string> </key>
<value>
<tuple/>
</value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
...@@ -30,8 +30,8 @@ ...@@ -30,8 +30,8 @@
<tuple> <tuple>
<string>invalidate</string> <string>invalidate</string>
<string>invalidate_action</string> <string>invalidate_action</string>
<string>revalidate</string> <string>modified</string>
<string>revalidate_action</string> <string>validate</string>
</tuple> </tuple>
</value> </value>
</item> </item>
......
...@@ -38,11 +38,11 @@ ...@@ -38,11 +38,11 @@
</item> </item>
<item> <item>
<key> <string>id</string> </key> <key> <string>id</string> </key>
<value> <string>revalidate</string> </value> <value> <string>modified</string> </value>
</item> </item>
<item> <item>
<key> <string>new_state_id</string> </key> <key> <string>new_state_id</string> </key>
<value> <string></string> </value> <value> <string>modified</string> </value>
</item> </item>
<item> <item>
<key> <string>script_name</string> </key> <key> <string>script_name</string> </key>
......
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="TransitionDefinition" module="Products.DCWorkflow.Transitions"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>actbox_category</string> </key>
<value> <string>workflow</string> </value>
</item>
<item>
<key> <string>actbox_icon</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>actbox_name</string> </key>
<value> <string>Revalidate</string> </value>
</item>
<item>
<key> <string>actbox_url</string> </key>
<value> <string>%(content_url)s/Base_viewWorkflowActionDialog?workflow_action=revalidate_action</string> </value>
</item>
<item>
<key> <string>after_script_name</string> </key>
<value> <string>revalidate</string> </value>
</item>
<item>
<key> <string>description</string> </key>
<value> <string>Revalidate a Component, until then the code previously validated/revalidated is used.</string> </value>
</item>
<item>
<key> <string>guard</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAI=</string> </persistent>
</value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>revalidate_action</string> </value>
</item>
<item>
<key> <string>new_state_id</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>script_name</string> </key>
<value> <string>checkConsistency</string> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string>Revalidate Action</string> </value>
</item>
<item>
<key> <string>trigger_type</string> </key>
<value> <int>1</int> </value>
</item>
</dictionary>
</pickle>
</record>
<record id="2" aka="AAAAAAAAAAI=">
<pickle>
<global name="Guard" module="Products.DCWorkflow.Guard"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>roles</string> </key>
<value>
<tuple>
<string>Assignee</string>
<string>Assignor</string>
<string>Manager</string>
</tuple>
</value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="VariableDefinition" module="Products.DCWorkflow.Variables"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>default_expr</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>default_value</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>description</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>for_catalog</string> </key>
<value> <int>0</int> </value>
</item>
<item>
<key> <string>for_status</string> </key>
<value> <int>1</int> </value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>text_content</string> </value>
</item>
<item>
<key> <string>info_guard</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>update_always</string> </key>
<value> <int>1</int> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
2012-02-02 arnaud.fontaine
* Perform revalidate automatically rather than requiring a manual action.
2012-02-01 arnaud.fontaine 2012-02-01 arnaud.fontaine
* Replace comments by description field in Component_view. * Replace comments by description field in Component_view.
......
40998 40999
\ No newline at end of file \ No newline at end of file
...@@ -58,11 +58,14 @@ class Component(Base): ...@@ -58,11 +58,14 @@ class Component(Base):
'Reference', 'Reference',
'TextDocument') 'TextDocument')
def checkConsistency(self, *args, **kw): def checkConsistency(self, text_content=None, *args, **kw):
""" """
XXX-arnau: should probably in a separate Constraint class XXX-arnau: should probably in a separate Constraint class
""" """
if not self.getTextContent(): if text_content is None:
text_content = self.getTextContent()
if not text_content:
return [ConsistencyMessage(self, return [ConsistencyMessage(self,
object_relative_url=self.getRelativeUrl(), object_relative_url=self.getRelativeUrl(),
message="No source code", message="No source code",
...@@ -70,7 +73,7 @@ class Component(Base): ...@@ -70,7 +73,7 @@ class Component(Base):
message = None message = None
try: try:
self.load() self.load(text_content=text_content)
except SyntaxError, e: except SyntaxError, e:
message = "%s (line: %d, column: %d)" % (e.msg, e.lineno, e.offset) message = "%s (line: %d, column: %d)" % (e.msg, e.lineno, e.offset)
except Exception, e: except Exception, e:
...@@ -99,21 +102,36 @@ class Component(Base): ...@@ -99,21 +102,36 @@ class Component(Base):
Then, when the user revalidates the Component through a workflow action, Then, when the user revalidates the Component through a workflow action,
'text_content_non_validated' property is copied back to 'text_content'. 'text_content_non_validated' property is copied back to 'text_content'.
XXX-arnau: having a separate property would require hackish code when XXX-arnau: the workflow history bit is really ugly and should be moved to
exporting the bt5, perhaps a workflow variable would be better? an interaction workflow instead
""" """
if self.getValidationState() == 'validated': validation_state = self.getValidationState()
return self.setProperty('text_content_non_validated', text_content) if validation_state in ('validated', 'modified'):
error_message_list = self.checkConsistency(text_content=text_content)
if error_message_list:
self.modified()
return super(Component, self)._setTextContent(text_content) validation_workflow = self.workflow_history['component_validation_workflow']
def setTextContentAfterRevalidation(self): last_validation_workflow = validation_workflow[-1]
""" last_validation_workflow['error_message'] = error_message_list[0]
Call upon revalidate on an already validated Component to set the source last_validation_workflow['text_content'] = text_content
code from text_content_non_validated property
""" previous_validation_workflow = validation_workflow[-2]
super(Component, self)._setTextContent(self.getTextContent()) previous_validation_workflow['error_message'] = ''
self.setProperty('text_content_non_validated', None) previous_validation_workflow['text_content'] = ''
else:
super(Component, self)._setTextContent(text_content)
self.validate()
if validation_state == 'modified':
# XXX-arnau: copy/paste
validation_workflow = self.workflow_history['component_validation_workflow']
previous_validation_workflow = validation_workflow[-2]
previous_validation_workflow['error_message'] = ''
previous_validation_workflow['text_content'] = ''
else:
return super(Component, self)._setTextContent(text_content)
def getTextContent(self, validated_only=False): def getTextContent(self, validated_only=False):
""" """
...@@ -123,13 +141,29 @@ class Component(Base): ...@@ -123,13 +141,29 @@ class Component(Base):
validated), meaningful when editing a Component or checking consistency validated), meaningful when editing a Component or checking consistency
""" """
if not validated_only: if not validated_only:
text_content_non_validated = self.getProperty('text_content_non_validated') text_content_non_validated = \
self.workflow_history['component_validation_workflow'][-1].get('text_content',
None)
if text_content_non_validated: if text_content_non_validated:
return text_content_non_validated return text_content_non_validated
return super(Component, self).getTextContent() return super(Component, self).getTextContent()
def load(self, namespace_dict={}, validated_only=False): def _getErrorMessage(self):
current_workflow = self.workflow_history['component_validation_workflow'][-1]
return current_workflow['error_message']
def getTranslatedValidationStateTitleWithErrorMessage(self):
validation_state_title = self.getTranslatedValidationStateTitle()
error_message = self._getErrorMessage()
if error_message:
return "%s (%s)" % (validation_state_title,
str(error_message.getTranslatedMessage()))
return validation_state_title
def load(self, namespace_dict={}, validated_only=False, text_content=None):
""" """
Load the source code into the given dict. Using exec() rather than Load the source code into the given dict. Using exec() rather than
imp.load_source() as the latter would required creating an intermediary imp.load_source() as the latter would required creating an intermediary
...@@ -138,7 +172,10 @@ class Component(Base): ...@@ -138,7 +172,10 @@ class Component(Base):
it. By default namespace_dict is an empty dict to allow checking the it. By default namespace_dict is an empty dict to allow checking the
source code before validate. source code before validate.
""" """
exec self.getTextContent(validated_only=validated_only) in namespace_dict if text_content is None:
text_content = self.getTextContent(validated_only=validated_only)
exec text_content in namespace_dict
@staticmethod @staticmethod
def _getFilesystemPath(): def _getFilesystemPath():
......
...@@ -1204,6 +1204,18 @@ class TestZodbPropertySheet(ERP5TypeTestCase): ...@@ -1204,6 +1204,18 @@ class TestZodbPropertySheet(ERP5TypeTestCase):
self.fail("Creating a Category Expression with syntax error raises "\ self.fail("Creating a Category Expression with syntax error raises "\
"an error") "an error")
from Products.ERP5Type.Tool.ComponentTool import ComponentTool
ComponentTool._original_reset = ComponentTool.reset
ComponentTool._reset_performed = False
def assertResetNotCalled(*args, **kwargs):
raise AssertionError("reset should only be called once revalidating")
def assertResetCalled(self, *args, **kwargs):
from Products.ERP5Type.Tool.ComponentTool import ComponentTool
ComponentTool._reset_performed = True
return ComponentTool._original_reset(self, *args, **kwargs)
import abc import abc
class _TestZodbComponent(ERP5TypeTestCase): class _TestZodbComponent(ERP5TypeTestCase):
...@@ -1255,74 +1267,54 @@ class _TestZodbComponent(ERP5TypeTestCase): ...@@ -1255,74 +1267,54 @@ class _TestZodbComponent(ERP5TypeTestCase):
self.assertHasAttribute(self._module, self.assertHasAttribute(self._module,
'TestValidateInvalidateComponent') 'TestValidateInvalidateComponent')
def testRevalidate(self): def testSourceCodeWithSyntaxError(self):
""" valid_code = 'def foobar(*args, **kwargs):\n return 42'
Check whether revalidate is performed properly ComponentTool.reset = assertResetCalled
""" try:
validated_code = 'def foobar(*args, **kwargs):\n return "Validated"' component = self._newComponent('TestComponentWithSyntaxError', valid_code)
component = self._newComponent('TestRevalidateComponent', validated_code)
component.validate() component.validate()
transaction.commit() transaction.commit()
self.tic() self.tic()
finally:
ComponentTool.reset = ComponentTool._original_reset
self.assertHasAttribute(self._module, 'TestRevalidateComponent') self.assertEquals(component.getValidationState(), 'validated')
self.assertEquals(component.getTextContent(), validated_code) self.assertEquals(component.getTextContent(), valid_code)
self.assertEquals(component.getTextContent(validated_only=True), self.assertEquals(component.getTextContent(validated_only=True), valid_code)
validated_code) self.assertHasAttribute(self._module, 'TestComponentWithSyntaxError')
from Products.ERP5Type.Tool.ComponentTool import ComponentTool
old_reset_function = ComponentTool.reset
def assertResetNotCalled(*args, **kwargs):
raise AssertionError("reset should only be called once revalidating")
def assertResetCalled(self, *args, **kwargs):
from Products.ERP5Type.Tool.ComponentTool import ComponentTool
ComponentTool._reset_performed = True
return old_reset_function(self, *args, **kwargs)
revalidated_code = 'def foobar(*args, **kwargs):\n return "Revalidated"'
try: invalid_code = 'def foobar(*args, **kwargs)\n return 42'
ComponentTool.reset = assertResetNotCalled ComponentTool.reset = assertResetNotCalled
component.setTextContent(revalidated_code) try:
component.setTextContent(invalid_code)
transaction.commit() transaction.commit()
self.tic() self.tic()
finally:
ComponentTool.reset = ComponentTool._original_reset
self.assertEquals(component.getTextContent(), revalidated_code) self.assertEquals(component.getValidationState(), 'modified')
self.assertEquals(component.getTextContent(validated_only=True), self.assertNotEqual(component._getErrorMessage(), '')
validated_code) self.assertEquals(component.getTextContent(), invalid_code)
self.assertEquals(component.getTextContent(validated_only=True), valid_code)
self._component_tool.reset()
self.assertHasAttribute(self._module, 'TestComponentWithSyntaxError')
ComponentTool.reset = assertResetCalled ComponentTool.reset = assertResetCalled
component.revalidate() try:
component.setTextContent(valid_code)
transaction.commit() transaction.commit()
self.tic() self.tic()
self.assertTrue(ComponentTool._reset_performed) self.assertEquals(ComponentTool._reset_performed, True)
self.assertEquals(component.getTextContent(), revalidated_code)
self.assertEquals(component.getTextContent(validated_only=True),
revalidated_code)
finally: finally:
self._component_tool.reset = old_reset_function ComponentTool.reset = ComponentTool._original_reset
ComponentTool._reset_performed = False
def testSourceCodeWithSyntaxError(self):
test_component = self._newComponent( self.assertEquals(component.getValidationState(), 'validated')
'TestComponentWithSyntaxError', self.assertEquals(component._getErrorMessage(), '')
'def foobar(*args, **kwargs):\n return 42') self.assertEquals(component.getTextContent(), valid_code)
self.assertEquals(component.getTextContent(validated_only=True), valid_code)
self.assertEqual(test_component.checkConsistency(), []) self.assertHasAttribute(self._module, 'TestComponentWithSyntaxError')
test_component.validate()
transaction.commit()
self.tic()
self.assertHasAttribute(self._module,
'TestComponentWithSyntaxError')
test_component.setTextContent('def foobar(*args, **kwargs)\n return 42')
transaction.commit()
self.tic()
self.assertNotEqual(test_component.checkConsistency(), [])
from Products.ERP5Type.Core.ExtensionComponent import ExtensionComponent from Products.ERP5Type.Core.ExtensionComponent import ExtensionComponent
......
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