I've done some more work on my alternative to turbogears.flash(), and
it's pretty decent as this point (at least I think so), and it might
be useful for others.
What it does:
* Multiple messages
* Different classes of message (warning, error, etc.), which can be
styled accordingly.
* HTML in messages (only if explicitly allowed, and not the default)
* Logger-like invocation: f.warning("The sky is falling!")
* Hideable messages (tell a message to go away once and you'll never
see it again)
It works based on sessions, rather than a cookie. To use it in any
controller, you just instantiate a Flash2 object and call one of the
methods to add a message:
f = Flash2()
f.info("Hello, world!")
You can even do it all at once if you're only going to have one
message:
Flash2().info("Hello, world -- in one line!")
Here's a more complete example controller:
@expose(template=".templates.test_flash2")
def test_flash2(self):
f2 = Flash2()
f2.info("This is the first message. It's an info.")
f2.warning("This is the second message. It's a warning.",
hideable=True)
f2.error("This is the third message. It's an error!", growl=True)
f2.info("This is the fourth message. It's an info.",
hideable=True)
f2.warning("This is the fifth message. It has some <strong>HTML</
strong> in it! <a href='http://www.google.com/'>Google</a> rocks.",
html=True)
f2.warning("This is the sixth message. It is NOT an HTML message,
but yet it has some <strong>HTML</strong> in it! <a href='http://
www.google.com/'>Google</a> rocks.")
f2.ok("Super!")
f2.add_message("There's a <strong>bug</strong> on this page.",
'bug', True)
f2.add_message("Heavy construction underway!", 'construction')
f2.add_message("There's a feed here somewhere.", 'feed')
return dict()
You can see the results in a screenshot here: http://www.anseljh.com/code/flash2/flash2.png
(Note that the fourth message doesn't show up; the "Hide" link was
previously clicked.)
Here's how to implement it.
(1) Add the Flash2Message and Flash2 classes to your controllers.py:
class Flash2Message:
def __init__(self, msg, cls='info', html=False, hideable=False):
self.message = msg
self.css = cls
self.html = html
self.hideable = hideable
self.md5 = md5.md5(self.message).hexdigest()
class Flash2:
def __init__(self):
self.messages = []
self.messages_dict = {'info':[], 'warning':[], 'error':[], 'ok':
[]}
self.generator = self._generator() # use f2.generator.next() to
get messages
self.next = self._generator # next() method to make class iterable
def __iter__(self):
return self.generator
def add_message(self, msg, cls, html=False, growl=False,
hideable=False):
m = Flash2Message(msg, cls, html, hideable)
self.messages.append(m)
if cls not in self.messages_dict:
self.messages_dict[cls] = []
self.messages_dict[cls].append(m)
cherrypy.session['flash2'] = self # stick the generator in the TG/
CP session; use it in master template
def _generator(self):
"""
Build a generator for getting messages from a Flash2 instance
"""
while(1):
try:
m = self.messages.pop(0) # pop the first Flash2Message in the
list
yield m
except IndexError:
raise StopIteration
def info(self, msg, html=False, hideable=False, growl=False):
self.add_message(msg, 'info', html, growl, hideable)
def warning(self, msg, html=False, hideable=False, growl=False):
self.add_message(msg, 'warning', html, growl, hideable)
def error(self, msg, html=False, hideable=False, growl=False):
self.add_message(msg, 'error', html, growl, hideable)
def ok(self, msg, html=False, hideable=False, growl=False):
self.add_message(msg, 'ok', html, growl, hideable)
# ignore all the 'growl' stuff -- that's remnants from debugging
(2) Add this at the top of your master.kid template -- after the
DOCTYPE but before the <html> tag:
<?python
from cherrypy import request as cp_request
from cherrypy import session as cp_session
?>
(3) Add this to your master template wherever you want the flash
messages to show up:
<div py:if="cp_session.has_key('flash2')" id="flash2">
<div py:for="message in cp_session.get('flash2')"
class="message ${message.css}" id="flash2_${message.md5}"
py:if="'flash2_hide_'+message.md5 not in cp_request.simple_cookie">
<div py:if="message.hideable" class="hide">
<a href="#" onclick="javascript:
MochiKit.Visual.fade('flash2_${message.md5}', {'duration':0.25});
document.cookie='flash2_hide_${message.md5}=True';">Hide</a>
</div>
<span py:if="message.html"
py:content="XML(message.message)" />
<span py:if="not message.html"
py:content="message.message" />
</div>
</div>
(4) Put some styles in your master stylesheet. Here's what I have:
#flash2 .message {
/* base styles for all flash2 messages */
border-width: 3px;
border-style: solid;
margin: .5em;
padding: .5em;
}
#flash2 .error {
/*background: url(/static/images/warning.gif) left center no-
repeat;*/
background-color: #fdd; /* red */
border-color: #f00;
}
#flash2 .warning {
background-color: #ffd; /* yellow? */
border-color: #ff0;
}
#flash2 .info {
padding-left: 50px;
background: url(/static/images/info.png) left center no-repeat;
background-color: #eeeeff;
border-color: #bbd;
}
#flash2 .ok {
padding-left: 50px;
background: url(/static/images/ok.png) left center no-repeat;
background-color: #cceecc;
border-color: #81db28;
}
#flash2 .bug {
padding-left: 50px;
background: url(/static/images/bug001.gif) .2em center no-repeat;
background-color: #fdd; /* red */
border-color: #f00;
}
#flash2 .construction {
padding-left: 50px;
/*background: url(/static/images/construction1.gif) .2em center no-
repeat;*/
background-color: #ffd; /* yellow? */
border-color: #ff0;
}
#flash2 .feed {
padding-left: 50px;
background: url(/static/images/feed-icon-24x24.png) .2em center no-
repeat;
background-color: #ffd; /* yellow? */
border-color: #ff8509;
}
#flash2 .message .hide {
float: right;
font-size: 85%;
}
And that's it.
Comments? Suggestions for improvement?
-Ansel
P.S. The "Hide" link includes a MochiKit 1.4 call
(MochiKit.Visual.fade()), which I don't think will work on a stock TG
1.0.x installation. You can replace that part with some other DOM
scripting to hide the message element, but that's left as an exercise
for the reader.
P.P.S. Because it depends on sessions, you'll have to enable them in
your config files. See: http://docs.turbogears.org/1.0/Sessions
You should add this to the recipe section of the wiki. Something like
this should eventually be included in TG.
Or even better, turn it into an installable package and add it to the
cheeseshop/cogbin!
You could also turn this into a widget that adds itself to
turboears.view.variable_providers in its initialization method. Then you
can put the template and JS/CSS stuff in it as well.
Chris
Thank you, it's just like what I needed, the way I would have done it :-)
I suggest you to bind a Flash() instance to the current request, and
wrap inner methods:
> def get_flash_service():
> try:
> ret = cherrypy.request.flash_service
> except AttributeError:
> ret = cherrypy.request.flash_service = Flash()
> return ret
>
>
> def info(*args, **kw):
> get_flash_service().info(*args, **kw)
>
> def warning(*args, **kw):
> get_flash_service().warning(*args, **kw)
>
> def error(*args, **kw):
> get_flash_service().error(*args, **kw)
>
> def ok(*args, **kw):
> get_flash_service().ok(*args, **kw)
>
> def add_message(*args, **kw):
> get_flash_service().add_message(*args, **kw)
This way, you can just use
import flash
flash.warning('whatever')
On Jun 11, 3:17 am, Marco Mariani <marco.mari...@prometeia.it> wrote:
> anse...@gmail.com ha scritto:
This way:
>>> def get_flash_service():
>>> try:
>>> ret = cherrypy.request.flash_service
>>> except AttributeError:
>>> ret = cherrypy.request.flash_service = Flash()
>>> return ret
>>>
>>> def info(*args, **kw):
>>> get_flash_service().info(*args, **kw)
>>>
I use this method to attach any kind of object to the request (even
short-lived db or ldap connections)
I've already switched to your class and it's cute :)
import flash2
flash2.info('Yadda yadda')
...and it would just work by binding to the request like you suggest.
Ansel
On Jun 12, 8:10 am, Marco Mariani <marco.mari...@prometeia.it> wrote:
> anse...@gmail.com ha scritto: