From b3a221d4f239bb6fd98df6b75d7d881b988c8af9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9rome=20Perrin?= <jerome@nexedi.com> Date: Tue, 22 May 2007 09:51:50 +0000 Subject: [PATCH] This is Kazuhiko's patch to restore 2 level selections (1 level of mapping per user, then 1 level per selection_name), this also adds conflict resolution for Persistent Mapping storage in portal_selections. This conflict resolution is just to prevent restarting transactions. git-svn-id: https://svn.erp5.org/repos/public/erp5/trunk@14543 20353a03-c40f-0410-a6d1-a30d3c3de9de --- product/ERP5Form/Selection.py | 9 ++ product/ERP5Form/SelectionTool.py | 106 ++++++++++++++------ product/ERP5Form/tests/testSelectionTool.py | 7 +- 3 files changed, 86 insertions(+), 36 deletions(-) diff --git a/product/ERP5Form/Selection.py b/product/ERP5Form/Selection.py index a924eaf365..5647b66d34 100644 --- a/product/ERP5Form/Selection.py +++ b/product/ERP5Form/Selection.py @@ -176,6 +176,15 @@ class Selection(Acquisition.Implicit, Traversable, Persistent): v = v.encode('ascii') setattr(self, k, v) + def _p_independent(self) : + return 1 + + def _p_resolveConflict(self, oldState, savedState, newState) : + """Selection are edited by listboxs, so many conflicts can happen, + this is a workaround, so that no unnecessary transaction is + restarted.""" + return newState + def __call__(self, method = None, context=None, REQUEST=None): #LOG("Selection", 0, str((self.__dict__))) #LOG("Selection", 0, str(method)) diff --git a/product/ERP5Form/SelectionTool.py b/product/ERP5Form/SelectionTool.py index 65ab5bfbd1..efd3bbd940 100644 --- a/product/ERP5Form/SelectionTool.py +++ b/product/ERP5Form/SelectionTool.py @@ -160,11 +160,7 @@ class SelectionTool( BaseTool, UniqueObject, SimpleItem ): """ if self.isMemcachedUsed(): return [] - user_id = self.portal_membership.getAuthenticatedMember().getUserName() - if user_id is not None: - prefix = '%s-' % user_id - return [x.replace(prefix, '', 1) for x in self.getSelectionContainer().keys() if x.startswith(prefix)] - return [] + return self._getSelectionNameListFromContainer() # backward compatibility security.declareProtected(ERP5Permissions.View, 'getSelectionNames') @@ -182,34 +178,16 @@ class SelectionTool( BaseTool, UniqueObject, SimpleItem ): return None return selection(context=context) - def getSelectionContainer(self): - """ - Return the selection container. - """ - if self.isMemcachedUsed(): - value = getattr(self, '_v_selection_data', None) - if value is None: - value = self.getPortalObject().portal_memcached.getMemcachedDict(key_prefix='selection_tool') - setattr(self, '_v_selection_data', value) - else: - value = getattr(self, '_selection_data', None) - if value is None: - value = PersistentMapping() - setattr(self, '_selection_data', value) - return value - security.declareProtected(ERP5Permissions.View, 'getSelectionFor') def getSelectionFor(self, selection_name, REQUEST=None): """ Returns the selection instance for a given selection_name """ - user_id = self.portal_membership.getAuthenticatedMember().getUserName() - if user_id is not None: - if isinstance(selection_name, (tuple, list)): - selection_name = selection_name[0] - selection = self.getSelectionContainer().get('%s-%s' % (user_id, selection_name)) - if selection is not None: - return selection.__of__(self) + if isinstance(selection_name, (tuple, list)): + selection_name = selection_name[0] + selection = self._getSelectionFromContainer(selection_name) + if selection is not None: + return selection.__of__(self) security.declareProtected(ERP5Permissions.View, 'setSelectionFor') def setSelectionFor(self, selection_name, selection_object, REQUEST=None): @@ -220,10 +198,7 @@ class SelectionTool( BaseTool, UniqueObject, SimpleItem ): # Set the name so that this selection itself can get its own name. selection_object.edit(name = selection_name) - user_id = self.portal_membership.getAuthenticatedMember().getUserName() - if user_id is not None: - self.getSelectionContainer()['%s-%s' % (user_id, selection_name)] = aq_base(selection_object) - return + self._setSelectionToContainer(selection_name, selection_object) security.declareProtected(ERP5Permissions.View, 'getSelectionParamsFor') def getSelectionParamsFor(self, selection_name, params=None, REQUEST=None): @@ -1314,8 +1289,73 @@ class SelectionTool( BaseTool, UniqueObject, SimpleItem ): return aq_base_name return aq_base_name + def _getUserId(self): + return self.portal_membership.getAuthenticatedMember().getUserName() + + def _getSelectionFromContainer(self, selection_name): + user_id = self._getUserId() + if user_id is None: return None + if self.isMemcachedUsed(): + return self._getMemcachedContainer().get('%s-%s' % + (user_id, selection_name)) + else: + return self._getPersistentContainer(user_id).get(selection_name, + None) + + def _setSelectionToContainer(self, selection_name, selection): + user_id = self._getUserId() + if user_id is None: return + if self.isMemcachedUsed(): + self._getMemcachedContainer().set('%s-%s' % (user_id, selection_name), aq_base(selection)) + else: + self._getPersistentContainer(user_id)[selection_name] = aq_base(selection) + + def _getSelectionNameListFromContainer(self): + if self.isMemcachedUsed(): + return [] + else: + user_id = self._getUserId() + if user_id is None: return [] + return self._getPersistentContainer(user_id).keys() + + def _getMemcachedContainer(self): + value = getattr(self, '_v_selection_data', None) + if value is None: + value = self.getPortalObject().portal_memcached.getMemcachedDict(key_prefix='selection_tool') + setattr(self, '_v_selection_data', value) + return value + + def _getPersistentContainer(self, user_id): + if getattr(self, 'selection_data', None) is None: + self.selection_data = PersistentMapping() + if not self.selection_data.has_key(user_id): + self.selection_data[user_id] = SelectionPersistentMapping() + return self.selection_data[user_id] + InitializeClass( SelectionTool ) + +class SelectionPersistentMapping(PersistentMapping): + """A conflict-free PersistentMapping. + + Like selection objects, the purpose is to only prevent restarting + transactions. + """ + def _p_independent(self) : + return 1 + + def _p_resolveConflict(self, oldState, savedState, newState): + # update keys that only savedState has + oldState = newState + # dict returned by PersistentMapping.__getstate__ contains the data + # under _container key, so only compare this key (this is coupled with + # PersistentMapping implementation, but this implementation is lot likely + # to change, because it would break existing pickles). + oldState['_container'].update(savedState['_container']) + + return oldState + + class TreeListLine: def __init__(self,object,is_pure_summary,depth, is_open,select_domain_dict,exception_uid_list): self.object=object @@ -1529,4 +1569,4 @@ def createFolderMixInPageSelectionMethod(listbox_id): security_property = getattr(SelectionTool, security_property_id, None) if security_property is not None: new_security_property_id = '%s__roles__' % (new_property_id, ) - setattr(FolderMixIn, new_security_property_id, security_property) \ No newline at end of file + setattr(FolderMixIn, new_security_property_id, security_property) diff --git a/product/ERP5Form/tests/testSelectionTool.py b/product/ERP5Form/tests/testSelectionTool.py index 1d6252eedb..7ed58ffa85 100644 --- a/product/ERP5Form/tests/testSelectionTool.py +++ b/product/ERP5Form/tests/testSelectionTool.py @@ -68,8 +68,9 @@ class TestSelectionTool(ERP5TypeTestCase): self.portal_selections.getSelectionNameList()) self.assertEquals(['test_selection'], self.portal_selections.getSelectionNames()) - self.assert_(self.portal_selections.getSelectionContainer() is not None) - self.assert_(getattr(self.portal_selections, '_selection_data', None) + self.assert_(self.portal_selections._getPersistentContainer('manager') + is not None) + self.assert_(getattr(self.portal_selections, 'selection_data', None) is not None) # use memcached tool self.portal_selections.setStorage('Memcached Tool') @@ -77,7 +78,7 @@ class TestSelectionTool(ERP5TypeTestCase): self.portal_selections.getSelectionNameList()) self.assertEquals([], self.portal_selections.getSelectionNames()) - self.assert_(self.portal_selections.getSelectionContainer() is not None) + self.assert_(self.portal_selections._getMemcachedContainer() is not None) self.assert_(getattr(self.portal_selections, '_v_selection_data', None) is not None) -- 2.30.9