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 @@
<key> <string>_body</string> </key>
<value> <string>kept_names = (\'editable_mode\', \'ignore_layout\', # erp5_web\n
\'selection_name\', \'selection_index\', # list mode\n
\'selection_key\', # list mode\n
\'bt_list\', # business template installation system\n
\'ignore_hide_rows\',\n
)\n
......
......@@ -546,6 +546,11 @@
</tbody>\n
\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
\n
<div class="listbox-footer">\n
......
......@@ -1196,7 +1196,20 @@ class ListBoxRenderer:
if self.getListMethodName():
# Update parameters, only if list_method is defined.
# (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():
params.setdefault(k, v)
......@@ -2102,6 +2115,11 @@ class ListBoxRenderer:
"""
return self.render(**kw)
def getSelectionKey(self):
selection_tool = self.getSelectionTool()
selection_name = self.getSelectionName()
return selection_tool.getAnonymousSelectionKey(selection_name)
class ListBoxRendererLine:
"""This class describes a line in a ListBox to assist ListBoxRenderer.
"""
......@@ -2447,6 +2465,9 @@ class ListBoxHTMLRendererLine(ListBoxRendererLine):
params.extend(('selection_name=%s' % selection_name,
'selection_index=%s' % self.index,
'reset:int=1'))
selection_tool = self.getObject().getPortalObject().portal_selections
if selection_tool._isAnonymous():
params.append('selection_key=%s' % selection.getAnonymousSelectionKey())
if params:
url = '%s?%s' % (url, '&amp;'.join(params))
except AttributeError:
......
......@@ -33,6 +33,7 @@ from OFS.Traversable import Traversable
from AccessControl import ClassSecurityInfo
from Products.ERP5Type import Permissions as ERP5Permissions
from Products.PythonScripts.Utility import allow_class
from hashlib import md5
# Put a try in front XXX
from Products.CMFCategory.Category import Category
......@@ -368,6 +369,10 @@ class Selection(Acquisition.Implicit, Traversable, Persistent):
def getReportTreeMode(self):
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)
allow_class(Selection)
......
......@@ -32,7 +32,6 @@
"""
from OFS.SimpleItem import SimpleItem
from Products.CMFCore.utils import UniqueObject
from Products.ERP5Type.Globals import InitializeClass, DTMLFile, PersistentMapping, get_request
from AccessControl import ClassSecurityInfo
from Products.ERP5Type.Tool.BaseTool import BaseTool
......@@ -43,8 +42,6 @@ from Products.ERP5Form.Selection import Selection, DomainSelection
from ZPublisher.HTTPRequest import FileUpload
from hashlib import md5
import string, re
from time import time
from random import random
from urlparse import urlsplit, urlunsplit
from zLOG import LOG, INFO, WARNING
from Acquisition import aq_base
......@@ -273,6 +270,17 @@ class SelectionTool( BaseTool, SimpleItem ):
return None
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')
def getSelectionFor(self, selection_name, REQUEST=None):
"""
......@@ -283,6 +291,11 @@ class SelectionTool( BaseTool, SimpleItem ):
if not selection_name:
return None
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:
return selection.__of__(self)
......@@ -296,19 +309,21 @@ class SelectionTool( BaseTool, SimpleItem ):
"""
if not selection_name:
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
selection_name == selection_object.name):
LOG('SelectionTool', WARNING,
"Selection not set: new Selection name ('%s') differs from existing one ('%s')" % \
(selection_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)
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')
def getSelectionParamsFor(self, selection_name, params=None, REQUEST=None):
......@@ -447,7 +462,10 @@ class SelectionTool( BaseTool, SimpleItem ):
"""
selection = self.getSelectionFor(selection_name, REQUEST=REQUEST)
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:
return None
......@@ -669,88 +687,30 @@ class SelectionTool( BaseTool, SimpleItem ):
"""
Access first item in a selection
"""
if not 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)
return self._redirectToIndex(0, selection_name, form_id, REQUEST)
security.declareProtected(ERP5Permissions.View, 'viewLast')
def viewLast(self, selection_index='', selection_name='', form_id='view', REQUEST=None):
"""
Access last item in a selection
"""
if not 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)
return self._redirectToIndex(-1, selection_name, form_id, REQUEST)
security.declareProtected(ERP5Permissions.View, 'viewNext')
def viewNext(self, selection_index='', selection_name='', form_id='view', REQUEST=None):
"""
Access next item in a selection
"""
if not 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)
return self._redirectToIndex(int(selection_index) + 1, selection_name, form_id, REQUEST)
security.declareProtected(ERP5Permissions.View, 'viewPrevious')
def viewPrevious(self, selection_index='', selection_name='', form_id='view', REQUEST=None):
"""
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:
REQUEST = get_request()
selection = self.getSelectionFor(selection_name, REQUEST=REQUEST)
......@@ -758,7 +718,9 @@ class SelectionTool( BaseTool, SimpleItem ):
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)]
if selection_index > 0:
selection_index = selection_index % len(selection_list)
o = selection_list[selection_index]
url = o.absolute_url()
form_id = self._getExistingFormId(o.getObject(), form_id)
else:
......@@ -768,13 +730,13 @@ class SelectionTool( BaseTool, SimpleItem ):
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)
url += '?selection_index=%s&selection_name=%s' % (selection_index, selection_name)
if ignore_layout:
url += '&ignore_layout:int=1'
if self._isAnonymous():
url += '&selection_key=%s' % self.getAnonymousSelectionKey(selection_name, REQUEST=REQUEST)
REQUEST.RESPONSE.redirect(url)
# ListBox related methods
security.declareProtected(ERP5Permissions.View, 'firstPage')
......@@ -1467,12 +1429,6 @@ class SelectionTool( BaseTool, SimpleItem ):
if user_id is not None:
return user_id
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
return user_id
......@@ -1498,6 +1454,25 @@ class SelectionTool( BaseTool, SimpleItem ):
temporary_selection_dict[selection_name]:
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):
user_id = self._getUserId()
if user_id is None: return None
......@@ -1540,15 +1515,20 @@ class SelectionTool( BaseTool, SimpleItem ):
self.getTemporarySelectionDict().keys()))
def _isAnonymous(self):
return self.portal_membership.isAnonymousUser()
return self._getUserId() == 'Anonymous User'
def _getContainer(self):
if self._isAnonymous():
container_id = '_v_anonymous_selection_container'
storage = self.getAnonymousStorage() or self.getStorage()
tv = getTransactionalVariable()
storage = tv.setdefault('_transactional_selection_container', {})
container = TransactionalCacheContainer(storage)
return container
else:
container_id = '_v_selection_container'
storage = self.getStorage()
return self._getContainerFromStorage(container_id, storage)
def _getContainerFromStorage(self, container_id, storage):
container = getattr(aq_base(self), container_id, None)
if container is None:
if storage.startswith('portal_memcached/'):
......@@ -1567,10 +1547,11 @@ class SelectionTool( BaseTool, SimpleItem ):
InitializeClass( SelectionTool )
class MemcachedContainer(object):
class BaseContainer(object):
def __init__(self, container):
self._container = container
class MemcachedContainer(BaseContainer):
def getSelectionNameList(self, user_id):
return []
......@@ -1589,10 +1570,11 @@ class MemcachedContainer(object):
def deleteGlobalSelection(self, user_id, selection_name):
pass
class PersistentMappingContainer(object):
def __init__(self, container):
self._container = container
class TransactionalCacheContainer(MemcachedContainer):
def setSelection(self, user_id, selection_name, selection):
self._container.__setitem__('%s-%s' % (user_id, selection_name), aq_base(selection))
class PersistentMappingContainer(BaseContainer):
def getSelectionNameList(self, user_id):
try:
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