Skip to content

Commit

Permalink
Add mirror 'applications/template_utils/'.
Browse files Browse the repository at this point in the history
  • Loading branch information
bryanveloso committed Jun 22, 2008
1 parent 73e74d3 commit 8b96e13
Show file tree
Hide file tree
Showing 10 changed files with 762 additions and 0 deletions.
7 changes: 7 additions & 0 deletions .braids
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
applications/template_utils:
local_branch: braid/svn/applications/template-utils
type: svn
revision:
squash: true
remote: http://django-template-utils.googlecode.com/svn/trunk/template_utils
Empty file.
32 changes: 32 additions & 0 deletions applications/template_utils/context_processors.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
"""
A generic function for generating context processors, and a processor
which adds media-specific settings to each ``RequestContext``.
"""

def settings_processor(*settings_list):
"""
Generates and returns a context processor function which will read
the values of all the settings passed in and return them in each
``RequestContext`` in which it is applied.
For example::
my_settings_processor = settings_processor('INTERNAL_IPS', 'SITE_ID')
``my_settings_processor`` would then be a valid context processor
which would return the values of the settings ``INTERNAL_IPS`` and
``SITE_ID`` in each ``RequestContext`` in which it was applied.
"""
def _processor(request):
from django.conf import settings
settings_dict = {}
for setting_name in settings_list:
settings_dict[setting_name] = getattr(settings, setting_name)
return settings_dict
return _processor

media = settings_processor('ADMIN_MEDIA_PREFIX', 'MEDIA_URL')
media.__doc__ = """A context processor which adds the values of the settings
``ADMIN_MEDIA_PREFIX`` and ``MEDIA_URL`` to a ``RequestContext``."""
215 changes: 215 additions & 0 deletions applications/template_utils/markup.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,215 @@
"""
Utilities for text-to-HTML conversion.
"""


def textile(text, **kwargs):
"""
Applies Textile conversion to a string, and returns the HTML.
This is simply a pass-through to the ``textile`` template filter
included in ``django.contrib.markup``, which works around issues
PyTextile has with Unicode strings. If you're not using Django but
want to use Textile with ``MarkupFormatter``, you'll need to
supply your own Textile filter.
"""
from django.contrib.markup.templatetags.markup import textile
return textile(text)

def markdown(text, **kwargs):
"""
Applies Markdown conversion to a string, and returns the HTML.
"""
import markdown
return markdown.markdown(text, **kwargs)

def restructuredtext(text, **kwargs):
"""
Applies reStructuredText conversion to a string, and returns the
HTML.
"""
from docutils import core
parts = core.publish_parts(source=text,
writer_name='html4css1',
**kwargs)
return parts['fragment']

DEFAULT_MARKUP_FILTERS = {
'textile': textile,
'markdown': markdown,
'restructuredtext': restructuredtext
}


class MarkupFormatter(object):
"""
Generic markup formatter which can handle multiple text-to-HTML
conversion systems.
Overview
========
Conversion is handled by filter functions registered with an
instance; a set of default filters is provided which cover
Markdown, reStructuredText and Textile (though using one of these
requires the appropriate module to be available on your system --
e.g., using the reST filter requires you to have ``docutils``
installed).
New filters can be added by registering them with an instance;
simply define a function which performs the conversion you want,
and use the ``register`` method to add it; ``register`` expects
two arguments:
1. The name to associate with the filter.
2. The actual filter function.
So, for example, you might define a new filter function called
``my_filter``, and register it like so::
formatter = MarkupFormatter()
formatter.register('my_filter', my_filter)
Instances are callable, so applying the conversion to a string is
simple::
my_html = formatter(my_string, filter_name='my_filter')
The filter to use for conversion is determined in either of two
ways:
1. If the keyword argument ``filter_name`` is supplied, it will be
used as the filter name.
2. Absent an explicit argument, the filter name will be taken from
the ``MARKUP_FILTER`` setting in your Django settings file (see
below).
Additionally, arbitrary keyword arguments can be supplied, and
they will be passed on to the filter function.
Reading default bahavior from a Django setting
==============================================
The Django setting ``MARKUP_FILTER`` can be used to specify
default behavior; if used, its value should be a 2-tuple:
* The first element should be the name of a filter.
* The second element should be a dictionary to use as keyword
arguments for that filter.
So, for example, to have the default behavior apply Markdown with
safe mode enabled, you would add this to your Django settings
file::
MARKUP_FILTER = ('markdown', { 'safe_mode': True })
The filter named in this setting does not have to be from the
default set; as long as you register a filter of that name before
trying to use the formatter, it will work.
To have the default behavior apply no conversion whatsoever, set
``MARKUP_FILTER`` like so::
MARKUP_FILTER = (None, {})
When the ``filter_name`` keyword argument is supplied, the
``MARKUP_FILTER`` setting is ignored entirely -- neither a filter
name nor any keyword arguments will be read from it. This means
that, by always supplying ``filter_name`` explicitly, it is
possible to use this formatter without configuring or even
installing Django.
Django and template autoescaping
================================
Django's template system defaults to escaping the output of
template variables, which can interfere with functions intended to
return HTML. ``MarkupFormatter`` does not in any way tamper with
Django's autoescaping, so pasing the results of formatting
directly to a Django template will result in that text being
escaped.
If you need to use ``MarkupFormatter`` for items which will be
passed to a Django template as variables, use the function
``django.utils.safestring.mark_safe`` to tell Django's template
system not to escape that text.
For convenience, a Django template filter is included (in
``templatetags/generic_markup.py``) which applies
``MarkupFormatter`` to a string and marks the result as not
requiring autoescaping.
Examples
========
Using the default behavior, with the filter name and arguments
taken from the ``MARKUP_FILTER`` setting::
formatter = MarkupFormatter()
my_string = 'Lorem ipsum dolor sit amet.\n\nConsectetuer adipiscing elit.'
my_html = formatter(my_string)
Explicitly naming the filter to use::
my_html = formatter(my_string, filter_name='markdown')
Passing keyword arguments::
my_html = formatter(my_string, filter_name='markdown', safe_mode=True)
Perform no conversion (return the text as-is)::
my_html = formatter(my_string, filter_name=None)
"""
def __init__(self):
self._filters = {}
for filter_name, filter_func in DEFAULT_MARKUP_FILTERS.items():
self.register(filter_name, filter_func)

def register(self, filter_name, filter_func):
"""
Registers a new filter for use.
"""
self._filters[filter_name] = filter_func

def __call__(self, text, **kwargs):
"""
Applies text-to-HTML conversion to a string, and returns the
HTML.
"""
if 'filter_name' in kwargs:
filter_name = kwargs['filter_name']
del kwargs['filter_name']
filter_kwargs = {}
else:
from django.conf import settings
filter_name, filter_kwargs = settings.MARKUP_FILTER
if filter_name is None:
return text
if filter_name not in self._filters:
raise ValueError("'%s' is not a registered markup filter. Registered filters are: %s." % (filter_name,
', '.join(self._filters.iterkeys())))
filter_func = self._filters[filter_name]
filter_kwargs.update(**kwargs)
return filter_func(text, **filter_kwargs)


# Unless you need to have multiple instances of MarkupFormatter lying
# around, or want to subclass it, the easiest way to use it is to
# import this instance.

formatter = MarkupFormatter()
72 changes: 72 additions & 0 deletions applications/template_utils/nodes.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
"""
Subclass of ``template.Node`` for easy context updating.
"""

from django.db.models import get_model
from django.conf import settings
from django import template


class ContextUpdatingNode(template.Node):
"""
Node that updates the context with certain values.
Subclasses should define ``get_content()``, which should return a
dictionary to be added to the context.
"""
def render(self, context):
context.update(self.get_content(context))
return ''

def get_content(self, context):
raise NotImplementedError


class GenericContentNode(ContextUpdatingNode):
"""
Base Node class for retrieving objects from any model.
By itself, this class will retrieve a number of objects from a
particular model (specified by an "app_name.model_name" string)
and store them in a specified context variable (these are the
``num``, ``model`` and ``varname`` arguments to the constructor,
respectively), but is also intended to be subclassed for
customization.
There are two ways to add extra bits to the eventual database
lookup:
1. Add the setting ``GENERIC_CONTENT_LOOKUP_KWARGS`` to your
settings file; this should be a dictionary whose keys are
"app_name.model_name" strings corresponding to models, and whose
values are dictionaries of keyword arguments which will be
passed to ``filter()``.
2. Subclass and override ``_get_query_set``; all that's expected
is that it will return a ``QuerySet`` which will be used to
retrieve the object(s). The default ``QuerySet`` for the
specified model (filtered as described above) will be available
as ``self.query_set`` if you want to work with it.
"""
def __init__(self, model, num, varname):
self.num = num
self.varname = varname
lookup_dict = getattr(settings, 'GENERIC_CONTENT_LOOKUP_KWARGS', {})
self.model = get_model(*model.split('.'))
if self.model is None:
raise template.TemplateSyntaxError("Generic content tag got invalid model: %s" % model)
self.query_set = self.model._default_manager.filter(**lookup_dict.get(model, {}))

def _get_query_set(self):
return self.query_set

def get_content(self, context):
query_set = self._get_query_set()
if self.num == 1:
result = query_set[0]
else:
result = list(query_set[:self.num])
return { self.varname: result }
Empty file.
Loading

0 comments on commit 8b96e13

Please sign in to comment.