Show one-time ("flash") messages to users in web applications

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

"""
Flash Messages
==============

One-time notification of users in web applications.

The common use case for flash messages is to issue informational, warning or
error messages after a POST request has been made and to show them after a
subsequent HTTP redirect.  The latter is done to ensure a POST request
(modifying data on the server) isn't sent again when the user reloads a page.
If the page to (which the user is redirected to) is reloaded, the message
won't be shown again.

A session object is used to store one or more messages.  This implementation
utilizes ``collections.defaultdict`` (which was introduced in Python 2.5) and
the Werkzeug_ WSGI toolkit.

In the actual snippet code, the import ``request`` object is expected to be a
``werkzeug.wrappers.BaseRequest`` (or subclass) wrapper_ instance, provided as
`context local`_, and needs to have a ``werkzeug.contrib.sessions`` session
store attached to it as attribute named ``session``.  All this can also be
adapted to work with other components, such as the Beaker_ session middleware
or the Paste_ or WebOb_ WSGI toolkits.

To retrieve waiting flash messages and remove them from the session object
afterwards::

    import flash
    flash_messages = flash.get_messages(True)

They can be then passed on to a template engine.  An example of how to show
waiting messages in a Genshi_ (layout) template::

    <ul id="flash" py:if="flash_messages">
      <py:for each="category, messages in flash_messages.iteritems()">
        <li py:for="message in messages" class="${category}">${message}</li>
      </py:for>
    </ul><!-- /#flash -->

Cascading Stylesheets (CSS) and some nice icons can then be used to style the
messages according to their respective types.

.. _Werkzeug:       http://werkzeug.pocoo.org/
.. _wrapper:        http://werkzeug.pocoo.org/documentation/wrappers
.. _context local:  http://werkzeug.pocoo.org/documentation/local
.. _Beaker:         http://beaker.groovie.org/
.. _Paste:          http://pythonpaste.org/
.. _WebOb:          http://pythonpaste.org/webob/
.. _Genshi:         http://genshi.edgewall.org/

:Copyright: 2008 Jochen Kupperschmidt
:Date: 16-May-2008
:License: MIT
"""

from collections import defaultdict

from example_application import request


def get_messages(clear=False):
    """Return messages as dictionary with categories as keys."""
    messages = request.session.get('flash', {})
    if clear:
        clear_messages()
    return messages

def clear_messages():
    """Clear all messages."""
    request.session['flash'] = defaultdict(list)

def _set_message(category, message):
    """Set a message."""
    if not 'flash' in request.session:
        request.session['flash'] = defaultdict(list)
    request.session['flash'][category].append(message)
    request.session.modified = True

def set_error(message):
    """Set an error message."""
    _set_message('error', message)

def set_warning(message):
    """Set a warning message."""
    _set_message('warning', message)

def set_notice(message):
    """Set a notice message."""
    _set_message('notice', message)