Commit bd67acb3 authored by Kazuhiko Shiozaki's avatar Kazuhiko Shiozaki

rewrite anonymous support in selection.

* now Selection for anonymous is stored per its content and its key is embedded in ListBox rendering.
* we no longer provide cookie to each anonymous user.
* thus same URL (i.e. same parameter) for any anonymous user should have the same result.
* Selection storage for anonymous can be still different from the storage for the normal users.
parent d18c8480
...@@ -52,6 +52,7 @@ ...@@ -52,6 +52,7 @@
<key> <string>_body</string> </key> <key> <string>_body</string> </key>
<value> <string>kept_names = (\'editable_mode\', \'ignore_layout\', # erp5_web\n <value> <string>kept_names = (\'editable_mode\', \'ignore_layout\', # erp5_web\n
\'selection_name\', \'selection_index\', # list mode\n \'selection_name\', \'selection_index\', # list mode\n
\'selection_key\', # list mode\n
\'bt_list\', # business template installation system\n \'bt_list\', # business template installation system\n
\'ignore_hide_rows\',\n \'ignore_hide_rows\',\n
)\n )\n
......
...@@ -546,6 +546,11 @@ ...@@ -546,6 +546,11 @@
</tbody>\n </tbody>\n
\n \n
</table>\n </table>\n
<input type="hidden" name="selection_name_selection_key" value="md5"\n
tal:define="selection_key here/getSelectionKey"\n
tal:condition="selection_key"\n
tal:attributes="name string:${selection_name}_selection_key;\n
value selection_key" />\n
</div>\n </div>\n
\n \n
<div class="listbox-footer">\n <div class="listbox-footer">\n
......
...@@ -1196,7 +1196,20 @@ class ListBoxRenderer: ...@@ -1196,7 +1196,20 @@ class ListBoxRenderer:
if self.getListMethodName(): if self.getListMethodName():
# Update parameters, only if list_method is defined. # Update parameters, only if list_method is defined.
# (i.e. do not update parameters in listboxes intended to show a previously defined selection. # (i.e. do not update parameters in listboxes intended to show a previously defined selection.
params.update(self.request.form) listbox_prefix = '%s_' % self.getId()
for k, v in self.request.form.iteritems():
# Ignore parameters for other listboxes and selection keys.
if 'listbox_' in k or k.endswith('selection_key'):
continue
elif k.startswith(listbox_prefix):
k = k[len(listbox_prefix):]
# <listbox_field_id>_uid is already handled in
# ListBoxValidator.validate() and putting uid in selection
# will limit the contents for the selection.
if k != 'uid':
params[k] = v
else:
params[k] = v
for k, v in self.getDefaultParamList(): for k, v in self.getDefaultParamList():
params.setdefault(k, v) params.setdefault(k, v)
...@@ -2102,6 +2115,11 @@ class ListBoxRenderer: ...@@ -2102,6 +2115,11 @@ class ListBoxRenderer:
""" """
return self.render(**kw) return self.render(**kw)
def getSelectionKey(self):
selection_tool = self.getSelectionTool()
selection_name = self.getSelectionName()
return selection_tool.getAnonymousSelectionKey(selection_name)
class ListBoxRendererLine: class ListBoxRendererLine:
"""This class describes a line in a ListBox to assist ListBoxRenderer. """This class describes a line in a ListBox to assist ListBoxRenderer.
""" """
...@@ -2447,6 +2465,9 @@ class ListBoxHTMLRendererLine(ListBoxRendererLine): ...@@ -2447,6 +2465,9 @@ class ListBoxHTMLRendererLine(ListBoxRendererLine):
params.extend(('selection_name=%s' % selection_name, params.extend(('selection_name=%s' % selection_name,
'selection_index=%s' % self.index, 'selection_index=%s' % self.index,
'reset:int=1')) 'reset:int=1'))
selection_tool = self.getObject().getPortalObject().portal_selections
if selection_tool._isAnonymous():
params.append('selection_key=%s' % selection.getAnonymousSelectionKey())
if params: if params:
url = '%s?%s' % (url, '&amp;'.join(params)) url = '%s?%s' % (url, '&amp;'.join(params))
except AttributeError: except AttributeError:
......
...@@ -33,6 +33,7 @@ from OFS.Traversable import Traversable ...@@ -33,6 +33,7 @@ from OFS.Traversable import Traversable
from AccessControl import ClassSecurityInfo from AccessControl import ClassSecurityInfo
from Products.ERP5Type import Permissions as ERP5Permissions from Products.ERP5Type import Permissions as ERP5Permissions
from Products.PythonScripts.Utility import allow_class from Products.PythonScripts.Utility import allow_class
from hashlib import md5
# Put a try in front XXX # Put a try in front XXX
from Products.CMFCategory.Category import Category from Products.CMFCategory.Category import Category
...@@ -368,6 +369,10 @@ class Selection(Acquisition.Implicit, Traversable, Persistent): ...@@ -368,6 +369,10 @@ class Selection(Acquisition.Implicit, Traversable, Persistent):
def getReportTreeMode(self): def getReportTreeMode(self):
return getattr(self, 'report_tree_mode', 0) return getattr(self, 'report_tree_mode', 0)
security.declarePublic('getAnonymousSelectionKey')
def getAnonymousSelectionKey(self):
return md5(repr(dict([(k, v) for k, v in self.__dict__.iteritems() if k != 'index']))).hexdigest()
InitializeClass(Selection) InitializeClass(Selection)
allow_class(Selection) allow_class(Selection)
......
...@@ -32,7 +32,6 @@ ...@@ -32,7 +32,6 @@
""" """
from OFS.SimpleItem import SimpleItem from OFS.SimpleItem import SimpleItem
from Products.CMFCore.utils import UniqueObject
from Products.ERP5Type.Globals import InitializeClass, DTMLFile, PersistentMapping, get_request from Products.ERP5Type.Globals import InitializeClass, DTMLFile, PersistentMapping, get_request
from AccessControl import ClassSecurityInfo from AccessControl import ClassSecurityInfo
from Products.ERP5Type.Tool.BaseTool import BaseTool from Products.ERP5Type.Tool.BaseTool import BaseTool
...@@ -43,8 +42,6 @@ from Products.ERP5Form.Selection import Selection, DomainSelection ...@@ -43,8 +42,6 @@ from Products.ERP5Form.Selection import Selection, DomainSelection
from ZPublisher.HTTPRequest import FileUpload from ZPublisher.HTTPRequest import FileUpload
from hashlib import md5 from hashlib import md5
import string, re import string, re
from time import time
from random import random
from urlparse import urlsplit, urlunsplit from urlparse import urlsplit, urlunsplit
from zLOG import LOG, INFO, WARNING from zLOG import LOG, INFO, WARNING
from Acquisition import aq_base from Acquisition import aq_base
...@@ -273,6 +270,17 @@ class SelectionTool( BaseTool, SimpleItem ): ...@@ -273,6 +270,17 @@ class SelectionTool( BaseTool, SimpleItem ):
return None return None
return selection(method=method, context=context, REQUEST=REQUEST, params=params) return selection(method=method, context=context, REQUEST=REQUEST, params=params)
def _getRequest(self, REQUEST=None):
if REQUEST is None:
REQUEST = getattr(self, 'REQUEST', None)
return REQUEST
def _getSelectionKeyFromRequest(self, selection_name, REQUEST):
REQUEST = self._getRequest(REQUEST=REQUEST)
if REQUEST is not None:
return REQUEST.get('%s_selection_key' % selection_name, None) or \
REQUEST.get('selection_key', None)
security.declareProtected(ERP5Permissions.View, 'getSelectionFor') security.declareProtected(ERP5Permissions.View, 'getSelectionFor')
def getSelectionFor(self, selection_name, REQUEST=None): def getSelectionFor(self, selection_name, REQUEST=None):
""" """
...@@ -283,6 +291,11 @@ class SelectionTool( BaseTool, SimpleItem ): ...@@ -283,6 +291,11 @@ class SelectionTool( BaseTool, SimpleItem ):
if not selection_name: if not selection_name:
return None return None
selection = self._getSelectionFromContainer(selection_name) selection = self._getSelectionFromContainer(selection_name)
if selection is None and self._isAnonymous():
selection_key = self._getSelectionKeyFromRequest(selection_name, REQUEST)
if selection_key is not None:
selection = self.getAnonymousSelection(selection_key, selection_name)
self._setSelectionToContainer(selection_name, selection)
if selection is not None: if selection is not None:
return selection.__of__(self) return selection.__of__(self)
...@@ -296,19 +309,21 @@ class SelectionTool( BaseTool, SimpleItem ): ...@@ -296,19 +309,21 @@ class SelectionTool( BaseTool, SimpleItem ):
""" """
if not selection_name: if not selection_name:
return return
anonymous_uid = REQUEST and REQUEST.get('anonymous_uid', None)
if anonymous_uid is not None:
self.REQUEST.response.setCookie('anonymous_uid', anonymous_uid,
path='/')
if not (selection_object is None or if not (selection_object is None or
selection_name == selection_object.name): selection_name == selection_object.name):
LOG('SelectionTool', WARNING, LOG('SelectionTool', WARNING,
"Selection not set: new Selection name ('%s') differs from existing one ('%s')" % \ "Selection not set: new Selection name ('%s') differs from existing one ('%s')" % \
(selection_name, (selection_name,
selection_object.name)) selection_object.name))
elif self.getSelectionFor(selection_name) != selection_object: elif self.getSelectionFor(selection_name, REQUEST=REQUEST) != selection_object:
self._setSelectionToContainer(selection_name, selection_object) self._setSelectionToContainer(selection_name, selection_object)
if selection_object is None and self._isAnonymous():
REQUEST = self._getRequest(REQUEST=REQUEST)
for key in ('%s_selection_key' % selection_name, 'selection_key'):
try:
del REQUEST.form[key]
except KeyError:
pass
security.declareProtected(ERP5Permissions.View, 'getSelectionParamsFor') security.declareProtected(ERP5Permissions.View, 'getSelectionParamsFor')
def getSelectionParamsFor(self, selection_name, params=None, REQUEST=None): def getSelectionParamsFor(self, selection_name, params=None, REQUEST=None):
...@@ -447,7 +462,10 @@ class SelectionTool( BaseTool, SimpleItem ): ...@@ -447,7 +462,10 @@ class SelectionTool( BaseTool, SimpleItem ):
""" """
selection = self.getSelectionFor(selection_name, REQUEST=REQUEST) selection = self.getSelectionFor(selection_name, REQUEST=REQUEST)
if selection: if selection:
return selection.getListUrl() url = selection.getListUrl()
if self._isAnonymous() and '?' in url:
url += '&selection_key=%s' % self._getSelectionKeyFromRequest(selection_name, REQUEST)
return url
else: else:
return None return None
...@@ -669,88 +687,30 @@ class SelectionTool( BaseTool, SimpleItem ): ...@@ -669,88 +687,30 @@ class SelectionTool( BaseTool, SimpleItem ):
""" """
Access first item in a selection Access first item in a selection
""" """
if not REQUEST: return self._redirectToIndex(0, selection_name, form_id, REQUEST)
REQUEST = get_request()
selection = self.getSelectionFor(selection_name, REQUEST=REQUEST)
if selection:
method = self.unrestrictedTraverse(selection.method_path)
selection_list = selection(method = method, context=self, REQUEST=REQUEST)
if len(selection_list):
o = selection_list[0]
url = o.absolute_url()
form_id = self._getExistingFormId(o.getObject(), form_id)
else:
url = REQUEST.getURL()
else:
url = REQUEST.getURL()
ignore_layout = int(REQUEST.get('ignore_layout', 0))
if form_id != 'view':
url += '/%s' % form_id
url += '?selection_index=%s&selection_name=%s' % (0, selection_name)
if ignore_layout:
url += '&ignore_layout:int=1'
REQUEST.RESPONSE.redirect(url)
security.declareProtected(ERP5Permissions.View, 'viewLast') security.declareProtected(ERP5Permissions.View, 'viewLast')
def viewLast(self, selection_index='', selection_name='', form_id='view', REQUEST=None): def viewLast(self, selection_index='', selection_name='', form_id='view', REQUEST=None):
""" """
Access last item in a selection Access last item in a selection
""" """
if not REQUEST: return self._redirectToIndex(-1, selection_name, form_id, REQUEST)
REQUEST = get_request()
selection = self.getSelectionFor(selection_name, REQUEST=REQUEST)
if selection:
method = self.unrestrictedTraverse(selection.method_path)
selection_list = selection(method = method, context=self, REQUEST=REQUEST)
if len(selection_list):
o = selection_list[-1]
url = o.absolute_url()
form_id = self._getExistingFormId(o.getObject(), form_id)
else:
url = REQUEST.getURL()
else:
url = REQUEST.getURL()
ignore_layout = int(REQUEST.get('ignore_layout', 0))
if form_id != 'view':
url += '/%s' % form_id
url += '?selection_index=%s&selection_name=%s' % (-1, selection_name)
if ignore_layout:
url += '&ignore_layout:int=1'
REQUEST.RESPONSE.redirect(url)
security.declareProtected(ERP5Permissions.View, 'viewNext') security.declareProtected(ERP5Permissions.View, 'viewNext')
def viewNext(self, selection_index='', selection_name='', form_id='view', REQUEST=None): def viewNext(self, selection_index='', selection_name='', form_id='view', REQUEST=None):
""" """
Access next item in a selection Access next item in a selection
""" """
if not REQUEST: return self._redirectToIndex(int(selection_index) + 1, selection_name, form_id, REQUEST)
REQUEST = get_request()
selection = self.getSelectionFor(selection_name, REQUEST=REQUEST)
if selection:
method = self.unrestrictedTraverse(selection.method_path)
selection_list = selection(method = method, context=self, REQUEST=REQUEST)
if len(selection_list):
o = selection_list[(int(selection_index) + 1) % len(selection_list)]
url = o.absolute_url()
form_id = self._getExistingFormId(o.getObject(), form_id)
else:
url = REQUEST.getURL()
else:
url = REQUEST.getURL()
ignore_layout = int(REQUEST.get('ignore_layout', 0))
if form_id != 'view':
url += '/%s' % form_id
url += '?selection_index=%s&selection_name=%s' % (int(selection_index)+1,
selection_name)
if ignore_layout:
url += '&ignore_layout:int=1'
REQUEST.RESPONSE.redirect(url)
security.declareProtected(ERP5Permissions.View, 'viewPrevious') security.declareProtected(ERP5Permissions.View, 'viewPrevious')
def viewPrevious(self, selection_index='', selection_name='', form_id='view', REQUEST=None): def viewPrevious(self, selection_index='', selection_name='', form_id='view', REQUEST=None):
""" """
Access previous item in a selection Access previous item in a selection
""" """
return self._redirectToIndex(int(selection_index) - 1, selection_name, form_id, REQUEST)
def _redirectToIndex(self, selection_index, selection_name, form_id, REQUEST):
if not REQUEST: if not REQUEST:
REQUEST = get_request() REQUEST = get_request()
selection = self.getSelectionFor(selection_name, REQUEST=REQUEST) selection = self.getSelectionFor(selection_name, REQUEST=REQUEST)
...@@ -758,7 +718,9 @@ class SelectionTool( BaseTool, SimpleItem ): ...@@ -758,7 +718,9 @@ class SelectionTool( BaseTool, SimpleItem ):
method = self.unrestrictedTraverse(selection.method_path) method = self.unrestrictedTraverse(selection.method_path)
selection_list = selection(method = method, context=self, REQUEST=REQUEST) selection_list = selection(method = method, context=self, REQUEST=REQUEST)
if len(selection_list): if len(selection_list):
o = selection_list[(int(selection_index) - 1) % len(selection_list)] if selection_index > 0:
selection_index = selection_index % len(selection_list)
o = selection_list[selection_index]
url = o.absolute_url() url = o.absolute_url()
form_id = self._getExistingFormId(o.getObject(), form_id) form_id = self._getExistingFormId(o.getObject(), form_id)
else: else:
...@@ -768,13 +730,13 @@ class SelectionTool( BaseTool, SimpleItem ): ...@@ -768,13 +730,13 @@ class SelectionTool( BaseTool, SimpleItem ):
ignore_layout = int(REQUEST.get('ignore_layout', 0)) ignore_layout = int(REQUEST.get('ignore_layout', 0))
if form_id != 'view': if form_id != 'view':
url += '/%s' % form_id url += '/%s' % form_id
url += '?selection_index=%s&selection_name=%s' % (int(selection_index)-1, url += '?selection_index=%s&selection_name=%s' % (selection_index, selection_name)
selection_name)
if ignore_layout: if ignore_layout:
url += '&ignore_layout:int=1' url += '&ignore_layout:int=1'
if self._isAnonymous():
url += '&selection_key=%s' % self.getAnonymousSelectionKey(selection_name, REQUEST=REQUEST)
REQUEST.RESPONSE.redirect(url) REQUEST.RESPONSE.redirect(url)
# ListBox related methods # ListBox related methods
security.declareProtected(ERP5Permissions.View, 'firstPage') security.declareProtected(ERP5Permissions.View, 'firstPage')
...@@ -1467,12 +1429,6 @@ class SelectionTool( BaseTool, SimpleItem ): ...@@ -1467,12 +1429,6 @@ class SelectionTool( BaseTool, SimpleItem ):
if user_id is not None: if user_id is not None:
return user_id return user_id
user_id = self.portal_membership.getAuthenticatedMember().getUserName() user_id = self.portal_membership.getAuthenticatedMember().getUserName()
if user_id == 'Anonymous User' and self.getAnonymousStorage() is not None:
anonymous_uid = self.REQUEST.get('anonymous_uid', None)
if anonymous_uid is None:
anonymous_uid = md5('%s.%s' % (time(), random())).hexdigest()
self.REQUEST['anonymous_uid'] = anonymous_uid
user_id = 'Anonymous:%s' % anonymous_uid
tv['_user_id'] = user_id tv['_user_id'] = user_id
return user_id return user_id
...@@ -1498,6 +1454,25 @@ class SelectionTool( BaseTool, SimpleItem ): ...@@ -1498,6 +1454,25 @@ class SelectionTool( BaseTool, SimpleItem ):
temporary_selection_dict[selection_name]: temporary_selection_dict[selection_name]:
temporary_selection_dict[selection_name].pop() temporary_selection_dict[selection_name].pop()
def getAnonymousSelection(self, key, selection_name):
container_id = '_v_anonymous_selection_container'
storage = self.getAnonymousStorage() or self.getStorage()
container = self._getContainerFromStorage(container_id, storage)
return container.getSelection(key, selection_name)
def getAnonymousSelectionKey(self, selection_name, REQUEST=None):
if not self._isAnonymous():
return ''
selection = self.getSelectionFor(selection_name, REQUEST=REQUEST)
if selection is None:
return ''
key = selection.getAnonymousSelectionKey()
container_id = '_v_anonymous_selection_container'
storage = self.getAnonymousStorage() or self.getStorage()
container = self._getContainerFromStorage(container_id, storage)
container.setSelection(key, selection_name, selection)
return key
def _getSelectionFromContainer(self, selection_name): def _getSelectionFromContainer(self, selection_name):
user_id = self._getUserId() user_id = self._getUserId()
if user_id is None: return None if user_id is None: return None
...@@ -1540,15 +1515,20 @@ class SelectionTool( BaseTool, SimpleItem ): ...@@ -1540,15 +1515,20 @@ class SelectionTool( BaseTool, SimpleItem ):
self.getTemporarySelectionDict().keys())) self.getTemporarySelectionDict().keys()))
def _isAnonymous(self): def _isAnonymous(self):
return self.portal_membership.isAnonymousUser() return self._getUserId() == 'Anonymous User'
def _getContainer(self): def _getContainer(self):
if self._isAnonymous(): if self._isAnonymous():
container_id = '_v_anonymous_selection_container' tv = getTransactionalVariable()
storage = self.getAnonymousStorage() or self.getStorage() storage = tv.setdefault('_transactional_selection_container', {})
container = TransactionalCacheContainer(storage)
return container
else: else:
container_id = '_v_selection_container' container_id = '_v_selection_container'
storage = self.getStorage() storage = self.getStorage()
return self._getContainerFromStorage(container_id, storage)
def _getContainerFromStorage(self, container_id, storage):
container = getattr(aq_base(self), container_id, None) container = getattr(aq_base(self), container_id, None)
if container is None: if container is None:
if storage.startswith('portal_memcached/'): if storage.startswith('portal_memcached/'):
...@@ -1567,10 +1547,11 @@ class SelectionTool( BaseTool, SimpleItem ): ...@@ -1567,10 +1547,11 @@ class SelectionTool( BaseTool, SimpleItem ):
InitializeClass( SelectionTool ) InitializeClass( SelectionTool )
class MemcachedContainer(object): class BaseContainer(object):
def __init__(self, container): def __init__(self, container):
self._container = container self._container = container
class MemcachedContainer(BaseContainer):
def getSelectionNameList(self, user_id): def getSelectionNameList(self, user_id):
return [] return []
...@@ -1589,10 +1570,11 @@ class MemcachedContainer(object): ...@@ -1589,10 +1570,11 @@ class MemcachedContainer(object):
def deleteGlobalSelection(self, user_id, selection_name): def deleteGlobalSelection(self, user_id, selection_name):
pass pass
class PersistentMappingContainer(object): class TransactionalCacheContainer(MemcachedContainer):
def __init__(self, container): def setSelection(self, user_id, selection_name, selection):
self._container = container self._container.__setitem__('%s-%s' % (user_id, selection_name), aq_base(selection))
class PersistentMappingContainer(BaseContainer):
def getSelectionNameList(self, user_id): def getSelectionNameList(self, user_id):
try: try:
return self._container[user_id].keys() return self._container[user_id].keys()
......
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