Flash notes with HTML tags

19 views
Skip to first unread message

Christoph Zwerschke

unread,
May 17, 2007, 11:35:51 AM5/17/07
to turbo...@googlegroups.com
Just noticed that I cannot include HTML tags or HTML entities in a
flash() note (they are printed literally), and even flash(kid.XML(...))
does not work, since the flash note is stored as text in a cookie.

If somebody had the same problem, the solution is to replace
py:content="tg_flash" with py:content="XML(tg_flash)" in master.kid.

I wanted to add this to the docu but found that flash() wasn't even
documented anywhere :-( or did I overlook something?

-- Chris

Noah Gift

unread,
May 17, 2007, 11:38:53 AM5/17/07
to turbo...@googlegroups.com
great tip...I was wondering how to do that.

Alberto Valverde

unread,
May 17, 2007, 11:47:50 AM5/17/07
to turbo...@googlegroups.com

On May 17, 2007, at 5:35 PM, Christoph Zwerschke wrote:

>
> Just noticed that I cannot include HTML tags or HTML entities in a
> flash() note (they are printed literally), and even flash(kid.XML
> (...))
> does not work, since the flash note is stored as text in a cookie.
>
> If somebody had the same problem, the solution is to replace
> py:content="tg_flash" with py:content="XML(tg_flash)" in master.kid.

I wonder if it would be a good idea to make it a default... Hmm,
could this open the door to XSS attacks? (I guess probably not since
the attacker would need to hijack the cookie, modify it, and then
make the attacked browser use it... but you never know...)


>
> I wanted to add this to the docu but found that flash() wasn't even
> documented anywhere :-( or did I overlook something?

Feel free to start a "flash" page :)

Alberto

Christoph Zwerschke

unread,
May 17, 2007, 12:33:50 PM5/17/07
to turbo...@googlegroups.com
Alberto Valverde wrote:
> I wonder if it would be a good idea to make it a default... Hmm,
> could this open the door to XSS attacks? (I guess probably not since
> the attacker would need to hijack the cookie, modify it, and then
> make the attacked browser use it... but you never know...)

Strangely, I was worried about that too, but came to the same conclusion
that you're lost anyway if somebody can hack your cookies.

>> I wanted to add this to the docu but found that flash() wasn't even
>> documented anywhere :-( or did I overlook something?
>
> Feel free to start a "flash" page :)

I think what's missing is a page explaining not only flash() but also
other basic stuff that does not fall into one of the big categories.
Maybe "TurboGears Basic Tricks" or "TurboGears Idioms" or even
"TurboGears Magic" (explaining all the automagical things going on under
the hood that are not explained elsewhere)?

-- chris


Alberto Valverde

unread,
May 17, 2007, 12:39:02 PM5/17/07
to turbo...@googlegroups.com

There's already a WidgetTips page but no TurbogearsTips that I'm
aware of. The later will be a nice title I think for a catch-all page
to dump all those useful idioms that pop up frequently in the ML.

Alberto

Matthew Bevan

unread,
May 17, 2007, 2:02:47 PM5/17/07
to turbo...@googlegroups.com
> If somebody had the same problem, the solution is to replace
> py:content="tg_flash" with py:content="XML(tg_flash)" in master.kid.

Useful. I had a slightly different problem - I needed to display
different "classes" of flash message - informational, warnings,
errors, or success. To do so I use the following:

<div py:if="value_of('tg_flash', None) and '::' in tg_flash"
py:attrs="class=tg_flash.split('::')[0]" id="flash">
<label>${tg_flash.split('::')[0].capitalize()}:</label>
<div py:content="XML(tg_flash.split('::')[1])">Test important
message.</div>
</div>
<div py:if="value_of('tg_flash', None) and '::' not in tg_flash"
id="flash" class="info">
<label>Information:</label>
<div py:content="XML(tg_flash)">Test important message.</div>
</div>

It'll probably be a bit more elegant in Genshi, what with it having
an "else" structure.

Matthew Bevan, Systems Administrator
Top Floor Computer Systems Ltd.


Jorge Godoy

unread,
May 17, 2007, 6:34:53 PM5/17/07
to turbo...@googlegroups.com
Christoph Zwerschke <ci...@online.de> writes:

There was a recipe for that that's been on the wiki for a while, but I
couldn't find it.

It might have been missed on some migration or link change... :-(

--
Jorge Godoy <jgo...@gmail.com>

Jorge Godoy

unread,
May 17, 2007, 6:37:35 PM5/17/07
to turbo...@googlegroups.com
Matthew Bevan <matt....@topfloor.ca> writes:

> Useful. I had a slightly different problem - I needed to display different
> "classes" of flash message - informational, warnings, errors, or success. To
> do so I use the following:

I pass the class together with the message. My JS for that is below:

################################################################################
function displayStatusMessage(status, msg, timeout) {
var msg = swapDOM('statusmessage', DIV({'id': 'statusmessage'},
DIV({'class': status}, msg)));
// Não defino o timeout no código, mas é interessante a implementação,
// então mantive aqui.
if (!timeout) { timeout=5 }
if (timeout) {
msg.hidecb = callLater(timeout, swapDOM, msg,
DIV({'id': 'statusmessage'}, null));
}
connect(msg, 'onclick', function(e) {
var msg = e.src();
if (msg.hidecb) {
msg.hidecb.cancel();
}
disconnect(msg);
swapDOM(msg, DIV({'id': 'statusmessage'}, null));
}
);
}
################################################################################


--
Jorge Godoy <jgo...@gmail.com>

Christoph Zwerschke

unread,
May 18, 2007, 4:15:29 AM5/18/07
to turbo...@googlegroups.com
Jorge Godoy schrieb:

> There was a recipe for that that's been on the wiki for a while, but I
> couldn't find it.
>
> It might have been missed on some migration or link change... :-(

Yes, seems I rediscovered an old trick. Found this, buried in the SVN:
http://trac.turbogears.org/browser/website/newdocs/docs/controllers/fancy.html?rev=2342&format=raw
And the old wiki even has the warning about XSS attacks:
http://trac.turbogears.org/wiki/FancyStatus.
I think that is a valid warning. You really must be careful what you
display in your status messages if you allow XML. So I don't think we
should make it the standard, but if you know what you're doing, it is
still a useful trick. Another reason for not making it the standard is
that it's sometimes useful to display Python variables in the news flash
during debugging, and object representations often use angle brackets.

Seems the process of TG documentation development is suboptimal :-)
Anyway I'm going to add the TurbogearsTips page suggested by Alberto now
where I will include these things so that they don't need to be
rediscovered a 3rd time..

-- Chris

Marco Mariani

unread,
May 18, 2007, 4:31:06 AM5/18/07
to turbo...@googlegroups.com
Jorge Godoy ha scritto:

>> Useful. I had a slightly different problem - I needed to display different
>> "classes" of flash message - informational, warnings, errors, or success. To
>> do so I use the following:
>>
>
> I pass the class together with the message.

I've done the same... the need is there, a handful of ways to resolve
it, could be useful to have a standard method

But I think it would be more interesting to handle several feedback
messages (e.g. via list pickling, or session) because in the general
case a single request handles more than one operation


Christoph Zwerschke

unread,
May 18, 2007, 4:34:47 AM5/18/07
to turbo...@googlegroups.com
Alberto Valverde wrote:
> There's already a WidgetTips page but no TurbogearsTips that I'm
> aware of. The later will be a nice title I think for a catch-all page
> to dump all those useful idioms that pop up frequently in the ML.

By the way, I noticed that some very useful Widget docs, such as
http://docs.turbogears.org/1.0/Widgets
http://docs.turbogears.org/1.0/WidgetsOverview
http://docs.turbogears.org/1.0/RoughDocs/WidgetTips
are currently orphaned in the Wiki, is this by intent?

And I also wondered, is there a way to find which pages link to a
certain page? Knowing this could help improve the quality of the wiki.

-- Chris

Christoph Zwerschke

unread,
May 18, 2007, 5:49:02 AM5/18/07
to turbo...@googlegroups.com
The page is now here:

http://docs.turbogears.org/1.0/RoughDocs/TurboGearsTips

You are invited to add your own tips there, but I suggest linking to
separate pages if the tips are too long.

-- Chris

Alberto Valverde

unread,
May 18, 2007, 7:02:51 AM5/18/07
to turbo...@googlegroups.com

Excellent!

Thanks Chris! :)

Alberto

ans...@gmail.com

unread,
May 18, 2007, 2:30:28 PM5/18/07
to TurboGears
These are good tips. Here's a related suggestion for 2.0: make the
flash work like a logger. For example:

turbogears.flash.info("An informational message...")
turbogears.flash.error("A big freakin' error!")

Each type of message would map to a CSS class, so you'd get something
like:

<div id="flash" class="flash error">
A big freakin' error!
</div>

That way, you could style the different types of flash messages with
simple CSS and hopefully avoid tricky DOM stuff.

Thoughts?

Christopher Arndt

unread,
May 18, 2007, 2:55:35 PM5/18/07
to turbo...@googlegroups.com
ans...@gmail.com schrieb:

> These are good tips. Here's a related suggestion for 2.0: make the
> flash work like a logger. For example:
>
> turbogears.flash.info("An informational message...")
> turbogears.flash.error("A big freakin' error!")
>
> Each type of message would map to a CSS class, so you'd get something
> like:
>
> <div id="flash" class="flash error">
> A big freakin' error!
> </div>
>
> That way, you could style the different types of flash messages with
> simple CSS and hopefully avoid tricky DOM stuff.

I have a widget that does almost exactly that. I haven't packaged it as
a separate widget yet, but I use it in my applications. You can look at
the source code of my CBlog app [1] to see it in use. Have a look at the
following files.

cblog/fflash.py
cblog/widgets/fancyflash.py
cblog/static/javascript/fancyflash.js
cblog/static/css/fancyflash.css

It also need the RUZEE.events JS library [2] packaged as a widget, which
can be found in the cblog/widgets/jslibs directory.

I works by encoding the flash message as JSON data and decoding it when
the page is rendered. To use it, you need to put the following in your
master template:

${tg_fancyflash(tg_flash)}

and then put the 'fancyflash' widget in tg.include_widgets, so that the
widget is present on every page. I use a function called
'register_sitewidgets' for that. It is defined in the file
cblog/widgets/base.py and called in cblog/controllers/__init__.py.


I hope I'll have the time to package fancyflash as a standalone widget
soon. If anybody wants to have a shot at it, feel free to do it, just
include me in the credits ;-) (and Splee, whose idea it was in the first
place)

Chris


[1] http://chrisarndt.de/projects/cblog/download/ (Unfortunately my blog
where you could see CBlog in action, is down atm, because the server
broke down)
[2] http://www.ruzee.com/blog/ruzeeevents/

Lee McFadden

unread,
May 18, 2007, 3:00:52 PM5/18/07
to turbo...@googlegroups.com
On 5/18/07, ans...@gmail.com <ans...@gmail.com> wrote:
>
> These are good tips. Here's a related suggestion for 2.0: make the
> flash work like a logger. For example:
>
> turbogears.flash.info("An informational message...")
> turbogears.flash.error("A big freakin' error!")
>

That's a nice syntax. I've created my own version of tg.flash that
takes the class as a second argument and defaults to whatever I set it
in my app.cfg, e.g:

myproject.flash("An informational message...")
myproject.flash("A big freakin' error!", "error")

Still, that alone doesn't solve the issue of adding things like links
to your flash message. In the end I gave up trying to hack tg.flash
and cookies to show messages and started using cherrypy.request to
store status messages. Since the data is still in pure python and
hasn't required serialisation into a cookie you can store elementtree
elements for direct output of links etc.

I'm not 100% sure what the best practice is for storing data in
cherrypy.request but I use it fairly liberally for this kind of thing.

I don't have any code that I can post at the moment as it's too
tightly tied with the project, but if there's some interest I could
probably break it out and publish it.

Lee

--
Lee McFadden

blog: http://www.splee.co.uk
work: http://fireflisystems.com
skype: fireflisystems

Matthew Bevan

unread,
May 18, 2007, 4:03:50 PM5/18/07
to turbo...@googlegroups.com
>> turbogears.flash.info("An informational message...")
>> turbogears.flash.error("A big freakin' error!")
>
> That's a nice syntax.

Indeed. My fancy Kid template uses the following syntax:

turbogears.flash("An informational message...")
turbogears.flash("error::Quite the error!")
turbogears.flash("warning::Warning.")
turbogears.flash("success::Congratulations!")

I like having full control over the name of the CSS class used. I
can basically have an unlimited number of different statuses, rather
than having to make-do with someone's pre-conceived notion of what
should be allowable - i.e. info, error, and warn.

I.e. class::Label::message formatting:

turbogears.flash("acc-create-success::Success::Congratulations, you
have successfully joined. Check your e-mail for activation
information.")

Then I can have decorative iconography implemented in CSS.

ans...@gmail.com

unread,
May 18, 2007, 4:12:25 PM5/18/07
to TurboGears
I just implemented my own idea. :) It's a turbogears.flash()
replacement that:

1. handles multiple messages
2. handles multiple classes of messages (info, warning, error), acting
like a logger
3. handles HTML, but only if you explicitly allow it

I tried to keep it as simple as possible. Here's how you use it:

# instantiate a Flash2 object
f2 = Flash2()

# Add some messages!
f2.info("This is the first message. It's an info.")
f2.warning("This is the second message. It's a warning.")
f2.error("This is the third message. It's an error!")
f2.info("This is the fourth message. It's an info.")

# For HTML, add on html=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)

If you like screenshots (and who doesn't?):
http://www.anseljh.com/code/flash2.png

Here's the code.

Two classes go in your controllers.py:

--------------
class Flash2Message:
def __init__(self, msg, cls='info', html=False):
self.message = msg
self.css = cls
self.html = html

class Flash2:
def __init__(self):
self.messages = []
self.messages_dict = {'info':[], 'warning':[], 'error':[]}
def add_message(self, msg, cls, html=False):
m = Flash2Message(msg, cls, html)
self.messages.append(m)
self.messages_dict[cls].append(m)
def info(self, msg, html=False):
self.add_message(msg, 'info', html)
def warning(self, msg, html=False):
self.add_message(msg, 'warning', html)
def error(self, msg, html=False):
self.add_message(msg, 'error', html)
--------------

Here's what goes in your template:

--------------
<div py:if="flash2" id="flash2_container">
<div py:for="message in flash2.messages" class="flash2 $
{message.css}">
<span py:if="message.html" py:content="XML(message.message)" />
<span py:if="not message.html" py:content="message.message" />
</div>
</div>
--------------


And here's some CSS to liven things up:

--------------
<style type="text/css">
#flash2_container {
border: 1px dashed #0f0; /* thin green border for debugging */
/* padding: .5em; */
}
#flash2_container .flash2 {
/* styles for all flash2 messages */
border-width: 3px;
border-style: solid;
margin: .5em;
padding: .5em;
}
#flash2_container .error {
background-color: #fdd; /* red */
border-color: #f00;
}
#flash2_container .warning {
background-color: #ffd; /* yellow? */
border-color: #ff0;
}
#flash2_container .info {
background-color: #ddf; /* blue */
border-color: #00f;
}
</style>
--------------

That's it. Implementing globally is left as an exercise for the
reader. :)

On May 18, 12:00 pm, "Lee McFadden" <splee...@gmail.com> wrote:

Christoph Zwerschke

unread,
May 18, 2007, 4:44:16 PM5/18/07
to turbo...@googlegroups.com
ans...@gmail.com wrote:
> # For HTML, add on html=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)

I suggest naming the flag "xhtml" so that you're reminded you can only
use well-formed XML (because of Kid).

> That's it. Implementing globally is left as an exercise for the
> reader. :)

If you want to use the tg_flash cookie mechanism, then you would
probably encode the cls attribute behind the scenes in a string
similarly to what Matthew suggested. Maybe something like this can be
implemented in TG 1.1 in a backward compatible way.

-- Chris

andre...@gmail.com

unread,
Jun 17, 2007, 8:15:25 PM6/17/07
to TurboGears
I'd like to show my suggestion to the problem. It is based on
ans...@gmail.com 's solution (some posts above), but it is a bit
simpler:

* It also handles multiple messages
* also based on session.
* accept html
* the error type automatically produces the correspondent CSS class.

It's supposed to be used with genshi. There is a snippet of the
master.html template:

<div py:if="'flash' in cherrypy.session">
<p py:for="message in cherrypy.session['flash']"
py:content="message.to_genshi()" py:attrs="message.attrs">Flash
Message</p>
</div>

The flash function could be used as:

flash('Item <strong>removed</strong>') or
flash('Invalid date', message_type='error')


Finally, the code:
-----------------------------

# -*- coding: utf-8 -*-

# inspired on http://www.nabble.com/My-turbogears.flash()-alternative-t3896614.html

import cherrypy
import genshi

class FlashMessage(object):
def __init__(self, message, message_type):
self.message = message
self.message_type = message_type

def __repr__(self):
return self.message

@property
def attrs(self):
return {'class': '%s' % self.message_type}

def to_genshi(self):
return genshi.Markup(self.message)


class FlashMessagesIterator(object):
def __init__(self):
self.messages = list()

def append(self, message):
self.messages.append(message)

def __iter__(self):
return self

def next(self):
if len(self.messages):
return self.messages.pop(0)
else:
raise StopIteration


def flash(message, message_type = 'notice'):
if 'flash' not in cherrypy.session:
cherrypy.session['flash'] = FlashMessagesIterator()

flash_message = FlashMessage(message, message_type)
cherrypy.session['flash'].append(flash_message)


def flash_errors(tg_errors):
for field, error in tg_errors.items():
message = '%s: %s' % (field, error)
flash(message = message, message_type = 'error')


--------------

Regards,

André


Christoph Zwerschke

unread,
Jun 18, 2007, 2:21:39 PM6/18/07
to turbo...@googlegroups.com
andre...@gmail.com wrote:
> I'd like to show my suggestion to the problem.

Nice.

> <div py:if="'flash' in cherrypy.session">
> <p py:for="message in cherrypy.session['flash']"
> py:content="message.to_genshi()" py:attrs="message.attrs">Flash
> Message</p>
> </div>

Instead of message.to_genshi() you can write XML(message.message), then
it will work with both Kid and Genshi. With Genshi you can also used
HTML(...) instead of XML(...). The to_genshi() method is not necessary.

One problem is that cherrypy.session is not available in the template by
default, you must add it via turbogears.view.variable_providers. I think
it would be a good idea to make this generally available if sessions are
enabled (http://trac.turbogears.org/ticket/1409).

-- Chris

Reply all
Reply to author
Forward
0 new messages