├── doc
└── source
│ ├── _static
│ └── .gitkeep
│ ├── modules
│ ├── index.rst
│ ├── decorators.rst
│ ├── context_processors.rst
│ ├── models.rst
│ ├── storage
│ │ ├── index.rst
│ │ ├── cookie.rst
│ │ └── session.rst
│ ├── middleware.rst
│ └── codec
│ │ ├── json_impl.rst
│ │ ├── pickle_impl.rst
│ │ ├── json_zlib_impl.rst
│ │ └── index.rst
│ ├── _templates
│ └── layout.html
│ ├── installation.rst
│ ├── custom_codecs.rst
│ ├── index.rst
│ ├── custom_storages.rst
│ ├── getting_involved.rst
│ ├── configuration.rst
│ ├── changelog.rst
│ ├── conf.py
│ └── usage.rst
├── src
└── djangoflash
│ ├── tests
│ ├── testproj
│ │ ├── media
│ │ │ └── test.css
│ │ ├── app
│ │ │ ├── models.py
│ │ │ ├── __init__.py
│ │ │ ├── urls.py
│ │ │ ├── views.py
│ │ │ └── tests.py
│ │ ├── __init__.py
│ │ ├── templates
│ │ │ └── simple.html
│ │ ├── urls.py
│ │ ├── manage.py
│ │ └── settings.py
│ ├── __init__.py
│ ├── suite.py
│ ├── context_processors.py
│ ├── decorators.py
│ ├── storage.py
│ ├── codec.py
│ └── models.py
│ ├── views.py
│ ├── codec
│ ├── json_impl.py
│ ├── json_zlib_impl.py
│ ├── pickle_impl.py
│ └── __init__.py
│ ├── __init__.py
│ ├── decorators.py
│ ├── storage
│ ├── __init__.py
│ ├── session.py
│ └── cookie.py
│ ├── context_processors.py
│ ├── middleware.py
│ └── models.py
├── .gitignore
├── AUTHORS
├── README.rst
├── setup.py
├── LICENSE
├── fabfile.py
└── ez_setup.py
/doc/source/_static/.gitkeep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/djangoflash/tests/testproj/media/test.css:
--------------------------------------------------------------------------------
1 | html * {}
2 |
--------------------------------------------------------------------------------
/src/djangoflash/tests/testproj/app/models.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 |
3 | """Create your models here.
4 | """
5 |
--------------------------------------------------------------------------------
/src/djangoflash/views.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 |
3 | """Django-Flash doesn't provide any Django views.
4 | """
5 |
--------------------------------------------------------------------------------
/src/djangoflash/tests/__init__.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 |
3 | """Dummy file to make this directory a package.
4 | """
5 |
--------------------------------------------------------------------------------
/src/djangoflash/tests/testproj/__init__.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 |
3 | """Dummy file to make this directory a package.
4 | """
5 |
--------------------------------------------------------------------------------
/src/djangoflash/tests/testproj/app/__init__.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 |
3 | """Dummy file to make this directory a package.
4 | """
5 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Python bytecodes
2 | *.pyc
3 |
4 | # Build directory
5 | build/*
6 | dist/*
7 | doc/build/*
8 |
9 | # Egg info directory
10 | *.egg-info
11 |
12 | # Test coverage file
13 | .coverage
14 |
--------------------------------------------------------------------------------
/doc/source/modules/index.rst:
--------------------------------------------------------------------------------
1 | .. _modulesindex:
2 |
3 | Django-Flash overview
4 | =====================
5 |
6 | .. toctree::
7 |
8 | models
9 | middleware
10 | context_processors
11 | decorators
12 | storage/index
13 | codec/index
14 |
--------------------------------------------------------------------------------
/src/djangoflash/tests/testproj/templates/simple.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | Simple template
4 |
5 |
6 |
7 | Flash context: {{ flash.message }}
8 |
9 |
10 |
--------------------------------------------------------------------------------
/doc/source/modules/decorators.rst:
--------------------------------------------------------------------------------
1 | :mod:`djangoflash.decorators` --- Django-Flash decorators
2 | =========================================================
3 |
4 | .. automodule:: djangoflash.decorators
5 | :members:
6 | :synopsis: Django-flash decorators
7 |
8 |
9 | .. seealso::
10 | :ref:`modulesindex`
11 |
12 |
--------------------------------------------------------------------------------
/doc/source/modules/context_processors.rst:
--------------------------------------------------------------------------------
1 | :mod:`djangoflash.context_processors` --- Django-Flash context processors
2 | =========================================================================
3 |
4 | .. automodule:: djangoflash.context_processors
5 | :members:
6 | :synopsis: Django-flash context processors
7 |
8 | .. seealso::
9 | :ref:`modulesindex`
10 |
--------------------------------------------------------------------------------
/doc/source/modules/models.rst:
--------------------------------------------------------------------------------
1 | :mod:`djangoflash.models` --- Django-Flash model
2 | ================================================
3 |
4 | .. automodule:: djangoflash.models
5 | :synopsis: Django-flash model
6 |
7 |
8 | :class:`FlashScope` Class
9 | `````````````````````````
10 |
11 | .. autoclass:: FlashScope
12 | :show-inheritance:
13 | :members:
14 |
15 |
16 | .. seealso::
17 | :ref:`modulesindex`
18 |
19 |
--------------------------------------------------------------------------------
/src/djangoflash/tests/testproj/urls.py:
--------------------------------------------------------------------------------
1 | from django.conf.urls.defaults import *
2 |
3 | from django.conf import settings
4 |
5 | urlpatterns = patterns('',
6 | (r'', include('testproj.app.urls')),
7 |
8 | # django-flash needs to ignore requests to static files, in development mode
9 | (r'^media/(?P.*)$', 'django.views.static.serve', \
10 | {'document_root': settings.MEDIA_ROOT, 'show_indexes': True}),
11 | )
12 |
--------------------------------------------------------------------------------
/doc/source/modules/storage/index.rst:
--------------------------------------------------------------------------------
1 | :mod:`djangoflash.storage` --- Flash storage backends
2 | =====================================================
3 |
4 | .. automodule:: djangoflash.storage
5 | :members:
6 | :synopsis: Django-flash storage backend
7 |
8 |
9 | Built-in flash storage backends
10 | ```````````````````````````````
11 |
12 | .. toctree::
13 | :maxdepth: 1
14 |
15 | session
16 | cookie
17 |
18 | .. seealso::
19 | :ref:`modulesindex`
20 |
--------------------------------------------------------------------------------
/doc/source/modules/middleware.rst:
--------------------------------------------------------------------------------
1 | .. _middleware:
2 |
3 | :mod:`djangoflash.middleware` --- Django-Flash middleware
4 | =========================================================
5 |
6 | .. automodule:: djangoflash.middleware
7 | :synopsis: Django-flash middleware
8 |
9 | :class:`FlashMiddleware` Class
10 | ``````````````````````````````
11 |
12 | .. autoclass:: FlashMiddleware
13 | :show-inheritance:
14 | :members:
15 |
16 |
17 | .. seealso::
18 | :ref:`modulesindex`
19 |
--------------------------------------------------------------------------------
/doc/source/modules/codec/json_impl.rst:
--------------------------------------------------------------------------------
1 | .. _json_codec:
2 |
3 | :mod:`djangoflash.codec.json_impl` --- JSON-based codec implementation
4 | ======================================================================
5 |
6 | .. automodule:: djangoflash.codec.json_impl
7 | :synopsis: JSON-based codec implementation
8 |
9 |
10 | :class:`CodecClass` Class
11 | `````````````````````````
12 |
13 | .. autoclass:: CodecClass
14 | :show-inheritance:
15 | :members:
16 |
17 |
18 | .. seealso::
19 | :ref:`modulesindex`
20 |
21 |
--------------------------------------------------------------------------------
/doc/source/modules/storage/cookie.rst:
--------------------------------------------------------------------------------
1 | .. _storage_cookie:
2 |
3 | :mod:`djangoflash.storage.cookie` --- Cookie-based flash storage
4 | ================================================================
5 |
6 | .. automodule:: djangoflash.storage.cookie
7 | :synopsis: Cookie-based flash storage
8 |
9 |
10 | :class:`FlashStorageClass` Class
11 | ````````````````````````````````
12 |
13 | .. autoclass:: FlashStorageClass
14 | :show-inheritance:
15 | :members:
16 |
17 |
18 | .. seealso::
19 | :ref:`modulesindex`
20 |
21 |
--------------------------------------------------------------------------------
/doc/source/modules/codec/pickle_impl.rst:
--------------------------------------------------------------------------------
1 | .. _pickle_codec:
2 |
3 | :mod:`djangoflash.codec.pickle_impl` --- Pickle-based codec implementation
4 | ==========================================================================
5 |
6 | .. automodule:: djangoflash.codec.pickle_impl
7 | :synopsis: JSON-based codec implementation
8 |
9 |
10 | :class:`CodecClass` Class
11 | `````````````````````````
12 |
13 | .. autoclass:: CodecClass
14 | :show-inheritance:
15 | :members:
16 |
17 |
18 | .. seealso::
19 | :ref:`modulesindex`
20 |
21 |
--------------------------------------------------------------------------------
/doc/source/modules/storage/session.rst:
--------------------------------------------------------------------------------
1 | .. _storage_session:
2 |
3 | :mod:`djangoflash.storage.session` --- Session-based flash storage
4 | ==================================================================
5 |
6 | .. automodule:: djangoflash.storage.session
7 | :synopsis: Session-based flash storage
8 |
9 |
10 | :class:`FlashStorageClass` Class
11 | ````````````````````````````````
12 |
13 | .. autoclass:: FlashStorageClass
14 | :show-inheritance:
15 | :members:
16 |
17 |
18 | .. seealso::
19 | :ref:`modulesindex`
20 |
21 |
--------------------------------------------------------------------------------
/doc/source/modules/codec/json_zlib_impl.rst:
--------------------------------------------------------------------------------
1 | .. _json_zlib_codec:
2 |
3 | :mod:`djangoflash.codec.json_zlib_impl` --- JSON/zlib-based codec implementation
4 | ================================================================================
5 |
6 | .. automodule:: djangoflash.codec.json_zlib_impl
7 | :synopsis: JSON/zlib-based codec implementation
8 |
9 |
10 | :class:`CodecClass` Class
11 | `````````````````````````
12 |
13 | .. autoclass:: CodecClass
14 | :show-inheritance:
15 | :members:
16 |
17 |
18 | .. seealso::
19 | :ref:`modulesindex`
20 |
21 |
--------------------------------------------------------------------------------
/doc/source/_templates/layout.html:
--------------------------------------------------------------------------------
1 | {% extends "!layout.html" %}
2 |
3 | {% block footer %}
4 | {{ super() }}
5 |
9 |
14 | {% endblock %}
15 |
16 |
--------------------------------------------------------------------------------
/src/djangoflash/tests/testproj/manage.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | from django.core.management import execute_manager
3 | try:
4 | import settings # Assumed to be in the same directory.
5 | except ImportError:
6 | import sys
7 | sys.stderr.write("Error: Can't find the file 'settings.py' in the directory containing %r. It appears you've customized things.\nYou'll have to run django-admin.py, passing it your settings module.\n(If the file settings.py does indeed exist, it's causing an ImportError somehow.)\n" % __file__)
8 | sys.exit(1)
9 |
10 | if __name__ == "__main__":
11 | execute_manager(settings)
12 |
--------------------------------------------------------------------------------
/src/djangoflash/tests/testproj/app/urls.py:
--------------------------------------------------------------------------------
1 | from django.conf.urls.defaults import *
2 |
3 | from testproj.app import views
4 |
5 | urlpatterns = patterns('',
6 | (r'^default/$', views.render_template),
7 | (r'^set_flash_var/$', views.set_flash_var),
8 | (r'^set_another_flash_var/$', views.set_another_flash_var),
9 | (r'^set_now_var/$', views.set_now_var),
10 | (r'^keep_var/$', views.keep_var),
11 | (r'^keep_var_decorator/$', views.keep_var_decorator),
12 | (r'^discard_var/$', views.discard_var),
13 | (r'^replace_flash/$', views.replace_flash),
14 | (r'^remove_flash/$', views.remove_flash),
15 | )
16 |
--------------------------------------------------------------------------------
/doc/source/modules/codec/index.rst:
--------------------------------------------------------------------------------
1 | :mod:`djangoflash.codec` --- Flash serialization codecs
2 | =======================================================
3 |
4 | .. automodule:: djangoflash.codec
5 | :members: get_codec
6 | :synopsis: Codecs for data transport
7 |
8 |
9 | :class:`BaseCodec` Class
10 | ````````````````````````
11 |
12 | .. autoclass:: BaseCodec
13 | :show-inheritance:
14 | :members:
15 |
16 |
17 | Built-in serialization codecs
18 | `````````````````````````````
19 |
20 | .. toctree::
21 | :maxdepth: 1
22 |
23 | json_impl
24 | json_zlib_impl
25 | pickle_impl
26 |
27 | .. seealso::
28 | :ref:`modulesindex`
29 |
--------------------------------------------------------------------------------
/AUTHORS:
--------------------------------------------------------------------------------
1 | Django-Flash was originally created in late 2008 at Destaquenet Technology
2 | Solutions, a brazilian software development and consultancy startup.
3 |
4 | The PRIMARY AUTHORS are (and/or have been):
5 |
6 | Daniel Martins
7 |
8 |
9 | And here is an inevitably incomplete list of MUCH-APPRECIATED CONTRIBUTORS --
10 | people who have helped directly or indirectly to make Django-Flash much better
11 | (in alphabetic order):
12 |
13 | Chris Beaven
14 | Francesco Crippa
15 | Jeremy Sule
16 | Leah Culver
17 | Tobias McNulty
18 |
--------------------------------------------------------------------------------
/src/djangoflash/codec/json_impl.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 |
3 | """This module provides a JSON-based codec implementation.
4 | """
5 |
6 | try:
7 | import json
8 | except ImportError:
9 | from django.utils import simplejson as json
10 |
11 | from djangoflash.codec import BaseCodec
12 | from djangoflash.models import FlashScope
13 |
14 |
15 | class CodecClass(BaseCodec):
16 | """JSON-based codec implementation.
17 | """
18 | def __init__(self):
19 | """Returns a new JSON-based codec.
20 | """
21 | BaseCodec.__init__(self)
22 |
23 | def encode(self, flash):
24 | """Encodes the given *flash* as a JSON string.
25 | """
26 | return json.dumps(flash.to_dict())
27 |
28 | def decode(self, encoded_flash):
29 | """Restores the *flash* from the given JSON string.
30 | """
31 | return FlashScope(json.loads(encoded_flash))
32 |
--------------------------------------------------------------------------------
/src/djangoflash/__init__.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 |
3 | """
4 | Django-flash
5 | ~~~~~~~~~~~~
6 |
7 | Rails-like *flash* messages support for Django.
8 |
9 | :copyright: 2008-2009, Destaquenet Technology Solutions.
10 | :license: BSD.
11 | """
12 |
13 | __version__ = '1.8'
14 |
15 | __author__ = 'Daniel Fernandes Martins'
16 | __email__ = 'daniel@destaquenet.com'
17 |
18 |
19 | def run_tests(verbosity=1):
20 | """Runs the tests. This function is useful when you want to check if an
21 | already installed version of Django-Flash (e.g. one that don't have a
22 | ``setup.py`` file) works as expected. Example::
23 |
24 | $ python -c "import djangoflash; djangoflash.run_tests();"
25 | """
26 | from djangoflash.tests import suite
27 | import unittest
28 | runner = unittest.TextTestRunner(verbosity=verbosity)
29 | unittest.main(module=suite, testRunner=runner)
30 |
--------------------------------------------------------------------------------
/src/djangoflash/decorators.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 |
3 | """This module provides decorators to simplify common tasks.
4 | """
5 |
6 | from djangoflash.context_processors import CONTEXT_VAR
7 |
8 |
9 | def keep_messages(*keys):
10 | """Prevents specific values from being removed during the processing of
11 | the decorated view. If this decorator is used with no args, the entire
12 | flash is preserved.
13 | """
14 | def _keep_messages(view_method):
15 | def _wrapped_view_method(request, *args, **kwargs):
16 | if hasattr(request, CONTEXT_VAR):
17 | flash = getattr(request, CONTEXT_VAR)
18 | flash.keep(*keys)
19 | return view_method(request, *args, **kwargs)
20 | return _wrapped_view_method
21 |
22 | if len(keys) == 1 and callable(keys[0]):
23 | view_method = keys[0]
24 | keys = []
25 | return _keep_messages(view_method)
26 | return _keep_messages
27 |
--------------------------------------------------------------------------------
/src/djangoflash/codec/json_zlib_impl.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 |
3 | """This module provides a JSON-based codec implementation that uses the
4 | :mod:`zlib` module to reduce the encoded flash footprint.
5 | """
6 |
7 | import zlib
8 |
9 | from djangoflash.codec.json_impl import CodecClass as JSONCodecClass
10 | from djangoflash.models import FlashScope
11 |
12 |
13 | class CodecClass(JSONCodecClass):
14 | """JSON/zlib-based codec implementation.
15 | """
16 | def __init__(self):
17 | """Returns a new JSON/zlib-based codec.
18 | """
19 | JSONCodecClass.__init__(self)
20 |
21 | def encode(self, flash):
22 | """Encodes the given *flash* as a zlib compressed JSON string.
23 | """
24 | return zlib.compress(JSONCodecClass.encode(self, flash))
25 |
26 | def decode(self, encoded_flash):
27 | """Restores the *flash* from the given zlib compressed JSON string.
28 | """
29 | return JSONCodecClass.decode(self, zlib.decompress(encoded_flash))
30 |
--------------------------------------------------------------------------------
/README.rst:
--------------------------------------------------------------------------------
1 | Django-Flash
2 | ============
3 |
4 | Django-Flash is a simple Django extension that provides support for Rails_-like
5 | *flash* messages.
6 |
7 | The *flash* is a temporary storage mechanism that looks like a Python
8 | dictionary, so you can store values associated with keys and later retrieve
9 | them. It has one special property: by default, values stored into the *flash*
10 | during the processing of a request will be available during the processing of
11 | the immediately following request. Once that second request has been
12 | processed, those values are removed automatically from the storage.
13 |
14 | This is an open source project licenced under the terms of The
15 | `BSD License`_ and sponsored by Destaquenet Technology Solutions, a
16 | brazilian software development and consultancy startup.
17 |
18 |
19 | Installation and Usage
20 | ----------------------
21 |
22 | Please read the `online documentation `_
23 | for further instructions.
24 |
25 |
26 | .. _BSD License: http://www.opensource.org/licenses/bsd-license.php
27 | .. _Rails: http://www.rubyonrails.org/
28 |
--------------------------------------------------------------------------------
/src/djangoflash/codec/pickle_impl.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 |
3 | """This module provides a Pickle-based codec implementation.
4 |
5 | .. warning::
6 | The use of this codec is not recommended since the
7 | `Pickle documentation `_ itself
8 | clearly states that it's not intended to be secure against erroneous or
9 | maliciously constructed data.
10 | """
11 |
12 | try:
13 | import cPickle as pickle
14 | except ImportError:
15 | import pickle
16 |
17 | from djangoflash.codec import BaseCodec
18 |
19 |
20 | class CodecClass(BaseCodec):
21 | """Pickle-based codec implementation.
22 | """
23 | def __init__(self):
24 | """Returns a new Pickle-based codec.
25 | """
26 | BaseCodec.__init__(self)
27 |
28 | def encode(self, flash):
29 | """Encodes the given *flash* as a Pickle dump string.
30 | """
31 | return pickle.dumps(flash, pickle.HIGHEST_PROTOCOL)
32 |
33 | def decode(self, encoded_flash):
34 | """Restores the *flash* from the given Pickle dump string.
35 | """
36 | return pickle.loads(encoded_flash)
37 |
--------------------------------------------------------------------------------
/setup.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # -*- coding: utf-8 -*-
3 |
4 | import ez_setup
5 | ez_setup.use_setuptools()
6 |
7 | from setuptools import setup, find_packages
8 |
9 | setup(
10 | name = 'django-flash',
11 | version = '1.8',
12 | author = 'Daniel Fernandes Martins',
13 | author_email = 'daniel@destaquenet.com',
14 | description = 'Django-flash is a simple Django extension which provides support for Rails-like flash messages.',
15 | license = 'BSD',
16 | platforms = ['Any'],
17 | keywords = ['django', 'flash', 'session', 'scope', 'context', 'contrib'],
18 | url = 'http://djangoflash.destaquenet.com/',
19 | classifiers = [
20 | 'Development Status :: 5 - Production/Stable',
21 | 'Intended Audience :: Developers',
22 | 'License :: OSI Approved :: BSD License',
23 | 'Natural Language :: English',
24 | 'Operating System :: OS Independent',
25 | 'Programming Language :: Python',
26 | 'Framework :: Django',
27 | 'Topic :: Internet :: WWW/HTTP'
28 | ],
29 | install_requires = ['Django>=1.0_final'],
30 | packages = find_packages('src'),
31 | package_dir = {'':'src'},
32 | include_package_data = True,
33 | zip_safe = False,
34 | test_suite = 'djangoflash.tests.suite',
35 | )
36 |
--------------------------------------------------------------------------------
/src/djangoflash/tests/suite.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 |
3 | """Project's test suite.
4 | """
5 |
6 | import sys
7 |
8 | # Adds the Django test project to system path
9 | from django.core.management import setup_environ
10 | import djangoflash.tests.testproj.settings as project_settings
11 | sys.path.insert(0, setup_environ(project_settings))
12 |
13 | # Imports unit tests
14 | from context_processors import *
15 | from decorators import *
16 | from models import *
17 | from storage import *
18 | from codec import *
19 |
20 | # Now, the integration tests, which depends on SQLite
21 | has_sqlite = True
22 |
23 | try:
24 | import sqlite3
25 | except ImportError:
26 | try:
27 | import pysqlite2
28 | has_sqlite = True
29 | except ImportError:
30 | pass
31 |
32 | # Runs the integration tests if at least one module was found
33 | if has_sqlite:
34 | # Bootstraps integration environment
35 | import django.test.utils as test_utils
36 | from django.db import connection
37 | test_utils.setup_test_environment()
38 | connection.creation.create_test_db()
39 |
40 | # Imports integration tests
41 | from testproj.app.tests import *
42 | else:
43 | print >> sys.stderr, 'Integration: module "sqlite3" (or "pysqlite2") is required... SKIPPED'
44 |
--------------------------------------------------------------------------------
/src/djangoflash/storage/__init__.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 |
3 | """This package provides some built-in flash storage backends used to persist
4 | the *flash* contents across requests.
5 | """
6 |
7 | from django.conf import settings
8 |
9 |
10 | # Alias for use in settings file --> name of module in "storage" directory.
11 | # Any storage that is not in this dictionary is treated as a Python import
12 | # path to a custom storage.
13 |
14 | # This config style is deprecated in Django 1.2, but we'll continue to support
15 | # these alias for some more time.
16 | STORAGES = {
17 | 'session': 'session',
18 | 'cookie': 'cookie',
19 | }
20 |
21 | def get_storage(module):
22 | """Creates and returns the flash storage backend defined in the given
23 | module path (ex: ``"myapp.mypackage.mymodule"``). The argument can also
24 | be an alias to a built-in storage backend, such as ``"session"`` or
25 | ``"cookie"``.
26 | """
27 | if module in STORAGES:
28 | mod = __import__('djangoflash.storage.%s' % STORAGES[module], \
29 | {}, {}, [''])
30 | else:
31 | mod = __import__(module, {}, {}, [''])
32 | return getattr(mod, 'FlashStorageClass')()
33 |
34 | # Get the flash storage specified in the project's settings. Use the session
35 | # storage by default (for both security and backward compatibility reasons).
36 | storage = get_storage(getattr(settings, 'FLASH_STORAGE', 'session'))
37 |
--------------------------------------------------------------------------------
/src/djangoflash/storage/session.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 |
3 | """This module provides a session-based flash storage backend.
4 |
5 | Since this backend relies on the user's session, you need to include the
6 | :class:`SessionMiddleware` class to the ``MIDDLEWARE_CLASSES`` section of your
7 | project's ``settings.py`` file::
8 |
9 | MIDDLEWARE_CLASSES = (
10 | 'django.contrib.sessions.middleware.SessionMiddleware',
11 | 'djangoflash.middleware.FlashMiddleware',
12 | )
13 |
14 | .. seealso::
15 | :ref:`configuration`
16 | """
17 |
18 | class FlashStorageClass(object):
19 | """Session-based flash storage backend.
20 | """
21 | def __init__(self):
22 | """Returns a new session-based flash storage backend.
23 | """
24 | self._key = '_djflash_session'
25 |
26 | def set(self, flash, request, response):
27 | """Stores the given :class:`FlashScope` object in the session.
28 | """
29 | if hasattr(request, 'session'):
30 | if flash:
31 | request.session[self._key] = flash
32 | elif self._key in request.session:
33 | del request.session[self._key]
34 |
35 | def get(self, request):
36 | """Returns :class:`FlashScope` object stored in the session.
37 | """
38 | if hasattr(request, 'session') and self._key in request.session:
39 | return request.session[self._key]
40 |
--------------------------------------------------------------------------------
/doc/source/installation.rst:
--------------------------------------------------------------------------------
1 | Installation
2 | ============
3 |
4 | There are several ways to download and install Django-Flash:
5 |
6 | **Via PyPI**
7 |
8 | Execute the following command line to download and install the latest
9 | stable version from CheeseShop_::
10 |
11 | $ easy_install -U django-flash
12 |
13 |
14 | Follow `these instructions `_ to
15 | install SetupTools if you don't have it already.
16 |
17 |
18 | **Via GitHub**
19 |
20 | If you are a Git_ user and want to take a closer look at the project's
21 | source code, you would rather clone our
22 | `public repository `_
23 | instead::
24 |
25 | $ git clone git://github.com/danielfm/django-flash.git
26 | $ cd django-flash
27 | $ python setup.py install
28 |
29 |
30 | **Zip file/tarball**
31 |
32 | Django-Flash is also available for download as
33 | `compressed archives `_
34 | (either ``zip`` and ``tgz``). After unzip/untar the archive, execute the
35 | following command to install Django-Flash::
36 |
37 | $ python setup.py install
38 |
39 |
40 | **Manually**
41 |
42 | To add Django-Flash to your project as a bundled library, just add the
43 | ``djangoflash`` directory into your project along with the other apps.
44 |
45 |
46 | .. _CheeseShop: http://pypi.python.org/pypi
47 | .. _Git: http://git-scm.com/
48 |
49 |
--------------------------------------------------------------------------------
/doc/source/custom_codecs.rst:
--------------------------------------------------------------------------------
1 | .. _custom_codecs:
2 |
3 | Creating a custom serialization codec
4 | -------------------------------------
5 |
6 | Since :ref:`version 1.7 `, Django-Flash supports custom flash
7 | serialization codecs.
8 |
9 | By default, Django-Flash provides three built-in codecs:
10 |
11 | * :mod:`djangoflash.codec.json_impl` -- JSON-based codec (default);
12 | * :mod:`djangoflash.codec.json_zlib_impl` -- JSON/zlib-based codec;
13 | * :mod:`djangoflash.codec.pickle_impl` -- Pickle-based codec;
14 |
15 | The good news is that you can create your own codec if the existing ones are
16 | getting in your way. To do so, the first thing you need to do is create a
17 | Python module with a class called :class:`CodecClass`::
18 |
19 | # Let's suppose this module is called 'myproj.djangoflash.custom'
20 |
21 | from djangoflash.codec import BaseCodec
22 |
23 | class CodecClass(BaseCodec):
24 | def __init__(self):
25 | BaseCodec.__init__(self)
26 |
27 | def encode(self, flash):
28 | pass
29 |
30 | def decode(self, encoded_flash):
31 | pass
32 |
33 |
34 | Note that custom codecs must extend the :class:`djangoflash.codec.BaseCodec`
35 | class direct or indirectly.
36 |
37 | Finally, to use your custom codec, add the following setting to your project's
38 | ``settings.py`` file::
39 |
40 | FLASH_CODEC = 'myproj.djangoflash.custom' # Path to module
41 |
42 |
43 | .. seealso::
44 | :ref:`configuration`
45 |
46 |
--------------------------------------------------------------------------------
/src/djangoflash/tests/context_processors.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 |
3 | """djangoflash.context_processors test cases.
4 | """
5 |
6 | from unittest import TestCase
7 |
8 | from django.core.exceptions import SuspiciousOperation
9 | from django.http import HttpRequest
10 |
11 | from djangoflash.context_processors import CONTEXT_VAR, flash
12 | from djangoflash.models import FlashScope
13 |
14 |
15 | class FlashContextProcessorTestCase(TestCase):
16 | """Tests the context processor used to expose the flash to view templates.
17 | """
18 | def setUp(self):
19 | self.request = HttpRequest()
20 | self.scope = FlashScope();
21 | setattr(self.request, CONTEXT_VAR, self.scope);
22 |
23 | def test_expose_flash(self):
24 | """FlashContextProcessor: should expose the flash to view templates.
25 | """
26 | self.assertEqual(flash(self.request), {CONTEXT_VAR:self.scope})
27 |
28 | def test_expose_inexistent_flash(self):
29 | """FlashContextProcessor: should fail when there's no flash available.
30 | """
31 | delattr(self.request, CONTEXT_VAR)
32 | self.assertTrue(isinstance(flash(self.request)[CONTEXT_VAR], \
33 | FlashScope))
34 |
35 | def test_expose_invalid_flash(self):
36 | """FlashContextProcessor: should fail when exposing an invalid object as being the flash.
37 | """
38 | self.request.flash = 'Invalid object'
39 | self.assertRaises(SuspiciousOperation, flash, self.request)
40 |
--------------------------------------------------------------------------------
/src/djangoflash/tests/testproj/settings.py:
--------------------------------------------------------------------------------
1 | # Django settings for testproj project.
2 |
3 | import os
4 |
5 | DEBUG = True
6 | TEMPLATE_DEBUG = DEBUG
7 |
8 | # For Django < 1.2
9 | DATABASE_ENGINE = 'sqlite3'
10 | DATABASE_NAME = 'db'
11 |
12 | # For Django >= 1.2
13 | DATABASES = {
14 | 'default': {
15 | 'ENGINE':'django.db.backends.sqlite3',
16 | 'NAME': 'db'
17 | }
18 | }
19 |
20 | SECRET_KEY = 'g9b@q$)=^xd2g@-7pg=j=h3*8+xd#hgn-9je@iq5_m#seg&d1y'
21 |
22 | MEDIA_URL = '/media/'
23 | ADMIN_MEDIA_PREFIX = '/admin/'
24 | MEDIA_ROOT = os.path.normpath(os.path.dirname(__file__) + '/media/')
25 |
26 | TEMPLATE_LOADERS = (
27 | 'django.template.loaders.filesystem.load_template_source',
28 | )
29 |
30 | MIDDLEWARE_CLASSES = (
31 | 'django.middleware.common.CommonMiddleware',
32 | 'django.contrib.sessions.middleware.SessionMiddleware',
33 | 'djangoflash.middleware.FlashMiddleware',
34 | )
35 |
36 | TEMPLATE_CONTEXT_PROCESSORS = (
37 | 'djangoflash.context_processors.flash',
38 | 'django.core.context_processors.media',
39 | )
40 |
41 | ROOT_URLCONF = 'testproj.urls'
42 |
43 | TEMPLATE_DIRS = (
44 | os.path.normpath(os.path.dirname(__file__) + '/templates'),
45 | )
46 |
47 | INSTALLED_APPS = (
48 | 'app',
49 | 'django.contrib.sessions',
50 | )
51 |
52 |
53 | # Settings introduced by Django-Flash:
54 |
55 | # FLASH_IGNORE_MEDIA = DEBUG # True, False
56 | # FLASH_STORAGE = 'session' # 'session, 'cookie', 'path.to.module'
57 | # FLASH_CODEC = 'json' # 'json', 'json_zlib', 'pickle', 'path.to.module'
58 |
--------------------------------------------------------------------------------
/src/djangoflash/tests/testproj/app/views.py:
--------------------------------------------------------------------------------
1 | # Create your views here.
2 |
3 | from django.core.urlresolvers import reverse
4 | from django.http import HttpResponse, HttpResponseRedirect
5 | from django.shortcuts import render_to_response
6 | from django.template import RequestContext
7 |
8 | from djangoflash.decorators import keep_messages
9 |
10 |
11 | def render_template(request):
12 | return render_to_response('simple.html', \
13 | context_instance=RequestContext(request))
14 |
15 | def set_flash_var(request):
16 | request.flash['message'] = 'Message'
17 | return render_template(request)
18 |
19 | def set_another_flash_var(request):
20 | request.flash['anotherMessage'] = 'Another message'
21 | return render_template(request)
22 |
23 | def set_now_var(request):
24 | request.flash.now['message'] = 'Message'
25 | return render_template(request)
26 |
27 | def keep_var(request):
28 | request.flash.keep('message')
29 | return render_template(request)
30 |
31 | @keep_messages('message')
32 | def keep_var_decorator(request):
33 | return render_template(request)
34 |
35 | def discard_var(request):
36 | # Should behave the same way 'flash.now' does
37 | request.flash['message'] = 'Message'
38 | request.flash.discard('message')
39 | return render_template(request)
40 |
41 | def replace_flash(request):
42 | request.flash = "Replacing the flash with a string"
43 | return render_template(request)
44 |
45 | def remove_flash(request):
46 | # I've seen this happen, I'm not kidding... :)
47 | del request.flash
48 | return render_template(request)
49 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright (c) 2008-2010, Destaquenet Technology Solutions
2 | All rights reserved.
3 |
4 | Redistribution and use in source and binary forms, with or without modification,
5 | are permitted provided that the following conditions are met:
6 |
7 | 1. Redistributions of source code must retain the above copyright notice,
8 | this list of conditions and the following disclaimer.
9 |
10 | 2. Redistributions in binary form must reproduce the above copyright
11 | notice, this list of conditions and the following disclaimer in the
12 | documentation and/or other materials provided with the distribution.
13 |
14 | 3. Neither the name of Destaquenet Technology Solutions nor the names of
15 | its contributors may be used to endorse or promote products derived
16 | from this software without specific prior written permission.
17 |
18 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
19 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
20 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
21 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
22 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
23 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
24 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
25 | ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
27 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 |
--------------------------------------------------------------------------------
/src/djangoflash/storage/cookie.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 |
3 | """This module provides a cookie-based flash storage backend.
4 |
5 | .. warning::
6 | The actual :class:`FlashScope` object is sent back to the user in a cookie.
7 | Although some encryption is performed to help spot when the flash data is
8 | modified by third-parties, this backend should be avoided when sensitive
9 | information is stored in the *flash*.
10 |
11 | .. warning::
12 | Although in general user agents' cookie support should have no fixed limits,
13 | according to `RFC-2965 `_, section 5.3,
14 | all implementations must support at least 4096 bytes per cookie. So be
15 | careful about the amount of data you store in the *flash* when using this
16 | storage backend.
17 | """
18 |
19 | from djangoflash.codec import codec
20 |
21 |
22 | class FlashStorageClass(object):
23 | """Cookie-based flash storage backend.
24 | """
25 |
26 | def __init__(self):
27 | """Returns a new cookie-based flash storage backend.
28 | """
29 | self._key = '_djflash_cookie'
30 |
31 | def set(self, flash, request, response):
32 | """Stores the given :class:`FlashScope` object in a cookie.
33 | """
34 | if flash:
35 | response.set_cookie(self._key, codec.encode_and_sign(flash))
36 | elif self._key in request.COOKIES:
37 | response.delete_cookie(self._key)
38 |
39 | def get(self, request):
40 | """Returns :class:`FlashScope` object stored in a cookie.
41 | """
42 | data = request.COOKIES.get(self._key)
43 | if data:
44 | return codec.decode_signed(data)
45 |
--------------------------------------------------------------------------------
/doc/source/index.rst:
--------------------------------------------------------------------------------
1 | Django-Flash --- Rails-like *flash* messages support for Django
2 | ===============================================================
3 |
4 | Django-Flash is a simple Django extension that provides support for Rails_-like
5 | *flash* messages.
6 |
7 | The *flash* is a temporary storage mechanism that looks like a Python
8 | dictionary, so you can store values associated with keys and later retrieve
9 | them. It has one special property: by default, values stored into the *flash*
10 | during the processing of a request will be available during the processing of
11 | the immediately following request. Once that second request has been
12 | processed, those values are removed automatically from the storage.
13 |
14 | This is an open source project licenced under the terms of The
15 | `BSD License`_ and sponsored by `Destaquenet Technology Solutions`_, a
16 | brazilian software development and consultancy startup.
17 |
18 | .. seealso::
19 | `PDF version `_ of
20 | this documentation.
21 |
22 |
23 | Documentation contents
24 | ----------------------
25 |
26 | .. toctree::
27 | :maxdepth: 2
28 |
29 | installation
30 | configuration
31 | usage
32 | custom_storages
33 | custom_codecs
34 | modules/index
35 | getting_involved
36 | changelog
37 |
38 |
39 | Indices and tables
40 | ------------------
41 |
42 | * :ref:`genindex`
43 | * :ref:`modindex`
44 | * :ref:`search`
45 |
46 |
47 | .. _BSD License: http://www.opensource.org/licenses/bsd-license.php
48 | .. _Django: http://www.djangoproject.org/
49 | .. _Rails: http://www.rubyonrails.org/
50 | .. _Destaquenet Technology Solutions: http://www.destaquenet.com/
51 |
52 |
--------------------------------------------------------------------------------
/doc/source/custom_storages.rst:
--------------------------------------------------------------------------------
1 | .. _custom_storages:
2 |
3 | Creating a custom flash storage backend
4 | ---------------------------------------
5 |
6 | Since :ref:`version 1.5`, Django-Flash supports custom flash
7 | storage backends.
8 |
9 | By default, Django-flash provides two built-in storage backends:
10 |
11 | * :mod:`djangoflash.storage.session` -- Session-based storage (default);
12 | * :mod:`djangoflash.storage.cookie` -- Cookie-based storage;
13 |
14 | The good news is that you can create your own storage backend if the existing
15 | ones are getting in your way. To do so, the first thing you need to do is
16 | create a Python module with a class called :class:`FlashStorageClass`::
17 |
18 | # Let's suppose this module is called 'myproj.djangoflash.custom'
19 |
20 | # You can use the serialization codec configured by the user
21 | from djangoflash.codec import codec
22 |
23 | class FlashStorageClass(object):
24 | def _is_flash_stored(self, request):
25 | # This method checks whether the flash is already stored
26 | pass
27 |
28 | def set(self, flash, request, response):
29 | if flash:
30 | # Store the flash
31 | pass
32 | elif self._is_flash_stored(request):
33 | # Flash is null or empty, so remove the already stored flash
34 | pass
35 |
36 | def get(self, request):
37 | if self._is_flash_stored(request):
38 | # Return the stored flash
39 | pass
40 |
41 |
42 | Then, to use your custom flash storage backend, add the following setting
43 | to your project's ``settings.py`` file::
44 |
45 | FLASH_STORAGE = 'myproj.djangoflash.custom' # Path to module
46 |
47 |
48 | .. seealso::
49 | :ref:`configuration`
50 |
51 |
--------------------------------------------------------------------------------
/src/djangoflash/context_processors.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 |
3 | """
4 | This module provides the context processor that exposes
5 | :class:`djangoflash.models.FlashScope` objects to view templates.
6 |
7 | To plug this context processor to your Django project, edit your project's
8 | ``settings.py`` file as follows::
9 |
10 | TEMPLATE_CONTEXT_PROCESSORS = (
11 | 'djangoflash.context_processors.flash',
12 | )
13 |
14 |
15 | Doing this, the view templates will be able to access the *flash* contents
16 | using the ``flash`` context variable.
17 |
18 | .. warning::
19 | Your views should use the :class:`RequestContext` class to render the
20 | templates, otherwise the ``flash`` variable (along with *all* other
21 | variables provided by other context processors) won't be available to them.
22 | Please read the
23 | `Django docs `_
24 | for further instructions.
25 |
26 | """
27 |
28 | from django.core.exceptions import SuspiciousOperation
29 | from djangoflash.models import FlashScope
30 |
31 |
32 | # Name of the variable used to keep FlashScope objects both as an attribute
33 | # django.http.HttpRequest and the template context.
34 | CONTEXT_VAR = 'flash'
35 |
36 | def flash(request):
37 | """This context processor gets the :class:`FlashScope` object from the
38 | current *request* and adds it to the template context:
39 |
40 | .. code-block:: html+django
41 |
42 |
43 |
44 |
45 | request.flash['message'] = {{ flash.message }}
46 |
47 |
48 |
49 | """
50 | flash_scope = None
51 | try:
52 | flash_scope = getattr(request, CONTEXT_VAR)
53 | if not isinstance(flash_scope, FlashScope):
54 | raise SuspiciousOperation('Invalid flash: %s' % repr(flash_scope))
55 | except AttributeError:
56 | # Exposes an empty flash when none is available
57 | flash_scope = FlashScope()
58 | return {CONTEXT_VAR: flash_scope}
59 |
--------------------------------------------------------------------------------
/doc/source/getting_involved.rst:
--------------------------------------------------------------------------------
1 | Getting Involved
2 | ================
3 |
4 | As with any open source project, there are several ways you can help:
5 |
6 | * Report bugs, feature requests and other issues in the
7 | `issue tracking system `_;
8 | * Submit patches to reported issues (both those you find, or that others have
9 | filed);
10 | * Help with the documentation by pointing out areas that are lacking or unclear,
11 | and if you are so inclined, submitting patches to correct it;
12 | * Improve the overall project quality by suggesting refactorings and improving
13 | the test cases. A great way to learn -- and in turn give value back to the
14 | community -- is to review someone else's code. So, we invite you to review
15 | ours;
16 | * Create and share packages to make it even easier to distribute Django-Flash
17 | to other users of your favourite Distribution or Operating System;
18 | * Write about Django-Flash in your blog or personal web site. Let your friends
19 | know about this project.
20 |
21 | Your participation is much appreciated. Keep up with Django-Flash development on
22 | `Github `_.
23 |
24 |
25 | How do I join the team?
26 | -----------------------
27 |
28 | Django-Flash is a very mature project and it's probably not going to get lots of
29 | new features. But those that the developers notice participating to a high
30 | extent will be invited to join the team as a committer.
31 |
32 | This is as much based on personality and ability to work with other developers
33 | and the community as it is with proven technical ability. Being unhelpful to
34 | other users, or obviously looking to become a committer for bragging rights and
35 | nothing else is frowned upon, as is asking to be made a committer without having
36 | contributed sufficiently to be invited.
37 |
38 |
39 | Contact information
40 | -------------------
41 |
42 | :Author: Daniel Fernandes Martins
43 | :Company: `Destaquenet Technology Solutions`_
44 |
45 |
46 | .. _Destaquenet Technology Solutions: http://www.destaquenet.com/
47 |
48 |
--------------------------------------------------------------------------------
/src/djangoflash/codec/__init__.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 |
3 | """This package provides some built-in flash serialization codecs.
4 | """
5 |
6 | import base64
7 |
8 | from django.conf import settings
9 | from django.utils.hashcompat import md5_constructor
10 |
11 |
12 | class BaseCodec(object):
13 | """Base codec implementation. All codec implementations must extend this
14 | class.
15 | """
16 | def __init__(self):
17 | """Returns a new :class:`BaseCodec` object.
18 | """
19 | pass
20 |
21 | def encode(self, flash):
22 | """Empty implementation that raises :class:`NotImplementedError`.
23 | """
24 | raise NotImplementedError
25 |
26 | def decode(self, encoded_flash):
27 | """Empty implementation that raises :class:`NotImplementedError`.
28 | """
29 | raise NotImplementedError
30 |
31 | def encode_and_sign(self, flash):
32 | """Returns an encoded-and-signed version of the given *flash*.
33 | """
34 | encoded = self.encode(flash)
35 | encoded_md5 = md5_constructor(encoded + settings.SECRET_KEY).hexdigest()
36 | return base64.encodestring(encoded + encoded_md5)
37 |
38 | def decode_signed(self, encoded_flash):
39 | """Restores the *flash* object from the given encoded-and-signed data.
40 | """
41 | decoded_flash = base64.decodestring(encoded_flash)
42 | encoded, tamper_check = decoded_flash[:-32], decoded_flash[-32:]
43 | hex_digest = md5_constructor(encoded + settings.SECRET_KEY).hexdigest()
44 | if hex_digest != tamper_check:
45 | from django.core.exceptions import SuspiciousOperation
46 | raise SuspiciousOperation('User tampered with data.')
47 | try:
48 | return self.decode(encoded)
49 | except:
50 | # Errors might happen when decoding. Return None if that's the case
51 | return None
52 |
53 |
54 | # Alias for use in settings file --> name of module in "codec" directory.
55 | # Any codec that is not in this dictionary is treated as a Python import
56 | # path to a custom codec.
57 |
58 | # This config style is deprecated in Django 1.2, but we'll continue to support
59 | # these alias for some more time.
60 | CODECS = {
61 | 'json': 'json_impl',
62 | 'json_zlib': 'json_zlib_impl',
63 | 'pickle': 'pickle_impl',
64 | }
65 |
66 | def get_codec(module):
67 | """Creates and returns the codec defined in the given module path
68 | (ex: ``"myapp.mypackage.mymodule"``). The argument can also be an alias to
69 | a built-in codec, such as ``"json"``, ``"json_zlib"`` or ``"pickle"``.
70 | """
71 | if module in CODECS:
72 | # The "_codec" suffix is to avoid conflicts with built-in module names
73 | mod = __import__('djangoflash.codec.%s' % CODECS[module], \
74 | {}, {}, [''])
75 | else:
76 | mod = __import__(module, {}, {}, [''])
77 | return getattr(mod, 'CodecClass')()
78 |
79 | # Get the codec specified in the project's settings. Use the json codec by
80 | # default, for security reasons: http://nadiana.com/python-pickle-insecure
81 | codec = get_codec(getattr(settings, 'FLASH_CODEC', 'json'))
82 |
--------------------------------------------------------------------------------
/src/djangoflash/tests/decorators.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 |
3 | """djangoflash.decorators test cases.
4 | """
5 |
6 | from unittest import TestCase
7 |
8 | from django.http import HttpRequest
9 |
10 | from djangoflash.decorators import keep_messages
11 | from djangoflash.models import FlashScope
12 |
13 |
14 | # Only exports test cases
15 | __all__ = ['KeepMessagesDecoratorTestCase']
16 |
17 |
18 | def view_method(request):
19 | """Function that simulates a Django view.
20 | """
21 | if hasattr(request, 'flash'):
22 | request.flash.update()
23 | return True
24 | return False
25 |
26 |
27 | class KeepMessagesDecoratorTestCase(TestCase):
28 | """Tests the keep_messages decorator.
29 | """
30 | def setUp(self):
31 | """Create a request with an used message inside the flash.
32 | """
33 | self.request = HttpRequest()
34 | self.request.flash = self.flash = FlashScope()
35 | self.flash['message'] = 'Message'
36 | self.flash.update()
37 |
38 | def test_decorator_with_no_flash(self):
39 | """Decorators: keep_messages should not break when there's no flash scope attached to the request.
40 | """
41 | self.request = HttpRequest()
42 | view = keep_messages(view_method)
43 | self.assertFalse(view(self.request))
44 |
45 | def test_decorator_with_no_args(self):
46 | """Decorators: keep_messages with no args should avoid the removal of all flash-scoped values.
47 | """
48 | view = keep_messages(view_method)
49 | self.assertEqual('Message', self.flash['message'])
50 |
51 | self.assertTrue(view(self.request))
52 | self.assertEqual('Message', self.flash['message'])
53 |
54 | view_method(self.request)
55 | self.assertFalse('message' in self.flash)
56 |
57 | def test_decorator_with_empty_args(self):
58 | """Decorators: keep_messages with empty args should avoid the removal of all flash-scoped values.
59 | """
60 | view = keep_messages()(view_method)
61 | self.assertEqual('Message', self.flash['message'])
62 |
63 | self.assertTrue(view(self.request))
64 | self.assertEqual('Message', self.flash['message'])
65 |
66 | view_method(self.request)
67 | self.assertFalse('message' in self.flash)
68 |
69 | def test_decorator_with_args(self):
70 | """Decorators: keep_messages should avoid the removal of specific flash-scoped values.
71 | """
72 | view = keep_messages('message', 'another_message')(view_method)
73 | self.assertEqual('Message', self.flash['message'])
74 |
75 | self.assertTrue(view(self.request))
76 | self.assertEqual('Message', self.flash['message'])
77 |
78 | view_method(self.request)
79 | self.assertFalse('message' in self.flash)
80 |
81 | def test_decorator_with_invalid_arg(self):
82 | """Decorators: keep_messages should not avoid the removal of flash-scoped values.
83 | """
84 | view = keep_messages('another_message')(view_method)
85 | self.assertEqual('Message', self.flash['message'])
86 |
87 | self.assertTrue(view(self.request))
88 | self.assertFalse('message' in self.flash)
89 |
--------------------------------------------------------------------------------
/src/djangoflash/middleware.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 |
3 | """
4 | This module provides the :class:`FlashMiddleware` class, which manages the
5 | *flash* whenever a HTTP request hits the server.
6 |
7 | To plug this middleware to your Django project, edit your project's
8 | ``settings.py`` file as follows::
9 |
10 | MIDDLEWARE_CLASSES = (
11 | 'djangoflash.middleware.FlashMiddleware',
12 | )
13 | """
14 |
15 | from urlparse import urlparse
16 |
17 | from django.conf import settings
18 | from django.core.exceptions import SuspiciousOperation
19 | from django.core import urlresolvers
20 | from django.views.static import serve
21 |
22 | from djangoflash.context_processors import CONTEXT_VAR
23 | from djangoflash.models import FlashScope
24 | from djangoflash.storage import storage
25 |
26 |
27 | # This middleware integrates gracefully with CommonMiddleware
28 | _COMMON_MIDDLEWARE_CLASS = 'django.middleware.common.CommonMiddleware'
29 |
30 |
31 | class FlashMiddleware(object):
32 | """This middleware uses the flash storage backend specified by the
33 | project's ``settings.py`` file in order to store and retrieve
34 | :class:`djangoflash.models.FlashScope` objects, being also responsible for
35 | expiring old flash-scoped objects.
36 |
37 | .. note::
38 | This class is designed to be used by the Django framework itself.
39 | """
40 |
41 | def process_request(self, request):
42 | """This method is called by the Django framework when a *request* hits
43 | the server.
44 | """
45 | flash = _get_flash_from_storage(request)
46 | if _should_update_flash(request):
47 | flash.update()
48 |
49 | def process_response(self, request, response):
50 | """This method is called by the Django framework when a *response* is
51 | sent back to the user.
52 | """
53 | flash = _get_flash_from_request(request)
54 | if flash:
55 | storage.set(flash, request, response)
56 | else:
57 | _get_flash_from_storage(request)
58 |
59 | return response
60 |
61 |
62 | def _get_flash_from_storage(request):
63 | """Gets the flash from the storage, adds it to the given request and
64 | returns it. A new :class:`FlashScope` is used if the storage is empty.
65 | """
66 | flash = storage.get(request) or FlashScope()
67 | setattr(request, CONTEXT_VAR, flash)
68 | return flash
69 |
70 | def _get_flash_from_request(request):
71 | """Returns the :class:`FlashScope` object from the given request. If it
72 | couldn't be found, returns None.
73 | """
74 | flash = None
75 | if hasattr(request, CONTEXT_VAR):
76 | flash = getattr(request, CONTEXT_VAR)
77 | if not isinstance(flash, FlashScope):
78 | raise SuspiciousOperation('Invalid flash: %s' % repr(flash))
79 | return flash
80 |
81 | def _should_update_flash(request):
82 | """Returns True if the flash should be updated, False otherwise.
83 | """
84 | return not _is_trailing_slash_missing(request) and \
85 | not _is_request_to_serve(request)
86 |
87 | def _is_request_to_serve(request):
88 | """Returns True if *request* resolves to the built-in ``serve`` view,
89 | False othersise.
90 | """
91 | # Are we running in debug mode?
92 | debug = getattr(settings, 'DEBUG', False)
93 |
94 | # Uses the value of DEBUG as default value to FLASH_IGNORE_MEDIA
95 | if getattr(settings, 'FLASH_IGNORE_MEDIA', debug):
96 | try:
97 | return urlresolvers.resolve(request.path_info)[0] == serve
98 | except urlresolvers.Resolver404:
99 | pass
100 | return False
101 |
102 | def _is_trailing_slash_missing(request):
103 | """Returns True if the requested URL are elegible to be intercepted by the
104 | CommonMiddleware (if it's being used), which issues a HttpRedirect when a
105 | trailing slash is missing. Returns False otherwise.
106 | """
107 | if _COMMON_MIDDLEWARE_CLASS in settings.MIDDLEWARE_CLASSES:
108 | path = request.path
109 | if getattr(settings, 'APPEND_SLASH', False) and not path.endswith('/'):
110 | if not _is_valid_path(path) and _is_valid_path('%s/' % path):
111 | return True
112 | return False
113 |
114 | def _is_valid_path(path):
115 | """Returns True if *path* resolves against the default URL resolver,
116 | False otherwise.
117 | """
118 | try:
119 | urlresolvers.resolve(path)
120 | return True
121 | except urlresolvers.Resolver404:
122 | pass
123 | return False
124 |
--------------------------------------------------------------------------------
/fabfile.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 |
3 | """Build script used to test, build and deploy django-flash using several
4 | Python versions.
5 |
6 | In order to test and build django-flash in these different environments,
7 | this script requires different virtualenvs, each one targeted to a specific
8 | Python version:
9 |
10 | * django-flash-py2.7 - for Python 2.7
11 | * django-flash-py2.6 - for Python 2.6
12 | * django-flash-py2.5 - for Python 2.5
13 |
14 | Also, each one of these virtualenvs must have the following packages
15 | installed:
16 |
17 | * Django (version 1.0+)
18 | * Pysqlite (version recommended by the current Django version)
19 |
20 | Finally, to use this script, you must install the packages below to your
21 | default Python installation:
22 |
23 | * Fabric 0.9+
24 |
25 | That's it. You can now see all available targets provided by this build
26 | script by running the command line below:
27 |
28 | $ cd /path/to/django-flash
29 | $ fab -l
30 | """
31 |
32 | import os
33 | import re
34 | import sys
35 |
36 | from fabric.api import *
37 |
38 |
39 | # Adds the 'src' to the Python path
40 | sys.path += ('src',)
41 |
42 | # Supported Python versions
43 | env.versions = ('2.7', '2.6', '2.5')
44 | env.default_version = env.versions[0]
45 |
46 | # Environment info
47 | env.project = 'django-flash'
48 | env.virtualenv_dir = os.environ['WORKON_HOME'] or '~/.virtualenvs'
49 | env.default_editor = os.environ['EDITOR'] or 'vi'
50 |
51 | # Files that contain version information
52 | env.new_version_files = (
53 | 'setup.py',
54 | 'src/djangoflash/__init__.py',
55 | 'doc/source/conf.py',
56 | 'doc/source/changelog.rst',
57 | )
58 |
59 | # Information needed to build the documentation
60 | env.sphinx_output = 'build/sphinx'
61 | env.sphinx_latex = '%s/latex' % env.sphinx_output
62 | env.sphinx_html = '%s/html' % env.sphinx_output
63 | env.doc_output = 'djangoflash'
64 |
65 | # Host where the documentation website lives
66 | env.hosts = ['destaquenet.com']
67 | env.doc_folder = '/home/destaquenet/public_html'
68 |
69 |
70 | def setup(command, version=env.default_version):
71 | """Executes the given setup command with a virtual Python installation.
72 | """
73 | local('%s/%s-py%s/bin/python setup.py %s' %
74 | (env.virtualenv_dir, env.project, version, command))
75 |
76 | def test():
77 | """Runs all tests in different Python versions.
78 | """
79 | for version in env.versions:
80 | setup('test', version)
81 |
82 | def clean():
83 | """Removes the build directory.
84 | """
85 | local('rm -fR build')
86 |
87 | def build_docs():
88 | """Builds the documentation in PDF and HTML.
89 | """
90 | clean()
91 | setup('build_sphinx')
92 | setup('build_sphinx -b latex')
93 | local('make -C ' + env.sphinx_latex)
94 |
95 | def zip_docs():
96 | """Creates a zip file with the complete documentation.
97 | """
98 | build_docs()
99 | local('cp %s/%s.pdf %s' %
100 | (env.sphinx_latex, env.project, env.sphinx_html))
101 | local('cd %s; mv html %s; zip -r9 %s.zip %s' %
102 | ((env.sphinx_output,) + (env.doc_output,)*3))
103 |
104 | def register_pypi():
105 | """Register the current version on PyPI.
106 | """
107 | setup('register')
108 |
109 | def deploy_src():
110 | """Deploy the source code to PyPI.
111 | """
112 | setup('sdist upload')
113 |
114 | def deploy_eggs():
115 | """Upload Python Eggs to PyPI.
116 | """
117 | for version in env.versions:
118 | setup('bdist_egg upload', version)
119 |
120 | def deploy_pypi():
121 | """Deploys all artifacts to PyPI.
122 | """
123 | test()
124 | register_pypi()
125 | deploy_src()
126 | deploy_eggs()
127 |
128 | def deploy_website():
129 | """Deploys the documentation website.
130 | """
131 | zip_docs()
132 | put('%s/%s.zip' %
133 | (env.sphinx_output, env.doc_output), env.doc_folder)
134 | run('cd %s; rm -R %s; unzip %s.zip; rm %s.zip' %
135 | ((env.doc_folder,) + (env.doc_output,)*3))
136 |
137 | def deploy():
138 | """Deploys the application to PyPI and updates the documentation website.
139 | """
140 | deploy_pypi()
141 | deploy_website()
142 |
143 | def tag_new_version():
144 | """Updates the version number, pushing the changes and tagging afterwards.
145 | """
146 | # Checks if there are changed or untracked files
147 | git_status_file = 'build/git_status'
148 | local('git status > %s' % git_status_file, fail='ignore')
149 | if re.search(r'(Changed|Untracked)', file(git_status_file, 'r').read()):
150 | print 'There are changed or untracked files. Aborting...'
151 | return
152 |
153 | # Brings up the text editor with the files to be changed
154 | for f in env.new_version_files:
155 | local('%s %s' % (env.default_editor, f))
156 |
157 | # Asks for confirmation
158 | prompt('tag_proceed', 'You are about to commit and push the version '
159 | 'changes. Continue?', default='y')
160 |
161 | if env.tag_proceed.upper() != 'Y':
162 | print 'Aborting...'
163 | return
164 |
165 | # Commits and tags the new release
166 | from djangoflash import __version__
167 | local('git commit -am "Updated version number."; git push', fail='ignore')
168 | local('git tag -am "Tagged version %s." %s; git push --tags' %
169 | ((__version__,)*2), fail='ignore')
170 | local('git push --tags')
171 |
172 |
--------------------------------------------------------------------------------
/doc/source/configuration.rst:
--------------------------------------------------------------------------------
1 | .. _configuration:
2 |
3 | Configuration
4 | -------------
5 |
6 | In order to plug Django-Flash to your project, open your project's
7 | ``settings.py`` file and do the following changes::
8 |
9 | TEMPLATE_CONTEXT_PROCESSORS = (
10 | 'djangoflash.context_processors.flash',
11 | )
12 |
13 | MIDDLEWARE_CLASSES = (
14 | 'django.contrib.sessions.middleware.SessionMiddleware',
15 | 'djangoflash.middleware.FlashMiddleware',
16 | )
17 |
18 |
19 | That's all the required configuration.
20 |
21 | .. warning::
22 | The :class:`djangoflash.middleware.FlashMiddleware` class must be declared
23 | after the :class:`SessionMiddleware` class.
24 |
25 |
26 | Django-Flash and requests to media files
27 | ````````````````````````````````````````
28 |
29 | Django itself doesn’t serve static (media) files, such as images, style sheets,
30 | or video. It leaves that job to whichever web server you choose. But, *during
31 | development*, you can use the :meth:`django.views.static.serve` view to serve
32 | media files.
33 |
34 | The problem with it is that, as a regular view, requests to
35 | :meth:`django.views.static.serve` trigger the installed middlewares. And since
36 | the *flash* gets updated by :ref:`a middleware `, messages might be
37 | removed from the *flash* by accident if the response causes the web browser to
38 | issue requests to fetch static files.
39 |
40 | To make Django-Flash work well with the :meth:`django.views.static.serve` view,
41 | you can add the setting ``FLASH_IGNORE_MEDIA`` to your project's
42 | ``settings.py`` file::
43 |
44 | FLASH_IGNORE_MEDIA = True # Optional. Default: DEBUG
45 |
46 | Set the ``FLASH_IGNORE_MEDIA`` setting to ``True``, and Django-Flash won't
47 | remove any message from the *flash* if the request URL resolves to
48 | :meth:`django.views.static.serve`. Otherwise, every request will trigger the
49 | :class:`djangoflash.middleware.FlashMiddleware` as usual.
50 |
51 | .. note::
52 | This setting is optional; its default value is ``DEBUG``. So, if you adjust
53 | the ``DEBUG`` setting according to the environment in which the application
54 | runs (as you *should*), you don't have to worry about this setting at all,
55 | things will just work.
56 |
57 |
58 | Flash storage backends
59 | ``````````````````````
60 |
61 | Since :ref:`version 1.5`, Django-Flash supports custom flash
62 | storage backends.
63 |
64 | By default, Django-Flash provides two built-in storage backends:
65 |
66 | * :mod:`djangoflash.storage.session` -- Session-based storage (default);
67 | * :mod:`djangoflash.storage.cookie` -- Cookie-based storage;
68 |
69 | .. seealso::
70 | :ref:`custom_storages`
71 |
72 |
73 | Using the session-based storage
74 | '''''''''''''''''''''''''''''''
75 |
76 | Django-Flash uses the :ref:`session-based storage ` by default,
77 | so you don't need to do anything else to use it.
78 |
79 | *Although you are not required to do so*, you can add the following setting to
80 | your project's ``settings.py`` file to make it clear about what flash storage
81 | backend is being used::
82 |
83 | FLASH_STORAGE = 'session' # Optional
84 |
85 |
86 | This storage backend *doesn't* rely on codecs to serialize and de-serialize the
87 | flash data; it lets Django handle this.
88 |
89 |
90 | Using the cookie-based storage
91 | ''''''''''''''''''''''''''''''
92 |
93 | If you want to use the :ref:`cookie-based storage ` instead the
94 | default one, then add the following setting to the ``settings.py`` file::
95 |
96 | FLASH_STORAGE = 'cookie'
97 |
98 |
99 | Since cookies will be used to store the contents of the flash scope,
100 | Django-Flash doesn't require you to add the :class:`SessionMiddleware` class
101 | to the ``MIDDLEWARE_CLASSES`` section of your project's settings anymore.
102 |
103 | This storage backend relies on codecs to serialize and de-serialize the flash
104 | data.
105 |
106 |
107 | Flash serialization codecs
108 | ``````````````````````````
109 |
110 | Since :ref:`version 1.7`, Django-Flash supports custom flash
111 | serialization codecs.
112 |
113 | By default, Django-Flash provides three built-in codecs:
114 |
115 | * :mod:`djangoflash.codec.json_impl` -- JSON-based codec (default);
116 | * :mod:`djangoflash.codec.json_zlib_impl` -- JSON/zlib-based codec;
117 | * :mod:`djangoflash.codec.pickle_impl` -- Pickle-based codec;
118 |
119 | .. seealso::
120 | :ref:`custom_codecs`
121 |
122 |
123 | Using the JSON-based codec implementation
124 | '''''''''''''''''''''''''''''''''''''''''
125 |
126 | For security reasons, Django-flash uses the
127 | :ref:`JSON-based codec implementation ` by default, so you don't
128 | need to do anything else to use it.
129 |
130 | *Although you are not required to do so*, you can add the following setting to
131 | your project's ``settings.py`` file to make it clear about what codec
132 | implementation is being used::
133 |
134 | FLASH_CODEC = 'json' # Optional
135 |
136 |
137 | There's also an :ref:`alternative version ` of this codec that
138 | uses the :mod:`zlib` module to reduce the encoded flash footprint. This is
139 | particularly useful when the flash storage backend in use (such as the
140 | :ref:`cookie-based storage `) cannot handle the amount of data
141 | in the *flash*::
142 |
143 | FLASH_CODEC = 'json_zlib'
144 |
145 |
146 | Using the Pickle-based codec implementation
147 | '''''''''''''''''''''''''''''''''''''''''''
148 |
149 | If you want to use the :ref:`Pickle-based codec implementation `
150 | instead the default one, then add the following setting to the ``settings.py``
151 | file::
152 |
153 | FLASH_CODEC = 'pickle'
154 |
155 |
156 | .. warning::
157 | The use of this codec is not recommended since the
158 | `Pickle documentation `_ itself
159 | clearly states that it's not intended to be secure against erroneous or
160 | maliciously constructed data.
161 |
162 |
--------------------------------------------------------------------------------
/doc/source/changelog.rst:
--------------------------------------------------------------------------------
1 | .. _changelog:
2 |
3 | Changelog
4 | =========
5 |
6 | Like any other piece of software, Django-Flash is evolving at each release.
7 | Here you can track our progress:
8 |
9 | **Version 1.8** *(Feb 12, 2011)*
10 |
11 | * **Notice:** *breaks backwards compatibility;*
12 | * Removed :meth:`djangoflash.models.FlashScope.__call__` in order to avoid
13 | problems in Django 1.3;
14 | * Removed deprecated (since version 1.7.1) method
15 | :meth:`djangoflash.models.FlashScope.put_immediate` in favor of
16 | ``flash.now[key] = value``;
17 |
18 | **Version 1.7.2** *(May 20, 2010)*
19 |
20 | * **Notice:** Django 1.2 already provides a built-in user "messages" framework,
21 | but `we'll continue to support Django-Flash `_;
22 | * Updated test code to make it work properly on post-1.2 versions of Django;
23 |
24 | **Version 1.7.1** *(March 20, 2010)*
25 |
26 | * **Notice:** *breaks backwards compatibility;*
27 | * Removed deprecated (since version 1.4.2) method
28 | :meth:`djangoflash.models.FlashScope.has_key`;
29 | * Deprecating method :meth:`djangoflash.models.FlashScope.put_immediate` in
30 | favor of ``flash.now[key] = value``;
31 | * Deprecating method :meth:`djangoflash.models.FlashScope.put` in favor of
32 | ``flash(key=value)``;
33 | * Method :meth:`djangoflash.models.FlashScope.add` can now append several values
34 | to the given key;
35 | * Added a method :meth:`add` to :attr:`djangoflash.models.FlashScope.now` that
36 | simplifies the storage of multiple immediate values under the same key;
37 |
38 | **Version 1.7** *(October 25, 2009)*
39 |
40 | * Added support for custom flash serialization codecs;
41 | * Three built-in codec implementations: JSON, JSON/zlib and Pickle;
42 | * Module :mod:`djangoflash.storage.base` removed;
43 |
44 | **Version 1.6.3** *(October 07, 2009)*
45 |
46 | * Using the ``DEBUG`` setting as the default value of ``FLASH_IGNORE_MEDIA``;
47 |
48 | **Version 1.6.2** *(September 18, 2009)*
49 |
50 | * Done some work to avoid the loss of messages when the
51 | :class:`CommonMiddleware` returns a :class:`HttpResponseRedirect` due to a
52 | missing trailing slash;
53 |
54 | **Version 1.6.1** *(August 19, 2009)*
55 |
56 | * Now the middleware checks if the request resolves to
57 | :meth:`django.views.static.serve` instead of relying on the ``MEDIA_URL``
58 | setting;
59 |
60 | **Version 1.6** *(August 13, 2009)*
61 |
62 | * Fixed a bug in which messages are prematurely removed from the flash when
63 | they are replaced using ``flash.now`` in some circumstances;
64 | * Added the ``FLASH_IGNORE_MEDIA`` setting to let the user choose whether
65 | requests to static files should be ignored;
66 |
67 | **Version 1.5.3** *(July 22, 2009)*
68 |
69 | * Fixed a bug in the middleware which causes flash data to be dicarded after
70 | requests to static files;
71 |
72 | **Version 1.5.2** *(July 15, 2009)*
73 |
74 | * Added a :meth:`djangoflash.decorators.keep_messages` decorator for keeping
75 | flash messages;
76 | * New ``AUTHORS`` file;
77 |
78 | **Version 1.5.1** *(June 26, 2009)*
79 |
80 | * Added a method :meth:`djangoflash.models.FlashScope.add` that simplifies the
81 | storage of multiple values under the same key;
82 |
83 | **Version 1.5** *(June 24, 2006)*
84 |
85 | * License changed from LGPL to BSD to give uses more freedom;
86 | * Added support for custom flash storage backends;
87 | * Added a cookie-based flash storage;
88 | * Default session-based storage was factored out to an independent class;
89 | * Added a few more sanity checks;
90 |
91 | **Version 1.4.4** *(June 09, 2009)*
92 |
93 | * Fixed a critical bug in the middleware;
94 |
95 | **Version 1.4.3** *(June 08, 2009)*
96 |
97 | * Added a few more sanity checks;
98 |
99 | **Version 1.4.2** *(February 13, 2009)*
100 |
101 | * Deprecating method :meth:`djangoflash.models.FlashScope.has_key` in favor of
102 | ``key in flash``;
103 | * Documentation improvements;
104 | * Internals refactoring;
105 |
106 | **Version 1.4.1** *(February 06, 2009)*
107 |
108 | * Immediate values (:attr:`djangoflash.models.FlashScope.now`) can be
109 | manipulated using a dict-like syntax;
110 | * Unit test improvements;
111 | * Documentation improvements;
112 |
113 | **Version 1.4** *(February 05, 2009)*
114 |
115 | * **Notice:** *breaks backwards compatibility;*
116 | * Now Django-Flash works pretty much like the original `Ruby on Rails`_' flash;
117 | * Several code optmizations;
118 | * Several improvements on the test suite;
119 |
120 | **Version 1.3.5** *(February 03, 2009)*
121 |
122 | * Several documentation improvements;
123 | * Improvements on source code comments and unit tests;
124 |
125 | **Version 1.3.4** *(February 01, 2009)*
126 |
127 | * Added Sphinx_-based documentation;
128 | * Source code changed to improve the Pylint_ score;
129 | * :mod:`djangoflash` module now have a ``__version__`` property, which is
130 | very useful when you need to know what version of the Django-Flash is
131 | installed in your machine;
132 |
133 | **Version 1.3.3** *(January 31, 2009)*
134 |
135 | * *Critical Bug Fixed*: Django-Flash creates several useless session
136 | entries when the cookie support in user's browser is disabled;
137 | * Small improvements on unit tests;
138 |
139 | **Version 1.3.2** *(December 07, 2008)*
140 |
141 | * Small fixes;
142 |
143 | **Version 1.3.1** *(December 07, 2008)*
144 |
145 | * Added some sanity checks;
146 |
147 | **Version 1.3** *(December 07, 2008)*
148 |
149 | * **Notice:** *breaks backwards compatibility;*
150 | * Django-Flash now controls the expiration of flash-scoped values
151 | individually, which means that only expired values are removed from the
152 | session (and not the whole flash context);
153 | * Unit testing code was completely rewritten and now a real Django
154 | application is used in integration tests;
155 | * Huge source code review to make it easier to read and to assure the use
156 | of Python conventions;
157 | * Project renamed to **Django-Flash** (it was previously called
158 | **djangoflash**, without the hyphen);
159 |
160 | **Version 1.2** *(November 01, 2008)*
161 |
162 | * **Notice:** *breaks backwards compatibility;*
163 | * Improvements on the test comments;
164 | * Now the flash scope works pretty much like a :class:`dict`, although
165 | still there's no value-based expiration (the whole flash scope expires at
166 | the end of the request);
167 |
168 | **Version 1.1** *(November 01, 2008)*
169 |
170 | * Now using SetupTools_ to make the project easier to distribute;
171 |
172 | **Version 1.0** *(October 22, 2008)*
173 |
174 | * First (very simple) version;
175 |
176 | .. _Ruby on Rails: http://www.rubyonrails.org/
177 | .. _SetupTools: http://pypi.python.org/pypi/setuptools/
178 | .. _Sphinx: http://sphinx.pocoo.org/
179 | .. _Pylint: http://www.logilab.org/857
180 |
181 |
--------------------------------------------------------------------------------
/doc/source/conf.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | #
3 | # django-flash documentation build configuration file, created by
4 | # sphinx-quickstart on Sun Feb 1 01:49:12 2009.
5 | #
6 | # This file is execfile()d with the current directory set to its containing dir.
7 | #
8 | # The contents of this file are pickled, so don't put values in the namespace
9 | # that aren't pickleable (module imports are okay, they're removed automatically).
10 | #
11 | # Note that not all possible configuration values are present in this
12 | # autogenerated file.
13 | #
14 | # All configuration values have a default; values that are commented out
15 | # serve to show the default.
16 |
17 | import sys, os
18 |
19 | # If your extensions are in another directory, add it here. If the directory
20 | # is relative to the documentation root, use os.path.abspath to make it
21 | # absolute, like shown here.
22 | sys.path.append('./src')
23 |
24 | # General configuration
25 | # ---------------------
26 |
27 | # Add any Sphinx extension module names here, as strings. They can be extensions
28 | # coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
29 | extensions = ['sphinx.ext.autodoc', 'sphinx.ext.doctest']
30 |
31 | # Add any paths that contain templates here, relative to this directory.
32 | templates_path = ['_templates']
33 |
34 | # The suffix of source filenames.
35 | source_suffix = '.rst'
36 |
37 | # The encoding of source files.
38 | #source_encoding = 'utf-8'
39 |
40 | # The master toctree document.
41 | master_doc = 'index'
42 |
43 | # General information about the project.
44 | project = u'Django-Flash'
45 | copyright = u'2008-2010, Destaquenet Technology Solutions'
46 |
47 | # The version info for the project you're documenting, acts as replacement for
48 | # |version| and |release|, also used in various other places throughout the
49 | # built documents.
50 | #
51 | # The short X.Y version.
52 | version = '1.8'
53 | # The full version, including alpha/beta/rc tags.
54 | release = '1.8'
55 |
56 | # The language for content autogenerated by Sphinx. Refer to documentation
57 | # for a list of supported languages.
58 | # language = None
59 |
60 | # There are two options for replacing |today|: either, you set today to some
61 | # non-false value, then it is used:
62 | #today = ''
63 | # Else, today_fmt is used as the format for a strftime call.
64 | #today_fmt = '%B %d, %Y'
65 |
66 | # List of documents that shouldn't be included in the build.
67 | #unused_docs = []
68 |
69 | # List of directories, relative to source directory, that shouldn't be searched
70 | # for source files.
71 | exclude_trees = []
72 |
73 | # The reST default role (used for this markup: `text`) to use for all documents.
74 | #default_role = None
75 |
76 | # If true, '()' will be appended to :func: etc. cross-reference text.
77 | #add_function_parentheses = True
78 |
79 | # If true, the current module name will be prepended to all description
80 | # unit titles (such as .. function::).
81 | #add_module_names = True
82 |
83 | # If true, sectionauthor and moduleauthor directives will be shown in the
84 | # output. They are ignored by default.
85 | #show_authors = False
86 |
87 | # The name of the Pygments (syntax highlighting) style to use.
88 | pygments_style = 'sphinx'
89 |
90 |
91 | # Options for HTML output
92 | # -----------------------
93 |
94 | # The style sheet to use for HTML and HTML Help pages. A file of that name
95 | # must exist either in Sphinx' static/ path, or in one of the custom paths
96 | # given in html_static_path.
97 | html_style = 'default.css'
98 |
99 | # The name for this set of Sphinx documents. If None, it defaults to
100 | # " v documentation".
101 | #html_title = None
102 |
103 | # A shorter title for the navigation bar. Default is the same as html_title.
104 | #html_short_title = None
105 |
106 | # The name of an image file (relative to this directory) to place at the top
107 | # of the sidebar.
108 | #html_logo = None
109 |
110 | # The name of an image file (within the static path) to use as favicon of the
111 | # docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32
112 | # pixels large.
113 | #html_favicon = None
114 |
115 | # Add any paths that contain custom static files (such as style sheets) here,
116 | # relative to this directory. They are copied after the builtin static files,
117 | # so a file named "default.css" will overwrite the builtin "default.css".
118 | html_static_path = ['_static']
119 |
120 | # If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
121 | # using the given strftime format.
122 | #html_last_updated_fmt = '%b %d, %Y'
123 |
124 | # If true, SmartyPants will be used to convert quotes and dashes to
125 | # typographically correct entities.
126 | #html_use_smartypants = True
127 |
128 | # Custom sidebar templates, maps document names to template names.
129 | #html_sidebars = {}
130 |
131 | # Additional templates that should be rendered to pages, maps page names to
132 | # template names.
133 | #html_additional_pages = {}
134 |
135 | # If false, no module index is generated.
136 | #html_use_modindex = True
137 |
138 | # If false, no index is generated.
139 | #html_use_index = True
140 |
141 | # If true, the index is split into individual pages for each letter.
142 | #html_split_index = False
143 |
144 | # If true, the reST sources are included in the HTML build as _sources/.
145 | #html_copy_source = True
146 |
147 | # If true, an OpenSearch description file will be output, and all pages will
148 | # contain a tag referring to it. The value of this option must be the
149 | # base URL from which the finished HTML is served.
150 | #html_use_opensearch = ''
151 |
152 | # If nonempty, this is the file name suffix for HTML files (e.g. ".xhtml").
153 | #html_file_suffix = ''
154 |
155 | # Output file base name for HTML help builder.
156 | htmlhelp_basename = 'django-flashdoc'
157 |
158 |
159 | # Options for LaTeX output
160 | # ------------------------
161 |
162 | # The paper size ('letter' or 'a4').
163 | #latex_paper_size = 'letter'
164 |
165 | # The font size ('10pt', '11pt' or '12pt').
166 | #latex_font_size = '10pt'
167 |
168 | # Grouping the document tree into LaTeX files. List of tuples
169 | # (source start file, target name, title, author, document class [howto/manual]).
170 | latex_documents = [
171 | ('index', 'django-flash.tex', ur'Django-flash Documentation',
172 | ur'Destaquenet Technology Solutions', 'manual'),
173 | ]
174 |
175 | # The name of an image file (relative to this directory) to place at the top of
176 | # the title page.
177 | #latex_logo = None
178 |
179 | # For "manual" documents, if this is true, then toplevel headings are parts,
180 | # not chapters.
181 | #latex_use_parts = False
182 |
183 | # Additional stuff for the LaTeX preamble.
184 | #latex_preamble = ''
185 |
186 | # Documents to append as an appendix to all manuals.
187 | #latex_appendices = []
188 |
189 | # If false, no module index is generated.
190 | #latex_use_modindex = True
191 |
192 | # Fix import errors
193 | from django.conf import settings
194 | settings.configure()
195 |
--------------------------------------------------------------------------------
/src/djangoflash/tests/storage.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 |
3 | """djangoflash.storage test cases.
4 | """
5 |
6 | from unittest import TestCase
7 |
8 | from django.http import HttpRequest, HttpResponse
9 |
10 | from djangoflash.models import FlashScope
11 | from djangoflash import storage
12 | from djangoflash.storage import session, cookie
13 |
14 |
15 | class StorageTestCase(TestCase):
16 | """Tests methods used to parse flash storage URIs and create flash storage
17 | objects.
18 | """
19 | def test_get_session_storage_by_alias(self):
20 | """Storage: 'session' should resolve to session flash storage.
21 | """
22 | storage_impl = storage.get_storage('session')
23 | self.assertTrue(isinstance(storage_impl, session.FlashStorageClass))
24 |
25 | def test_get_cookie_storage_by_alias(self):
26 | """Storage: 'cookie' should resolve to cookie flash storage.
27 | """
28 | storage_impl = storage.get_storage('cookie')
29 | self.assertTrue(isinstance(storage_impl, cookie.FlashStorageClass))
30 |
31 | def test_get_storage_by_module_name(self):
32 | """Storage: 'djangoflash.storage.cookie' should resolve to cookie flash storage.
33 | """
34 | storage_impl = storage.get_storage('djangoflash.storage.cookie')
35 | self.assertTrue(isinstance(storage_impl, cookie.FlashStorageClass))
36 |
37 | def test_get_storage_by_invalid_module_name(self):
38 | """Storage: Should raise an error when resolving a module name that doesn't exists.
39 | """
40 | operation = lambda: storage.get_storage('invalid.module.path')
41 | self.assertRaises(ImportError, operation)
42 |
43 | def test_get_storage_by_invalid_module(self):
44 | """Storage: Should raise an error when module doesn't provide a storage class.
45 | """
46 | operation = lambda: storage.get_storage('djangoflash.models')
47 | self.assertRaises(AttributeError, operation)
48 |
49 |
50 | class SessionFlashStorageTestCase(TestCase):
51 | """Tests the session-based flash storage class.
52 | """
53 | def setUp(self):
54 | """Creates a cookie-based flash storage for testing.
55 | """
56 | self.request = HttpRequest()
57 | self.request.session = {}
58 | self.response = HttpResponse('')
59 | self.flash = FlashScope()
60 | self.storage = session.FlashStorageClass()
61 |
62 | def _get_flash(self):
63 | """Returns the flash contents from the session.
64 | """
65 | return self.request.session[self.storage._key]
66 |
67 | def test_set_null_object(self):
68 | """SessionStorage: should not store null values.
69 | """
70 | self.storage.set(None, self.request, self.response)
71 | self.assertEqual(0, len(self.request.session))
72 |
73 | def test_set_empty_object(self):
74 | """SessionStorage: should not store empty objects.
75 | """
76 | self.storage.set(self.flash, self.request, self.response)
77 | self.assertEqual(0, len(self.request.session))
78 |
79 | def test_clear_storage(self):
80 | """SessionStorage: should remove flash contents from the session.
81 | """
82 | self.flash['message'] = 'Message'
83 | self.storage.set(self.flash, self.request, self.response)
84 | self.assertEqual('Message', self._get_flash()['message'])
85 |
86 | # The flash should be completely removed from the session
87 | del self.flash['message']
88 | self.storage.set(self.flash, self.request, self.response)
89 | self.assertRaises(KeyError, self._get_flash)
90 |
91 | def test_set_object(self):
92 | """Session storage: should store valid objects.
93 | """
94 | self.flash['message'] = 'Message'
95 | self.storage.set(self.flash, self.request, self.response)
96 | self.assertEqual(1, len(self.request.session))
97 |
98 | def test_get_empty(self):
99 | """SessionStorage: should return nothing when empty.
100 | """
101 | self.assertEqual(None, self.storage.get(self.request))
102 |
103 | def test_get(self):
104 | """SessionStorage: should return the stored object.
105 | """
106 | self.flash['message'] = 'Message'
107 | self.storage.set(self.flash, self.request, self.response)
108 | self.assertEqual('Message', self.storage.get(self.request)['message'])
109 |
110 |
111 | class CookieFlashStorageTestCase(TestCase):
112 | """Tests the cookie-based flash storage class.
113 | """
114 | def setUp(self):
115 | """Creates a cookie-based flash storage for testing.
116 | """
117 | self.request = HttpRequest()
118 | self.response = HttpResponse('')
119 | self.flash = FlashScope()
120 | self.storage = cookie.FlashStorageClass()
121 |
122 | def _transfer_cookies_from_response_to_request(self):
123 | """Transfers the cookies set in the response to the request.
124 | """
125 | for key, cookie in self.response.cookies.items():
126 | self.request.COOKIES[key] = cookie.value
127 |
128 | def _get_cookie(self):
129 | """Returns the cookie used to store the flash contents.
130 | """
131 | return self.response.cookies[self.storage._key]
132 |
133 | def test_set_null_object(self):
134 | """CookieStorage: should not store null values.
135 | """
136 | self.storage.set(None, self.request, self.response)
137 | self.assertEqual(0, len(self.response.cookies))
138 |
139 | def test_set_empty_object(self):
140 | """CookieStorage: should not store an empty object.
141 | """
142 | self.storage.set(self.flash, self.request, self.response)
143 | self.assertEqual(0, len(self.response.cookies))
144 |
145 | def test_clear_storage(self):
146 | """CookieStorage: should set an empty/expired cookie.
147 | """
148 | self.flash['message'] = 'Message'
149 | self.storage.set(self.flash, self.request, self.response)
150 |
151 | # Simulates a request-response cycle
152 | self._transfer_cookies_from_response_to_request()
153 | del self.flash['message']
154 |
155 | # Cookie should be empty/expired
156 | self.storage.set(self.flash, self.request, self.response)
157 | self.assertEqual(0, self._get_cookie()['max-age'])
158 | self.assert_(not self._get_cookie().value)
159 |
160 | def test_set_object(self):
161 | """CookieStorage: should store valid objects.
162 | """
163 | self.flash['message'] = 'Message'
164 | self.storage.set(self.flash, self.request, self.response)
165 | self.assertEqual(1, len(self.response.cookies))
166 |
167 | def test_get_empty(self):
168 | """CookieStorage: should return nothing when empty.
169 | """
170 | self.assertEqual(None, self.storage.get(self.request))
171 |
172 | def test_get(self):
173 | """CookieStorage: should return the stored object.
174 | """
175 | self.flash['message'] = 'Message'
176 | self.storage.set(self.flash, self.request, self.response)
177 | self.assertEqual(None, self.storage.get(self.request))
178 |
179 | # Simulates a request-response cycle
180 | self._transfer_cookies_from_response_to_request()
181 | self.assertEqual('Message', self.storage.get(self.request)['message'])
182 |
--------------------------------------------------------------------------------
/src/djangoflash/tests/codec.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 |
3 | """djangoflash.codec test cases.
4 | """
5 |
6 | from unittest import TestCase
7 |
8 | from django.core.exceptions import SuspiciousOperation
9 |
10 | from djangoflash import codec
11 | from djangoflash.codec import pickle_impl, json_impl, json_zlib_impl, BaseCodec
12 | from djangoflash.models import FlashScope
13 |
14 |
15 | class CodecTestCase(TestCase):
16 | """Tests methods used to parse flash storage URIs and create flash storage
17 | objects.
18 | """
19 | def test_get_pickle_codec_by_alias(self):
20 | """Codec: 'pickle' should resolve to Pickle-based codec.
21 | """
22 | codec_impl = codec.get_codec('pickle')
23 | self.assertTrue(isinstance(codec_impl, pickle_impl.CodecClass))
24 |
25 | def test_get_json_codec_by_alias(self):
26 | """Codec: 'json' should resolve to JSON-based codec.
27 | """
28 | codec_impl = codec.get_codec('json')
29 | self.assertTrue(isinstance(codec_impl, json_impl.CodecClass))
30 |
31 | def test_get_json_zlib_codec_by_alias(self):
32 | """Codec: 'json_zlib' should resolve to JSON/zlib-based codec.
33 | """
34 | codec_impl = codec.get_codec('json_zlib')
35 | self.assertTrue(isinstance(codec_impl, json_impl.CodecClass))
36 | self.assertTrue(isinstance(codec_impl, json_zlib_impl.CodecClass))
37 |
38 | def test_get_codec_by_module_name(self):
39 | """Codec: 'djangoflash.codec.json_impl' should resolve to JSON-based codec.
40 | """
41 | codec_impl = codec.get_codec('djangoflash.codec.json_impl')
42 | self.assertTrue(isinstance(codec_impl, json_impl.CodecClass))
43 |
44 | def test_get_codec_by_invalid_module_name(self):
45 | """Codec: Should raise an error when resolving a module name that doesn't exists.
46 | """
47 | operation = lambda: codec.get_codec('invalid.module.path')
48 | self.assertRaises(ImportError, operation)
49 |
50 | def test_get_codec_by_invalid_module(self):
51 | """Codec: Should raise an error when module doesn't provide a codec class.
52 | """
53 | operation = lambda: codec.get_codec('djangoflash.models')
54 | self.assertRaises(AttributeError, operation)
55 |
56 |
57 | class BaseCodecTestCase(TestCase):
58 | """Tests the tampered checks and signing logic.
59 | """
60 | def setUp(self):
61 | """Creates a codec and a sample flash.
62 | """
63 | self.codec = json_impl.CodecClass()
64 | self.flash = FlashScope()
65 | self.flash['info'] = 'Info'
66 | self.flash.update()
67 | self.expected = 'eyJfc2Vzc2lvbiI6IHsiaW5mbyI6ICJJbmZvIn0sICJfdXNlZCI6' \
68 | 'IHsiaW5mbyI6IG51bGx9fWZk\nNDViYTljMmU3MWJlZjBjYjcxOW' \
69 | 'EwYjdlYzJlZjUx\n'
70 |
71 | def test_encode_and_sign(self):
72 | """Codec: BaseCodec should return an encoded and signed version of the flash.
73 | """
74 | encoded_and_signed = self.codec.encode_and_sign(self.flash)
75 | self.assertEqual(self.expected, encoded_and_signed)
76 |
77 | def test_decoded_signed(self):
78 | """Codec: BaseCodec should decode an encoded and signed version of the flash.
79 | """
80 | flash = self.codec.decode_signed(self.expected)
81 | self.assertEqual('Info', flash['info'])
82 | flash.update()
83 | self.assertFalse('info' in flash)
84 |
85 | def test_decoded_tampered(self):
86 | """Codec: BaseCodec should not decode a tampered version of the flash.
87 | """
88 | tampered = 'eyJfc2Vzc2lvbiI6IHsiaW6mbyI6ICJJbmZvIn0sICJfdXNlZCI6IHsia' \
89 | 'W5mbyI6IG51bGx9fWZk\nNDViYTljMmU3MWJlZjBjYjcxOWEwYjdlYzJl' \
90 | 'ZjUx\n'
91 | operation = lambda: self.codec.decode_signed(tampered)
92 | self.assertRaises(SuspiciousOperation, operation)
93 |
94 |
95 | class PickleCodecTestCase(TestCase):
96 | """Tests the Pickle-based serialization codec implementation.
97 | """
98 | def setUp(self):
99 | """Creates a Pickle-based codec and a sample flash.
100 | """
101 | self.codec = pickle_impl.CodecClass()
102 | self.flash = FlashScope()
103 | self.flash['info'] = 'Info'
104 | self.flash.update()
105 | self.expected = '\x80\x02cdjangoflash.models\nFlashScope\nq\x01)\x81q' \
106 | '\x02}q\x03(U\x03nowq\x04cdjangoflash.models\n_Immedi' \
107 | 'ateFlashScopeAdapter\nq\x05)\x81q\x06}q\x07U\x08dele' \
108 | 'gateq\x08h\x02sbU\x08_sessionq\t}q\nU\x04infoq\x0bU' \
109 | '\x04Infoq\x0csU\x05_usedq\r}q\x0eh\x0bNsub.'
110 |
111 | def test_encode(self):
112 | """Codec: Pickle-based codec should return a Pickle dump of the flash.
113 | """
114 | self.assertEqual(self.expected, self.codec.encode(self.flash))
115 |
116 | def test_decode(self):
117 | """Codec: Pickle-based codec should restore the flash from a Pickle dump string.
118 | """
119 | flash = self.codec.decode(self.expected)
120 | self.assertEqual('Info', flash['info'])
121 | flash.update()
122 | self.assertFalse('info' in flash)
123 |
124 |
125 | class JSONCodecTestCase(TestCase):
126 | """Tests the JSON-based serialization codec implementation.
127 | """
128 | def setUp(self):
129 | """Creates a JSON-based codec and a sample flash.
130 | """
131 | self.expected = '{"_session": {"info": "Info"}, ' \
132 | '"_used": {"info": null}}'
133 | self.codec = json_impl.CodecClass()
134 | self.flash = FlashScope()
135 | self.flash['info'] = 'Info'
136 | self.flash.update()
137 |
138 | def test_encode(self):
139 | """Codec: JSON-based codec should return a JSON version of the flash.
140 | """
141 | self.assertEqual(self.expected, self.codec.encode(self.flash))
142 |
143 | def test_decode(self):
144 | """Codec: JSON-based codec should restore the flash from a JSON string.
145 | """
146 | flash = self.codec.decode(self.expected)
147 | self.assertEqual('Info', flash['info'])
148 | flash.update()
149 | self.assertFalse('info' in flash)
150 |
151 |
152 | class JSONZlibCodecTestCase(TestCase):
153 | """Tests the JSON/zlib-based serialization codec implementation.
154 | """
155 | def setUp(self):
156 | """Creates a JSON\zlib-based codec and a sample flash.
157 | """
158 | self.expected = 'x\x9c\xabV\x8a/N-.\xce\xcc\xcfS\xb2R\xa8V\xca\xccK' \
159 | '\xcb\x072\x94
199 |
200 | My template
201 |
202 |
203 | {% if flash.message %}
204 |
205 |
206 |
207 |
{{ flash.message }}
208 |
209 | {% endif %}
210 |
211 |