Commit 16de28d5 authored by Jérome Perrin's avatar Jérome Perrin

Merge !504 Do not save documents when there are pending activities

When document has pending activities, we refuse changing ID ( because there might be pending `updateRelatedContent` activities if I remember correctly ), but it's done in a way that breaks the "atomic" aspect of the transaction a bit, because we

As a result, this happens sometimes that not all properties user changed are modified. In the example below, the change to *Include Documents in Site Map* is not saved (and also change to *ID*):

![erp5-sorryPendingActivitiesSavePartially](/uploads/ff4bfd6ad0e8a42ba3684cccdc450e21/erp5-sorryPendingActivitiesSavePartially.gif)

( screencast of editing a document to change ids and several other
properties - after clicking save, we can see that changing id is refused
because there is pending activities. Other properties that where changes
at the same times are not all modified, which breaks the
transactionality we can usally expect when editing documents in ERP5 )

The changed here is to use a field validator that refused editing when there are pending activities, so that user gets a:

![erp5-pending-activiities](/uploads/bfe825560bdee34f0443e8e36884f21c/erp5-pending-activiities.png)

( screenshot of the change: now edition is rejected )

and the result is either all changes are applied or no change is applied at all.

This is done by:
 * introducing a new `my_view_mode_id` field in `erp5_core`'s `Base_viewFieldLibrary`
 * using this field as proxy field of all editable `my_id` fields. Maybe I forgot some business templates, I changed only the most common ones. I intentionally did not change all fields of `erp5_ui_test` because I think they are used to compare speed of proxy fields vs traditional fields.

0352f50fd543fda2712bb8ca93d8a8814f975a26 introduces a Zelenium test exercising this new behavior.

/reviewed-on !504

Conflicts:
	product/ERP5/bootstrap/erp5_core/SkinTemplateItem/portal_skins/erp5_core/Base_viewFieldLibrary.xml
parents 6e09ff13 6c44fa17
......@@ -6,6 +6,12 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>delegated_list</string> </key>
<value>
<list/>
</value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>my_id</string> </value>
......@@ -69,7 +75,7 @@
</item>
<item>
<key> <string>form_id</string> </key>
<value> <string>Base_viewAccountingFieldLibrary</string> </value>
<value> <string>Base_viewFieldLibrary</string> </value>
</item>
<item>
<key> <string>target</string> </key>
......
......@@ -9,12 +9,7 @@
<item>
<key> <string>delegated_list</string> </key>
<value>
<list>
<string>description</string>
<string>title</string>
<string>required</string>
<string>display_width</string>
</list>
<list/>
</value>
</item>
<item>
......@@ -74,34 +69,18 @@
<key> <string>values</string> </key>
<value>
<dictionary>
<item>
<key> <string>description</string> </key>
<value> <string>Identification</string> </value>
</item>
<item>
<key> <string>display_width</string> </key>
<value> <int>20</int> </value>
</item>
<item>
<key> <string>field_id</string> </key>
<value> <string>my_string_field</string> </value>
<value> <string>my_view_mode_id</string> </value>
</item>
<item>
<key> <string>form_id</string> </key>
<value> <string>Base_viewFieldLibrary</string> </value>
</item>
<item>
<key> <string>required</string> </key>
<value> <int>1</int> </value>
</item>
<item>
<key> <string>target</string> </key>
<value> <string>Click to edit the target</string> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string>ID</string> </value>
</item>
</dictionary>
</value>
</item>
......
......@@ -9,9 +9,7 @@
<item>
<key> <string>delegated_list</string> </key>
<value>
<list>
<string>title</string>
</list>
<list/>
</value>
</item>
<item>
......@@ -73,7 +71,7 @@
<dictionary>
<item>
<key> <string>field_id</string> </key>
<value> <string>my_string_field</string> </value>
<value> <string>my_view_mode_id</string> </value>
</item>
<item>
<key> <string>form_id</string> </key>
......@@ -83,10 +81,6 @@
<key> <string>target</string> </key>
<value> <string>Click to edit the target</string> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string>ID</string> </value>
</item>
</dictionary>
</value>
</item>
......
......@@ -936,7 +936,7 @@ class TestERP5Document_getHateoas_mode_traverse(ERP5HALJSONStyleSkinsMixin):
self.assertEqual(result_dict['pt'], 'form_view')
self.assertEqual(result_dict['action'], 'Base_edit')
self.assertEqual(result_dict['group_list'][0][0], 'left')
self.assertEqual(result_dict['group_list'][0][1][0], ['my_id', {'meta_type': 'StringField'}])
self.assertEqual(result_dict['group_list'][0][1][0], ['my_id', {'meta_type': 'ProxyField'}])
self.assertEqual(result_dict['_debug'], "traverse")
class TestERP5Document_getHateoas_mode_search(ERP5HALJSONStyleSkinsMixin):
......
......@@ -9,10 +9,7 @@
<item>
<key> <string>delegated_list</string> </key>
<value>
<list>
<string>required</string>
<string>title</string>
</list>
<list/>
</value>
</item>
<item>
......@@ -74,24 +71,16 @@
<dictionary>
<item>
<key> <string>field_id</string> </key>
<value> <string>my_string_field</string> </value>
<value> <string>my_view_mode_id</string> </value>
</item>
<item>
<key> <string>form_id</string> </key>
<value> <string>Base_viewFieldLibrary</string> </value>
</item>
<item>
<key> <string>required</string> </key>
<value> <int>1</int> </value>
</item>
<item>
<key> <string>target</string> </key>
<value> <string>Click to edit the target</string> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string>ID</string> </value>
</item>
</dictionary>
</value>
</item>
......
......@@ -9,10 +9,7 @@
<item>
<key> <string>delegated_list</string> </key>
<value>
<list>
<string>display_width</string>
<string>title</string>
</list>
<list/>
</value>
</item>
<item>
......@@ -72,13 +69,9 @@
<key> <string>values</string> </key>
<value>
<dictionary>
<item>
<key> <string>display_width</string> </key>
<value> <int>20</int> </value>
</item>
<item>
<key> <string>field_id</string> </key>
<value> <string>my_string_field</string> </value>
<value> <string>my_view_mode_id</string> </value>
</item>
<item>
<key> <string>form_id</string> </key>
......@@ -88,10 +81,6 @@
<key> <string>target</string> </key>
<value> <string>Click to edit the target</string> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string>ID</string> </value>
</item>
</dictionary>
</value>
</item>
......
......@@ -9,10 +9,7 @@
<item>
<key> <string>delegated_list</string> </key>
<value>
<list>
<string>display_width</string>
<string>title</string>
</list>
<list/>
</value>
</item>
<item>
......@@ -72,13 +69,9 @@
<key> <string>values</string> </key>
<value>
<dictionary>
<item>
<key> <string>display_width</string> </key>
<value> <int>20</int> </value>
</item>
<item>
<key> <string>field_id</string> </key>
<value> <string>my_string_field</string> </value>
<value> <string>my_view_mode_id</string> </value>
</item>
<item>
<key> <string>form_id</string> </key>
......@@ -88,10 +81,6 @@
<key> <string>target</string> </key>
<value> <string>Click to edit the target</string> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string>ID</string> </value>
</item>
</dictionary>
</value>
</item>
......
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="ZopePageTemplate" module="Products.PageTemplates.ZopePageTemplate"/>
</pickle>
<pickle>
<dictionary>
<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_subpath</string> </key>
<value> <string>traverse_subpath</string> </value>
</item>
</dictionary>
</value>
</item>
</dictionary>
</state>
</object>
</value>
</item>
<item>
<key> <string>content_type</string> </key>
<value> <string>text/html</string> </value>
</item>
<item>
<key> <string>expand</string> </key>
<value> <int>0</int> </value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>testChangeIdWithPendingActivities</string> </value>
</item>
<item>
<key> <string>output_encoding</string> </key>
<value> <string>utf-8</string> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <unicode></unicode> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
<html xmlns:tal="http://xml.zope.org/namespaces/tal"
xmlns:metal="http://xml.zope.org/namespaces/metal">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Test Change Id with pending activities</title>
</head>
<body>
<table cellpadding="1" cellspacing="1" border="1">
<thead>
<tr><td rowspan="1" colspan="3">Test Change Id with pending activities</td></tr>
</thead><tbody>
<tal:block metal:use-macro="here/ListBoxZuite_CommonTemplate/macros/init" />
<tr>
<td>open</td>
<td>${base_url}/foo_module/FooModule_createObjects?num:int=1</td>
<td></td>
</tr>
<tr>
<td>assertTextPresent</td>
<td>Created Successfully.</td>
<td></td>
</tr>
<tr>
<td>open</td>
<td>${base_url}/foo_module/Zuite_waitForActivities</td>
<td></td>
</tr>
<tr>
<td>assertTextPresent</td>
<td>Done.</td>
<td></td>
</tr>
<tr>
<td>open</td>
<td>${base_url}/foo_module/view</td>
<td></td>
</tr>
<tr>
<td>clickAndWait</td>
<td>link=0</td>
<td></td>
</tr>
<tr>
<td>assertTitle</td>
<td>Foo - Title 0*</td>
<td></td>
</tr>
<tr>
<td>assertValue</td>
<td>field_my_id</td>
<td>0</td>
</tr>
<tr>
<td>type</td>
<td>field_my_title</td>
<td>Change title (to have an activity)</td>
</tr>
<tr>
<td>clickAndWait</td>
<td>Base_edit:method</td>
<td></td>
</tr>
<tr>
<td>verifyPortalStatusMessage</td>
<td>Data updated.</td>
<td></td>
</tr>
<tr>
<td>type</td>
<td>field_my_id</td>
<td>new_id</td>
</tr>
<tr>
<td>clickAndWait</td>
<td>Base_edit:method</td>
<td></td>
</tr><tr>
<td>verifyText</td>
<td>//div[@id="information_area"]</td>
<td>Input data has errors. Please look at the error messages below.</td>
</tr>
<tr>
<td>verifyText</td>
<td>//span[@class="error"]</td>
<td>Document has pending activities.</td>
</tr>
</tbody></table>
</body>
</html>
\ No newline at end of file
......@@ -9,10 +9,7 @@
<item>
<key> <string>delegated_list</string> </key>
<value>
<list>
<string>description</string>
<string>title</string>
</list>
<list/>
</value>
</item>
<item>
......@@ -72,26 +69,18 @@
<key> <string>values</string> </key>
<value>
<dictionary>
<item>
<key> <string>description</string> </key>
<value> <string>Identification</string> </value>
</item>
<item>
<key> <string>field_id</string> </key>
<value> <string>my_string_field</string> </value>
<value> <string>my_view_mode_id</string> </value>
</item>
<item>
<key> <string>form_id</string> </key>
<value> <string>Base_viewWebFieldLibrary</string> </value>
<value> <string>Base_viewFieldLibrary</string> </value>
</item>
<item>
<key> <string>target</string> </key>
<value> <string>Click to edit the target</string> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string>ID</string> </value>
</item>
</dictionary>
</value>
</item>
......
......@@ -9,12 +9,7 @@
<item>
<key> <string>delegated_list</string> </key>
<value>
<list>
<string>description</string>
<string>title</string>
<string>required</string>
<string>display_width</string>
</list>
<list/>
</value>
</item>
<item>
......@@ -74,34 +69,18 @@
<key> <string>values</string> </key>
<value>
<dictionary>
<item>
<key> <string>description</string> </key>
<value> <string>Identification</string> </value>
</item>
<item>
<key> <string>display_width</string> </key>
<value> <int>20</int> </value>
</item>
<item>
<key> <string>field_id</string> </key>
<value> <string>my_string_field</string> </value>
<value> <string>my_view_mode_id</string> </value>
</item>
<item>
<key> <string>form_id</string> </key>
<value> <string>Base_viewWebFieldLibrary</string> </value>
</item>
<item>
<key> <string>required</string> </key>
<value> <int>1</int> </value>
<value> <string>Base_viewFieldLibrary</string> </value>
</item>
<item>
<key> <string>target</string> </key>
<value> <string>Click to edit the target</string> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string>ID</string> </value>
</item>
</dictionary>
</value>
</item>
......
......@@ -9,10 +9,7 @@
<item>
<key> <string>delegated_list</string> </key>
<value>
<list>
<string>title</string>
<string>required</string>
</list>
<list/>
</value>
</item>
<item>
......@@ -72,30 +69,18 @@
<key> <string>values</string> </key>
<value>
<dictionary>
<item>
<key> <string>description</string> </key>
<value> <string>Identification</string> </value>
</item>
<item>
<key> <string>field_id</string> </key>
<value> <string>my_string_field</string> </value>
<value> <string>my_view_mode_id</string> </value>
</item>
<item>
<key> <string>form_id</string> </key>
<value> <string>Base_viewFieldLibrary</string> </value>
</item>
<item>
<key> <string>required</string> </key>
<value> <int>1</int> </value>
</item>
<item>
<key> <string>target</string> </key>
<value> <string>Click to edit the target</string> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string>ID</string> </value>
</item>
</dictionary>
</value>
</item>
......
......@@ -10,7 +10,7 @@
<key> <string>delegated_list</string> </key>
<value>
<list>
<string>title</string>
<string>external_validator</string>
</list>
</value>
</item>
......@@ -71,9 +71,15 @@
<key> <string>values</string> </key>
<value>
<dictionary>
<item>
<key> <string>external_validator</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAI=</string> </persistent>
</value>
</item>
<item>
<key> <string>field_id</string> </key>
<value> <string>my_string_field</string> </value>
<value> <string>my_view_mode_id</string> </value>
</item>
<item>
<key> <string>form_id</string> </key>
......@@ -83,14 +89,23 @@
<key> <string>target</string> </key>
<value> <string>Click to edit the target</string> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string>ID</string> </value>
</item>
</dictionary>
</value>
</item>
</dictionary>
</pickle>
</record>
<record id="2" aka="AAAAAAAAAAI=">
<pickle>
<global name="Method" module="Products.Formulator.MethodField"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>method_name</string> </key>
<value> <string>Base_validateDocumentHasNoActivity</string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
......@@ -8,7 +8,6 @@
from the context update logic
"""
from Products.Formulator.Errors import FormValidationError
from Products.CMFActivity.Errors import ActivityPendingError
request=container.REQUEST
portal = context.getPortalObject()
......@@ -215,33 +214,31 @@ encapsulated_editor_list = []
MARKER = []
message = Base_translateString("Data updated.")
try:
# We process all the field in form and
# we check if they are in the request,
# then we edit them
for field in form.get_fields():
parseField(field)
## XXX We need to find a way not to use meta_type.
field_meta_type = field.meta_type
if field_meta_type == 'ProxyField':
field_meta_type = field.getRecursiveTemplateField().meta_type
if(field_meta_type == 'ListBox'):
editListBox(field, request.get(field.id))
elif(field_meta_type == 'MatrixBox'):
editMatrixBox(field, request.get(field.id))
# Return parsed values
if silent_mode: return (kw, encapsulated_editor_list), 'edit'
# Maybe we should build a list of objects we need
# Update basic attributes
context.edit(REQUEST=request, edit_order=edit_order, **kw)
for encapsulated_editor in encapsulated_editor_list:
encapsulated_editor.edit(context)
except ActivityPendingError,e:
message = Base_translateString("%s" % e)
# We process all the field in form and
# we check if they are in the request,
# then we edit them
for field in form.get_fields():
parseField(field)
## XXX We need to find a way not to use meta_type.
field_meta_type = field.meta_type
if field_meta_type == 'ProxyField':
field_meta_type = field.getRecursiveTemplateField().meta_type
if(field_meta_type == 'ListBox'):
editListBox(field, request.get(field.id))
elif(field_meta_type == 'MatrixBox'):
editMatrixBox(field, request.get(field.id))
# Return parsed values
if silent_mode: return (kw, encapsulated_editor_list), 'edit'
# Maybe we should build a list of objects we need
# Update basic attributes
context.edit(REQUEST=request, edit_order=edit_order, **kw)
for encapsulated_editor in encapsulated_editor_list:
encapsulated_editor.edit(context)
if message_only:
return message
......
"""Refuse changing the id of an object with pending activities.
"""
document = context.aq_parent.aq_parent.aq_parent
if editor != document.getId():
return not document.hasActivity()
return True
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="PythonScript" module="Products.PythonScripts.PythonScript"/>
</pickle>
<pickle>