[Zope3-Users] Page Notification pattern and cookies - getting
cookies set in the same request?
Jeff Shell
eucci.group at gmail.com
Fri Jan 6 19:13:38 EST 2006
Hello. Something came up today that I'm not sure how to deal with. A
long and wordy explanation follows, but the quick question I have is
this: without using sessions, or anything else tied into transactions
/ persistence but tied to the current visitor, how can I write to a
very small 'messenger' store to send messages like "Item Deleted" or
"3 Items Added to Cart" that, when read back from, removes those
message. I just tried a cookie based implementation, but since the
request and response track cookies differently, I can't selectively
write and retrieve/clear these messages as easily as I'd like. The
important thing about these messages is that (a) the user sees them
only once, and (b) they can survive redirects. I've done this using a
session based utility that I wrote a long time ago, but the session
based one has some problems with a new feature I'm trying (interfering
with "undo last") and I'm not sure that doing so much frequent writing
/ clearing to a ZODB storage is such a good thing.
I don't know if having event listeners pull data out of cookies and
putting them into a threading.local type container is a good way to
go, writing anything new and expiring anything old when the response
is written out - and in-request I just read and write from that little
transient thing. Or can a _v_attribute (don't know where it'd attach)
or request annotation work? I still don't understand the life cycles
of some of these options, nor how to ensure they don't interfere with
other transactions so that "Undo Last" doesn't undo the removing of
the notification message and nothing else.
-- LONG VERSION --
I've been using a session based Page Messenger for a while now. I
wrote about it a couple of years ago [1]. The use case is for
displaying messages to the user in a uniform way - not tacking them
onto a query string, not stuffing them into the current view or
request, but putting them somewhere that can survive the current
request so that redirects can still show the message.
[1] http://euc.cx/toulouse/archives/2003/12/22/page_messages.html
I've loved this pattern, and I've just used Zope's sessioning support
(for better or worse) to deal with it, and it's generally worked out
great. In Zope 2 I put it behind a CMF Tool-like interface and turned
it into a utility for our Zope 3 apps. Ruby on Rails calls it 'flash',
and Subway and TurboGears both sport this flash feature, often just
using cookies.
The pattern is basically this: add a message to the messenger, it
stores it ... somewhere, anywhere. Later, in the same request or
perhaps the next, the messages are retrieved (my implementation's
always allowed for multiple messages). When they are retrieved,
they're removed from the storage so the user sees the message only
once (ie - reloading the page won't bring back the "Deleted Item 'x'"
message).
Today I was working on having 'undo' messages, similar to those im
GMail. I was frustrated trying to put a javascript confirm on a
browserMenuItem ('delete'), when I remembered that Zope has this great
undoable database. So I borrowed code from zope.app.undo.browser to
just do the 'undo last', and returned a message that had a link to the
@@undolast view. So a user can visit an object directly and see a
'Delete' action. When they delete, they go back to a content listing
and now get the message "Deleted 'Foo Title' [foo id] (Undo)".
But since my page messenger uses sessions - the 'undo last action'
would only undo the removal of the **undo message** from the messenger
container in the session. This is because the page is a redirect, and
at the time of clicking "undo last", the last action is the page view
that retrieved the notification message and removed it from the
notification store.
I've long wanted to try some alternate page messenger implementations
anyways, and went about doing a cookie based one. I've got it working
- including handling multiple messages of different styles (info,
error, warning, etc)... to a point. It works great on a redirect, but
not within the same request. Which is obvious to me now - I'm only
looking at cookies in the Request, not the Response. Likewise, items
aren't cleared from the Request until the end of the response (whereas
sessions would go immediately).
Is there a better pattern I could be using here, where cookies are
kindof the fallback? I guess I'd like to safely:
* Load all of the current messages stored in cookies at the beginning
of request handling and put them in a container that exists only for
the duration of the request.
* Access that container throughout the current request to get messages
for display and set new ones for display on the next page view,
whether it's from a call later in that request (return
self.template...) or after a redirect.
* Write undisplayed messages out to cookies at the end of the request
(again, whether it was a redirect or full page view).
Of course, no visitor should see any other visitors messages. This is
why sessions worked nicely - I didn't really have to think about it.
But I may have been using them poorly by doing so much writing just to
display a message to a user.
Can I listen to events to catch the beginning of the request, before
traversal, and get all of these message cookies out of the request and
into something like a 'thread.local' based object? I don't understand
threads and requests all that well, I must admit, so I don't know if
that's applicable.
Then, my utility does all of its interaction with that 'local' storage
that is not transactional or ZODB based: just something holding a
handful (typically one) message to display to the current site
visitor.
At the end of the request cycle, before the response is sent, another
event listener sets or expires cookies based on whats in that
container. Is that possible?
More information about the Zope3-users
mailing list