Source

django-template-utils / docs / markup.txt

The default branch has multiple heads

===============================
Generic text-to-HTML conversion
===============================


It's extremely common to use some form of text-to-HTML conversion
utility, such as Markdown or Textile, to allow plain text entered by
staff members or users to be transformed into HTML for output, and not
terribly uncommon to even store that generated HTML in the database,
by having it converted when a model instance is saved. And, in the
bundled ``django.contrib.markup``, Django provides template filters
which can be used for Markdown, Textile and reStructuredText, making
this even easier to do.

The downside to all of this is that your code becomes coupled to the
particular system you're using: if you use Markdown, for example, you
end up applying the ``markdown`` filter in all of your templates, or
hard-coding a call to ``markdown.markdown`` in a model's ``save``
method. And although most of the popular text-to-HTML converters
support lots of useful options, Django's template filters don't really
offer a way to take advantage of them.

This module provides a generic ``MarkupFormatter`` class which aims to
solve these problems; it is designed to allow the use of any
text-to-HTML converter with minimal coupling of your code.


Basic usage
===========

The ``MarkupFormatter`` class handles text-to-HTML conversion by using
"filter functions"; by default, three filters are available:
``markdown``, ``textile`` and ``restructuredtext``, which apply those
systems. You can, however, enable the use of any system you like by
writing a new filter function and registering it with an instance of
``MarkupFormatter``.

In the simplest case, converting text to HTML works by creating an
instance of ``MarkupFormatter``, then calling it, passing a string and
the name of a filter to use::

    from template_utils.markup import MarkupFormatter
    formatter = MarkupFormatter()
    my_string = """Lorem ipsum dolor sit amet.
    
    Consectetuer adipiscing elit."""
    my_html = formatter(my_string, filter_name='markdown')

You can also pass arbitrary keyword arguments, and they will be handed
off to the filter function; for example, to use Markdown's "safe mode"
(which strips raw HTML before processing), you could call it like
this::

    my_html = formatter(my_string, filter_name='markdown', safe_mode=True)

The ``safe_mode=True`` argument will be handed directly to the
``markdown`` filter.

To add a new filter, simply define it as a function; for example, to
achieve the same effect as Django's built-in ``escape`` and
``linebreaks`` template filters, you could define this function::

    def escape_linebreaks(text, **kwargs):
        from django.utils.html import escape, linebreaks
        return linebreaks(escape(text))

Filter functions must accept the string of text to convert as their
first postional argument, and should accept ``**kwargs`` even if they
don't do anything with it.

To register the filter, simply call the ``register`` method of an
instance of ``MarkupFormatter``::

    formatter.register('escape_linebreaks', escape_linebreaks)

The arguments to ``register`` are:

1. A string to use as the name of the filter.

2. The filter function.

Once the filter is registered, it can be used the same as the built-in
filters::

    my_html = formatter(my_string, filter_name='escape_linebreaks')

**Note:** ``template_utils.markup`` creates an instance of
``MarkupFormatter``, called ``formatter``; unless you have a need for
multiple instances of ``MarkupFormatter``, it's probably best to
import and use that instance (by doing ``from template_utils.markup
import formatter``). This allows the single instance to be used in
multiple places, and avoids the need to register any custom filter
functions with multiple instances.


Specifying default behavior in a Django setting
===============================================


To cut down on repetitive typing, ``MarkupFormatter`` can read a
default filter name and keyword arguments from the Django setting
``MARKUP_FILTER``; if you specify this setting, it should be a tuple
with two elements:

1. The name of a filter to use.

2. A dictionary of keyword arguments to pass to it.

So to have Markdown with "safe mode" be the default behavior, you
would add the following to your settings file::

    MARKUP_FILTER = ('markdown', { 'safe_mode': True })

When you've specified ``MARKUP_FILTER`` in this way, you can perform
text-to-html conversion just by passing in the string to convert, with
no other arguments::

    my_html = formatter(my_string)

The filter function specified in ``MARKUP_FILTER`` does not have to be
in the default filter set; so long as you register a filter of the
correct name before trying to do any text conversion, it will work.

To have the default behavior apply no conversion at all, specify
`None` as the filter name and an empty dictionary of keyword
arguments::

    MARKUP_FILTER = (None, {})

**Note:** if you pass the ``filter_name`` keyword argument when
performing text-to-HTML conversion, ``MarkupFormatter`` will
completely ignore the ``MARKUP_FILTER`` setting (it won't even try to
import the setting), so be sure to explicitly pass any keyword
arguments you want to use. ``MarkupFormatter`` behaves this way so
that it can be used stand-alone, without the need to configure or even
install Django.


Applying text-to-HTML conversion in templates
=============================================

A filter library called ``generic_markup`` is also included here, and
it contains the filter ``apply_markup``, which imports
``template_utils.markup.formatter`` and applies it to the text the
filter is attached to::

    {% load generic_markup %}
    {{ some_text|apply_markup %}

By itself, ``apply_markup`` will use the default behavior specified in
``MARKUP_FILTER``, but it does accept a single argument which allows
you to override the filter name::

    {{ some_text|apply_markup:"textile" }}

This would have the same effect as the following Python code::

    from template_utils.markup import formatter
    print formatter(some_text, filter_name='textile')

But remember that this will override *all* use of ``MARKUP_FILTER``,
so any custom keyword arguments you specified there will not be used
when you pass an argument to ``apply_markup``.

Also, note that on Django trunk templates automatically escape
variable output by default; the ``apply_markup`` filter will mark its
output as "safe" in order to avoid escaping of the generated HTML.