Commit fe07070e authored by Vincent Pelletier's avatar Vincent Pelletier Committed by Julien Muchembled

Move selection checksum computation next to its verification

Allows getting current selection's MD5 even outside ListBox's renderer.
Signed-off-by: Julien Muchembled's avatarJulien Muchembled <jm@nexedi.com>
parent 055d0a69
...@@ -129,7 +129,6 @@ if dialog_method == \'Base_createRelation\':\n ...@@ -129,7 +129,6 @@ if dialog_method == \'Base_createRelation\':\n
if dialog_method == \'Folder_delete\':\n if dialog_method == \'Folder_delete\':\n
return context.Folder_delete(form_id=kw[\'form_id\'],\n return context.Folder_delete(form_id=kw[\'form_id\'],\n
selection_name=kw[\'selection_name\'],\n selection_name=kw[\'selection_name\'],\n
uids=kw[\'listbox_uid\'],\n
md5_object_uid_list=kw[\'md5_object_uid_list\'])\n md5_object_uid_list=kw[\'md5_object_uid_list\'])\n
\n \n
\n \n
......
...@@ -57,24 +57,13 @@ from Products.CMFCore.WorkflowCore import WorkflowException\n ...@@ -57,24 +57,13 @@ from Products.CMFCore.WorkflowCore import WorkflowException\n
\n \n
portal = context.getPortalObject()\n portal = context.getPortalObject()\n
Base_translateString = portal.Base_translateString\n Base_translateString = portal.Base_translateString\n
REQUEST = portal.REQUEST\n
\n \n
selected_uids = portal.portal_selections.updateSelectionCheckedUidList(\n
selection_name,listbox_uid,uids)\n
uids = portal.portal_selections.getSelectionCheckedUidsFor(selection_name)\n uids = portal.portal_selections.getSelectionCheckedUidsFor(selection_name)\n
\n if portal.portal_selections.selectionHasChanged(md5_object_uid_list, uids):\n
error = portal.portal_selections.selectionHasChanged(md5_object_uid_list,\n
uids)\n
\n
REQUEST=context.REQUEST\n
qs = \'\'\n
ret_url = context.absolute_url() + \'/\' + form_id\n
if error:\n
message = Base_translateString("Sorry, your selection has changed.")\n message = Base_translateString("Sorry, your selection has changed.")\n
qs = \'?portal_status_message=%s\' % message\n elif uids:\n
elif uids is not None:\n
# Check if there is some related objets.\n # Check if there is some related objets.\n
object_used = 0\n
\n
object_list = [x.getObject() for x in context.Folder_getDeleteObjectList(uid=uids)]\n object_list = [x.getObject() for x in context.Folder_getDeleteObjectList(uid=uids)]\n
object_used = sum([x.getRelationCountForDeletion() and 1 for x in object_list])\n object_used = sum([x.getRelationCountForDeletion() and 1 for x in object_list])\n
\n \n
...@@ -84,7 +73,6 @@ elif uids is not None:\n ...@@ -84,7 +73,6 @@ elif uids is not None:\n
else:\n else:\n
message = Base_translateString("Sorry, ${count} items are in use.",\n message = Base_translateString("Sorry, ${count} items are in use.",\n
mapping={\'count\': repr(object_used)})\n mapping={\'count\': repr(object_used)})\n
qs = \'?portal_status_message=%s\' % message \n
else:\n else:\n
\n \n
# Do not delete objects which have a workflow history \n # Do not delete objects which have a workflow history \n
...@@ -106,7 +94,7 @@ elif uids is not None:\n ...@@ -106,7 +94,7 @@ elif uids is not None:\n
\n \n
# Remove some objects\n # Remove some objects\n
try:\n try:\n
if object_to_remove_list != []:\n if object_to_remove_list:\n
if context.portal_type == \'Preference\':\n if context.portal_type == \'Preference\':\n
# Templates inside preference are not indexed, so we cannot pass\n # Templates inside preference are not indexed, so we cannot pass\n
# uids= to manage_delObjects and have to use ids=\n # uids= to manage_delObjects and have to use ids=\n
...@@ -121,7 +109,7 @@ elif uids is not None:\n ...@@ -121,7 +109,7 @@ elif uids is not None:\n
except ConflictError:\n except ConflictError:\n
raise\n raise\n
except Exception, message:\n except Exception, message:\n
qs = \'?portal_status_message=%s\' % message\n pass\n
else:\n else:\n
object_ids = [x.getId() for x in object_to_remove_list]\n object_ids = [x.getId() for x in object_to_remove_list]\n
comment = Base_translateString(\'Deleted objects: ${object_ids}\',\n comment = Base_translateString(\'Deleted objects: ${object_ids}\',\n
...@@ -147,7 +135,6 @@ elif uids is not None:\n ...@@ -147,7 +135,6 @@ elif uids is not None:\n
raise\n raise\n
except:\n except:\n
not_deleted_count += 1\n not_deleted_count += 1\n
pass\n
\n \n
# Generate message\n # Generate message\n
if not_deleted_count == 1:\n if not_deleted_count == 1:\n
...@@ -158,20 +145,20 @@ elif uids is not None:\n ...@@ -158,20 +145,20 @@ elif uids is not None:\n
mapping={\'count\': not_deleted_count})\n mapping={\'count\': not_deleted_count})\n
qs = \'?portal_status_message=%s\' % message\n qs = \'?portal_status_message=%s\' % message\n
\n \n
# make sure nothing is checked after\n
portal.portal_selections.setSelectionCheckedUidsFor(selection_name, ())\n
else:\n else:\n
message = Base_translateString("Please select one or more items first.")\n message = Base_translateString("Please select one or more items first.")\n
qs = \'?portal_status_message=%s\' % message\n
\n \n
# make sure nothing is checked after\n return REQUEST.RESPONSE.redirect("%s/%s?portal_status_message=%s"\n
portal.portal_selections.setSelectionCheckedUidsFor(selection_name, [])\n % (context.absolute_url_path(), form_id, message))\n
return REQUEST.RESPONSE.redirect("%s%s" % (ret_url, qs))\n
]]></string> </value> ]]></string> </value>
</item> </item>
<item> <item>
<key> <string>_params</string> </key> <key> <string>_params</string> </key>
<value> <string>form_id=\'\',selection_index=None,object_uid=None,selection_name=None,field_id=None,uids=None,cancel_url=\'\',listbox_uid=[],md5_object_uid_list=\'\'</string> </value> <value> <string>form_id=\'\',selection_index=None,object_uid=None,selection_name=None,field_id=None,cancel_url=\'\',md5_object_uid_list=\'\'</string> </value>
</item> </item>
<item> <item>
<key> <string>id</string> </key> <key> <string>id</string> </key>
......
...@@ -405,20 +405,23 @@ class TestERP5Core(ERP5TypeTestCase, ZopeTestCase.Functional): ...@@ -405,20 +405,23 @@ class TestERP5Core(ERP5TypeTestCase, ZopeTestCase.Functional):
response = self.publish(self.portal_id, self.auth) response = self.publish(self.portal_id, self.auth)
self.assertEquals(HTTP_OK, response.getStatus()) self.assertEquals(HTTP_OK, response.getStatus())
def _Folder_delete(self, *object_list):
selection_name = 'test_selection'
portal_selections = self.portal.portal_selections
portal_selections.setSelectionCheckedUidsFor(selection_name,
[x.getUid() for x in object_list])
md5_string = portal_selections.getSelectionChecksum(selection_name)
return object_list[0].getParentValue().Folder_delete(
selection_name=selection_name, md5_object_uid_list=md5_string)
def test_Folder_delete(self): def test_Folder_delete(self):
module = self.portal.newContent(portal_type='Folder', id='test_folder') module = self.portal.newContent(portal_type='Folder', id='test_folder')
document_1 = module.newContent(portal_type='Folder', id='1') document_1 = module.newContent(portal_type='Folder', id='1')
document_2 = module.newContent(portal_type='Folder', id='2') document_2 = module.newContent(portal_type='Folder', id='2')
uid_list = [document_1.getUid(), document_2.getUid()]
self.portal.portal_selections.setSelectionParamsFor(
'test_selection', dict(uids=uid_list))
self.tic() self.tic()
md5_string = md5(str(sorted(map(str, uid_list)))).hexdigest() redirect = self._Folder_delete(document_1, document_2)
redirect = module.Folder_delete(selection_name='test_selection',
uids=uid_list,
md5_object_uid_list=md5_string)
self.assert_('Deleted.' in redirect, redirect) self.assert_('Deleted.' in redirect, redirect)
self.assertEquals(len(module.objectValues()), 0) self.assertEqual(module.objectCount(), 0)
def test_Folder_delete_related_object(self): def test_Folder_delete_related_object(self):
# deletion is refused if there are related objects # deletion is refused if there are related objects
...@@ -435,12 +438,7 @@ class TestERP5Core(ERP5TypeTestCase, ZopeTestCase.Functional): ...@@ -435,12 +438,7 @@ class TestERP5Core(ERP5TypeTestCase, ZopeTestCase.Functional):
self.assertEqual(2, organisation.getRelationCountForDeletion()) self.assertEqual(2, organisation.getRelationCountForDeletion())
self.assertEqual(0, person.getRelationCountForDeletion()) self.assertEqual(0, person.getRelationCountForDeletion())
def delete(assert_deleted, obj): def delete(assert_deleted, obj):
uid_list = [obj.getUid()] redirect = self._Folder_delete(obj)
self.portal.portal_selections.setSelectionParamsFor(
'test_selection', dict(uids=uid_list))
md5_string = md5(str(sorted(map(str, uid_list)))).hexdigest()
redirect = obj.getParentValue().Folder_delete(uids=uid_list,
selection_name='test_selection', md5_object_uid_list=md5_string)
self.assertTrue(('Sorry, 1 item is in use.', 'Deleted.')[assert_deleted] self.assertTrue(('Sorry, 1 item is in use.', 'Deleted.')[assert_deleted]
in redirect, redirect) in redirect, redirect)
self.tic() self.tic()
...@@ -463,21 +461,15 @@ class TestERP5Core(ERP5TypeTestCase, ZopeTestCase.Functional): ...@@ -463,21 +461,15 @@ class TestERP5Core(ERP5TypeTestCase, ZopeTestCase.Functional):
context=document_1, context=document_1,
base_category_list=('source',), base_category_list=('source',),
category_list=document_2.getRelativeUrl()) category_list=document_2.getRelativeUrl())
uid_list = [document_2.getUid(), ]
self.portal.portal_selections.setSelectionParamsFor(
'test_selection', dict(uids=uid_list))
self.tic() self.tic()
self.assertEquals([document_1], self.assertEquals([document_1],
self.portal.portal_categories.getRelatedValueList(document_2)) self.portal.portal_categories.getRelatedValueList(document_2))
md5_string = md5(str(sorted(map(str, uid_list)))).hexdigest()
document_1.manage_permission('View', [], acquire=0) document_1.manage_permission('View', [], acquire=0)
document_1.manage_permission('Access contents information', [], acquire=0) document_1.manage_permission('Access contents information', [], acquire=0)
redirect = module.Folder_delete(selection_name='test_selection', redirect = self._Folder_delete(document_2)
uids=uid_list,
md5_object_uid_list=md5_string)
self.assert_('Sorry, 1 item is in use.' in redirect, redirect) self.assert_('Sorry, 1 item is in use.' in redirect, redirect)
self.assertEquals(len(module.objectValues()), 2) self.assertEqual(module.objectCount(), 2)
def test_getPropertyForUid(self): def test_getPropertyForUid(self):
error_list = [] error_list = []
......
...@@ -50,7 +50,6 @@ from Products.ERP5Type.Globals import InitializeClass, get_request ...@@ -50,7 +50,6 @@ from Products.ERP5Type.Globals import InitializeClass, get_request
from Products.PythonScripts.Utility import allow_class from Products.PythonScripts.Utility import allow_class
from Products.PageTemplates.PageTemplateFile import PageTemplateFile from Products.PageTemplates.PageTemplateFile import PageTemplateFile
from warnings import warn from warnings import warn
from hashlib import md5
import cgi import cgi
DEFAULT_LISTBOX_DISPLAY_STYLE = 'table' DEFAULT_LISTBOX_DISPLAY_STYLE = 'table'
...@@ -2560,20 +2559,14 @@ class ListBoxHTMLRenderer(ListBoxRenderer): ...@@ -2560,20 +2559,14 @@ class ListBoxHTMLRenderer(ListBoxRenderer):
"""Generate a MD5 checksum against checked uids. This is used to confirm """Generate a MD5 checksum against checked uids. This is used to confirm
that selected values do not change between a display of a dialog and an execution. that selected values do not change between a display of a dialog and an execution.
FIXME: this should only use getCheckedUidList, but Folder_deleteObjectList does not use FIXME: SelectionTool.getSelectionChecksum's uid_list parameter is
the feature that checked uids are used when no list method is specified. deprecated, but Folder_deleteObjectList does not use the feature that
checked uids are used when no list method is specified.
""" """
checked_uid_list = self.request.get('uids') return self.getSelectionTool().getSelectionChecksum(
if checked_uid_list is None: self.getSelectionName(),
checked_uid_list = self.getCheckedUidList() uid_list=self.request.get('uids'),
if checked_uid_list is not None: )
checked_uid_list = [str(uid) for uid in checked_uid_list]
checked_uid_list.sort()
md5_string = md5(str(checked_uid_list)).hexdigest()
else:
md5_string = None
return md5_string
def getPhysicalRoot(self): def getPhysicalRoot(self):
"""Return the physical root (an Application object). This method is required for """Return the physical root (an Application object). This method is required for
......
...@@ -1170,13 +1170,26 @@ class SelectionTool( BaseTool, SimpleItem ): ...@@ -1170,13 +1170,26 @@ class SelectionTool( BaseTool, SimpleItem ):
""" """
We want to be sure that the selection did not change We want to be sure that the selection did not change
""" """
return md5_string != self._getUIDListChecksum(object_uid_list)
security.declareProtected(ERP5Permissions.View, 'getSelectionChecksum')
def getSelectionChecksum(self, selection_name, uid_list=None):
"""Generate an MD5 checksum against checked uids. This is used to confirm
that selected values do not change between a display of a dialog and an
execution.
uid_list (deprecated)
For backward compatibility with code not updating selected uids.
"""
if uid_list is None:
uid_list = self.getSelectionCheckedUidsFor(selection_name)
return self._getUIDListChecksum(uid_list)
def _getUIDListChecksum(self, uid_list):
if uid_list is None:
return None
# XXX To avoid the difference of the string representations of int and long, # XXX To avoid the difference of the string representations of int and long,
# convert each element to a string. # convert each element to a string.
object_uid_list = [str(x) for x in object_uid_list] return md5(str(sorted(map(str, uid_list)))).hexdigest()
object_uid_list.sort()
new_md5_string = md5(str(object_uid_list)).hexdigest()
return md5_string != new_md5_string
# Related document searching # Related document searching
def viewSearchRelatedDocumentDialog(self, index, form_id, def viewSearchRelatedDocumentDialog(self, index, form_id,
......
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