|
View:
New views
2 Messages
—
Rating Filter:
Alert me
|
|
|
Alternative Session ImplementationHi all,
I had been joining the GetPaid sprint at the Plone conference in Budapest for one day and we shortly discussed the session implementation. If I see it correctly the problems of ticket 209 are resolved (except of broken ZODBs maybe). We could not come up with a completely sessionless cart implementation, I think mainly because nobody knew the GetPaid design well enough. Anyway I have implemented the session based approach using zope.app.session. It consists of an ISession utility and an ISessionData local persistent utility to store the data. This means that sessions (in our case those of anonymous users) are written to the ZODB the portal is saved in. This can give you performance problems, depending on the number of anonymous shopping cart users. I don't know if there are bigger GetPaid installations. But it should be possible replace the local persistent utility with another implementation that does the loads/dumps dance internaly and saves the data somewhere else. Attached you find a patch converting the implementation and the tests to use zope.app.session. The functional anon cart tests are failing atm, but I did not have time to look at them nor do I know if they failed before already. ..Carsten --~--~---------~--~----~------------~-------~--~----~ GetPaid for Plone: http://www.plonegetpaid.com (overview info) | http://code.google.com/p/getpaid (code and issue tracker) You received this message because you are subscribed to the Google Groups "getpaid-dev" group. To post to this group, send email to getpaid-dev@... To unsubscribe from this group, send email to getpaid-dev+unsubscribe@... For more options, visit this group at http://groups.google.com/group/getpaid-dev?hl=en?hl=en -~----------~----~----~----~------~----~------~--~--- Index: Products/PloneGetPaid/configure.zcml =================================================================== --- Products/PloneGetPaid/configure.zcml (Revision 3169) +++ Products/PloneGetPaid/configure.zcml (Arbeitskopie) @@ -222,11 +222,58 @@ /> +<!-- zope.app.session for shopping carts --> -<subscriber - for="* - getpaid.core.interfaces.IPayableCreationEvent" - handler=".events.payable_created" - /> - + +<adapter factory=".cart.ClientId" + permission="zope.Public" /> + +<configure package="zope.app.session"> + + <adapter + factory=".session.Session" + provides="zope.app.session.interfaces.ISession" + permission="zope.Public" + /> + + <adapter + factory=".session.Session" + provides="zope.traversing.interfaces.IPathAdapter" + name="session" + /> + + <class class=".session.Session"> + <allow interface=".interfaces.ISession" /> + <implements interface="zope.traversing.interfaces.IPathAdapter" /> + </class> + + + <class class=".session.PersistentSessionDataContainer"> + <require + interface=".interfaces.ISessionDataContainer" + permission="zope.Public" /> + <!-- <require --> + <!-- set_schema=".interfaces.ISessionDataContainer" --> + <!-- permission="zope.ManageServices" /> --> + </class> + + + <class class=".session.SessionData"> + <allow interface=".interfaces.ISessionData" /> + </class> + + <class class=".session.SessionPkgData"> + <allow interface=".interfaces.ISessionPkgData" /> + </class> + + <subscriber + for="zope.app.appsetup.IDatabaseOpenedEvent" + handler=".bootstrap.bootStrapSubscriber" + /> + + <subscriber + for="zope.publisher.interfaces.http.IHTTPVirtualHostChangedEvent" + handler=".http.notifyVirtualHostChanged" + /> </configure> +</configure> \ No newline at end of file Index: Products/PloneGetPaid/config.py =================================================================== --- Products/PloneGetPaid/config.py (Revision 3169) +++ Products/PloneGetPaid/config.py (Arbeitskopie) @@ -4,3 +4,6 @@ PLONE3 = True except ImportError: PLONE3 = False + +SESSION_KEY = 'PloneGetPaid' +CART_KEY = 'getpaid.cart' Index: Products/PloneGetPaid/tests/test_cart.py =================================================================== --- Products/PloneGetPaid/tests/test_cart.py (Revision 3169) +++ Products/PloneGetPaid/tests/test_cart.py (Arbeitskopie) @@ -5,6 +5,7 @@ from zope.component import getUtility from Testing.ZopeTestCase import ZopeDocTestSuite +from Testing.ZopeTestCase.utils import setupCoreSessions from Products.Five.utilities.marker import mark from utils import optionflags @@ -13,8 +14,24 @@ from Products.PloneGetPaid import interfaces from getpaid.core.interfaces import IShoppingCartUtility + +class DummySessionAndBrowserIdManager: + + __test_browser_id__ = 'funkyTestBrowserId' + + def getBrowserIdManager(self): + return self + + def getBrowserId(self): + return self.__test_browser_id__ + + class TestCart(PloneGetPaidTestCase): + def afterSetUp(self): + super(TestCart, self).afterSetUp() + setattr(self.app.REQUEST, 'SESSION', DummySessionAndBrowserIdManager()) + def mySetup(self): self.setRoles(('Manager',)) id = self.portal.invokeFactory('Document', 'doc') @@ -161,18 +178,18 @@ ... ValueError: Malformed key: example:malformed:key """ - + def pauseBrowserSession(self): """Utility that disables the current session. The session can be resumed using resumeBrowserSession. """ - self._paused_browser_id = self.portal.REQUEST.browser_id_ - self.portal.REQUEST.browser_id_ = None + self._paused_browser_id = self.portal.REQUEST.SESSION.__test_browser_id__ + self.portal.REQUEST.SESSION.__test_browser_id__ = None def resumeBrowserSession(self): """Utility to restore the previous session. """ - self.portal.REQUEST.browser_id_ = self._paused_browser_id + self.portal.REQUEST.SESSION.__test_browser_id__ = self._paused_browser_id def test_suite(): Index: Products/PloneGetPaid/setuphandlers.py =================================================================== --- Products/PloneGetPaid/setuphandlers.py (Revision 3169) +++ Products/PloneGetPaid/setuphandlers.py (Arbeitskopie) @@ -14,11 +14,11 @@ from Products.PloneGetPaid.Extensions.install import install_plone3_portlets from Products.PloneGetPaid.Extensions.install import setup_payment_options from Products.PloneGetPaid.Extensions.install import register_shopping_cart_utility - from Products.PloneGetPaid.Extensions.install import setup_addressbook from Products.PloneGetPaid.Extensions.install import setup_named_orders from Products.PloneGetPaid.Extensions.install import setup_settings from Products.PloneGetPaid.Extensions.install import register_shopping_cart_utility +from Products.PloneGetPaid.Extensions.install import register_session_data_utility from Products.PloneGetPaid.config import PLONE3 @@ -82,6 +82,9 @@ print >> out, "Registering shopping cart utility" register_shopping_cart_utility(site) + print >> out, "Registering session data utility" + register_session_data_utility(site) + print >> out, "Notifying Installation" notify_install( site ) Index: Products/PloneGetPaid/cart.py =================================================================== --- Products/PloneGetPaid/cart.py (Revision 3169) +++ Products/PloneGetPaid/cart.py (Arbeitskopie) @@ -3,16 +3,21 @@ $Id$ """ -from zope.component import getUtility +from zope.app.session.interfaces import IClientId +from zope.app.session.interfaces import ISession +from zope.component import adapts, getUtility from zope.interface import implements +from zope.publisher.interfaces import IRequest from getpaid.core.cart import ShoppingCart from getpaid.core.interfaces import IShoppingCartUtility from Products.CMFCore.utils import getToolByName +from Products.PloneGetPaid.config import CART_KEY, SESSION_KEY from persistent import Persistent from BTrees.OOBTree import OOBTree from AccessControl import getSecurityManager + class ShoppingCartUtility(Persistent): implements(IShoppingCartUtility) @@ -73,40 +78,30 @@ self._sessions[uid] = cart return cart - def _getCartForSession(self, context, create=False, browser_id=None): - session_manager = getToolByName(context, 'session_data_manager') - if browser_id is None: - if not session_manager.hasSessionData() and not create: - return - session = session_manager.getSessionData() - else: - session = session_manager.getSessionDataByKey(browser_id) - if session is None: - return - if not session.has_key('getpaid.cart'): - if create: - session['getpaid.cart'] = cart = ShoppingCart() - else: - return None - return session['getpaid.cart'] + session = ISession(context.REQUEST) + if browser_id is not None: + # we overwrite the client_id that was set during the ISession + # initialization + session.client_id = browser_id + + sessiondata = session[SESSION_KEY] + cart = sessiondata.get(CART_KEY, None) + if create and cart is None: + sessiondata[CART_KEY] = cart = ShoppingCart() + return cart + def _getDisposableCart(self, context, browser_id=None): return ShoppingCart() def _getMultiShotCart(self, context, cart_id=None): - session_manager = getToolByName(context, 'session_data_manager') + sessiondata = ISession(context.REQUEST)[SESSION_KEY] + key = "%s.%s" % (CART_KEY, cart_id) + if not sessiondata.has_key(key): + sessiondata[key] = ShoppingCart() + return sessiondata[key] - key = "getpaid.cart.%s" % cart_id - - session = session_manager.getSessionData() - - if not session.has_key(key): - session[key] = cart = ShoppingCart() - - return session[key] - - def destroy(self, context, key=None): """ Destroy the cart. """ @@ -125,37 +120,26 @@ else: return self._destroyCartForSession(context) - def _destroyCartForUser(self, context, uid): if self._sessions.has_key(uid): del self._sessions[uid] - def _destroyCartForSession(self, context, browser_id=None): - session_manager = getToolByName(context, 'session_data_manager') - if browser_id is None: - if not session_manager.hasSessionData(): #nothing to destroy - return None - session = session_manager.getSessionData() - else: - session = session_manager.getSessionDataByKey(browser_id) - if session is None: - return - if not session.has_key('getpaid.cart'): - return - del session['getpaid.cart'] + session = ISession(context.REQUEST) + if browser_id is not None: + # overwrite the client_id that was set + # during initialization + session.client_id = browser_id + sessiondata = session[SESSION_KEY] + if sessiondata.has_key(CART_KEY): + del sessiondata[CART_KEY] def _destroyMultiShotCart(self, context, cart_id=None): - session_manager = getToolByName(context, 'session_data_manager') + sessiondata = ISession(context.REQUEST)[SESSION_KEY] + key = "%s.%s" % (CART_KEY, cart_id) + if sessiondata.has_key(key): + del session[key] - key = "getpaid.cart.%s" % cart_id - session = session_manager.getSessionData() - - if not session.has_key(key): - return - - del session[key] - def getKey(self, context): """Return key that can be used to recover the cart for the current user or session. @@ -164,15 +148,12 @@ if uid is not None: return 'user:%s' % uid else: - session_manager = getToolByName(context, 'session_data_manager') - if not session_manager.hasSessionData(): + session = ISession(context.REQUEST) + sessiondata = session[SESSION_KEY] + if not sessiondata.has_key(CART_KEY): return None - session = session_manager.getSessionData() - if not session.has_key('getpaid.cart'): - return None - return 'session:%s' % session.token + return 'session:%s' % session.client_id - def _decodeKey(self, key): try: name, value = key.split(':', 1) @@ -185,3 +166,19 @@ def manage_fixupOwnershipAfterAdd(self): pass + + +class ClientId(str): + """See zope.app.interfaces.utilities.session.IClientId + + An IClientId Utility that uses Zope2's browser_id_manager + implementation. + """ + implements(IClientId) + adapts(IRequest) + + def __new__(cls, request): + return str.__new__( + cls, request.SESSION.getBrowserIdManager().getBrowserId() + ) + Index: Products/PloneGetPaid/Extensions/install.py =================================================================== --- Products/PloneGetPaid/Extensions/install.py (Revision 3169) +++ Products/PloneGetPaid/Extensions/install.py (Arbeitskopie) @@ -12,6 +12,9 @@ from zope.event import notify from zope.app.component.hooks import setSite from zope.app.component.interfaces import ISite +from zope.app.session.interfaces import ISessionDataContainer +from zope.app.session.session import PersistentSessionDataContainer + from Products.PloneGetPaid import generations, preferences, addressbook, namedorder from Products.PloneGetPaid.interfaces import IGetPaidManagementOptions, IAddressBookUtility, INamedOrderUtility from Products.PloneGetPaid.config import PLONE3 @@ -61,6 +64,21 @@ # BBB for Zope 2.9 sm.registerUtility(interface=IOrderManager, utility=manager) +def register_session_data_utility( self ): + portal = getToolByName( self, 'portal_url').getPortalObject() + sm = portal.getSiteManager() + is_already_registered = [u for u in sm.getUtilitiesFor(ISessionDataContainer)] + if not len(is_already_registered): + session_data = PersistentSessionDataContainer() + session_data.timeout = 604800 # 7 days + session_data.resolution = 100 + sm.registerUtility(session_data, ISessionDataContainer) + try: + sm.registerUtility(component=session_data, provided=ISessionDataContainer) + except TypeError: + # BBB for Zope 2.9 + sm.registerUtility(interface=ISessionDataContainer, utility=session_data) + def setup_intid( self ): portal = getToolByName(self, 'portal_url').getPortalObject() try: |
|
|
Re: Alternative Session Implementation> Hi all, > > I had been joining the GetPaid sprint at the Plone conference in Budapest > for one day and we shortly discussed the session implementation. If I see > it correctly the problems of ticket 209 are resolved (except of broken > ZODBs maybe). > Hi, I still get this bug triggered every 2 days. Maybe my zodb is corrupted, but my workaround for that bug is reinstalling getpaid... I tried using the script to fix it, but did not work... > We could not come up with a completely sessionless cart implementation, I > think mainly because nobody knew the GetPaid design well enough. Anyway I > have implemented the session based approach using zope.app.session. It > consists of an ISession utility and an ISessionData local persistent > utility to store the data. This means that sessions (in our case those of > anonymous users) are written to the ZODB the portal is saved in. This can > give you performance problems, depending on the number of anonymous > shopping cart users. I don't know if there are bigger GetPaid installations. > But it should be possible replace the local persistent utility with another > implementation that does the loads/dumps dance internaly and saves the > data somewhere else. > > Attached you find a patch converting the implementation and the tests to > use zope.app.session. The functional anon cart tests are failing atm, but I > did not have time to look at them nor do I know if they failed before > already. > > ..Carsten > > > > --~--~---------~--~----~------------~-------~--~----~ GetPaid for Plone: http://www.plonegetpaid.com (overview info) | http://code.google.com/p/getpaid (code and issue tracker) You received this message because you are subscribed to the Google Groups "getpaid-dev" group. To post to this group, send email to getpaid-dev@... To unsubscribe from this group, send email to getpaid-dev+unsubscribe@... For more options, visit this group at http://groups.google.com/group/getpaid-dev?hl=en?hl=en -~----------~----~----~----~------~----~------~--~--- |
| Free embeddable forum powered by Nabble | Forum Help |