"Default variable filters" from django.template import resolve_variable, Library from django.conf import settings from django.utils.translation import gettext, ngettext import re import random as random_module register = Library() ####################### # STRING DECORATOR # ####################### def smart_string(obj): # FUTURE: Unicode strings should probably be normalized to a specific # encoding and non-unicode strings should be converted to unicode too. # if isinstance(obj, unicode): # obj = obj.encode(settings.DEFAULT_CHARSET) # else: # obj = unicode(obj, settings.DEFAULT_CHARSET) # FUTURE: Replace dumb string logic below with cool unicode logic above. if not isinstance(obj, basestring): obj = str(obj) return obj def stringfilter(func): """ Decorator for filters which should only receive strings. The object passed as the first positional argument will be converted to a string. """ def _dec(*args, **kwargs): if args: args = list(args) args[0] = smart_string(args[0]) return func(*args, **kwargs) # Include a reference to the real function (used to check original # arguments by the template parser). _dec._decorated_function = getattr(func, '_decorated_function', func) return _dec ################### # STRINGS # ################### def addslashes(value): "Adds slashes - useful for passing strings to JavaScript, for example." return value.replace('\\', '\\\\').replace('"', '\\"').replace("'", "\\'") addslashes = stringfilter(addslashes) def capfirst(value): "Capitalizes the first character of the value" return value and value[0].upper() + value[1:] capfirst = stringfilter(capfirst) def fix_ampersands(value): "Replaces ampersands with ``&`` entities" from django.utils.html import fix_ampersands return fix_ampersands(value) fix_ampersands = stringfilter(fix_ampersands) def floatformat(text, arg=-1): """ If called without an argument, displays a floating point number as 34.2 -- but only if there's a point to be displayed. With a positive numeric argument, it displays that many decimal places always. With a negative numeric argument, it will display that many decimal places -- but only if there's places to be displayed. Examples: * num1 = 34.23234 * num2 = 34.00000 * num1|floatformat results in 34.2 * num2|floatformat is 34 * num1|floatformat:3 is 34.232 * num2|floatformat:3 is 34.000 * num1|floatformat:-3 is 34.232 * num2|floatformat:-3 is 34 """ try: f = float(text) except ValueError: return '' try: d = int(arg) except ValueError: return smart_string(f) m = f - int(f) if not m and d < 0: return '%d' % int(f) else: formatstr = '%%.%df' % abs(d) return formatstr % f def linenumbers(value): "Displays text with line numbers" from django.utils.html import escape lines = value.split('\n') # Find the maximum width of the line count, for use with zero padding string format command width = str(len(str(len(lines)))) for i, line in enumerate(lines): lines[i] = ("%0" + width + "d. %s") % (i + 1, escape(line)) return '\n'.join(lines) linenumbers = stringfilter(linenumbers) def lower(value): "Converts a string into all lowercase" return value.lower() lower = stringfilter(lower) def make_list(value): """ Returns the value turned into a list. For an integer, it's a list of digits. For a string, it's a list of characters. """ return list(value) make_list = stringfilter(make_list) def slugify(value): "Converts to lowercase, removes non-alpha chars and converts spaces to hyphens" value = re.sub('[^\w\s-]', '', value).strip().lower() return re.sub('[-\s]+', '-', value) slugify = stringfilter(slugify) def stringformat(value, arg): """ Formats the variable according to the argument, a string formatting specifier. This specifier uses Python string formating syntax, with the exception that the leading "%" is dropped. See http://docs.python.org/lib/typesseq-strings.html for documentation of Python string formatting """ try: return ("%" + str(arg)) % value except (ValueError, TypeError): return "" def title(value): "Converts a string into titlecase" return re.sub("([a-z])'([A-Z])", lambda m: m.group(0).lower(), value.title()) title = stringfilter(title) def truncatewords(value, arg): """ Truncates a string after a certain number of words Argument: Number of words to truncate after """ from django.utils.text import truncate_words try: length = int(arg) except ValueError: # invalid literal for int() return value # Fail silently. if not isinstance(value, basestring): value = str(value) return truncate_words(value, length) truncatewords = stringfilter(truncatewords) def truncatewords_html(value, arg): """ Truncates HTML after a certain number of words Argument: Number of words to truncate after """ from django.utils.text import truncate_html_words try: length = int(arg) except ValueError: # invalid literal for int() return value # Fail silently. if not isinstance(value, basestring): value = str(value) return truncate_html_words(value, length) truncatewords_html = stringfilter(truncatewords_html) def upper(value): "Converts a string into all uppercase" return value.upper() upper = stringfilter(upper) def urlencode(value): "Escapes a value for use in a URL" import urllib if not isinstance(value, basestring): value = str(value) return urllib.quote(value) urlencode = stringfilter(urlencode) def urlize(value): "Converts URLs in plain text into clickable links" from django.utils.html import urlize return urlize(value, nofollow=True) urlize = stringfilter(urlize) def urlizetrunc(value, limit): """ Converts URLs into clickable links, truncating URLs to the given character limit, and adding 'rel=nofollow' attribute to discourage spamming. Argument: Length to truncate URLs to. """ from django.utils.html import urlize return urlize(value, trim_url_limit=int(limit), nofollow=True) urlizetrunc = stringfilter(urlizetrunc) def wordcount(value): "Returns the number of words" return len(value.split()) wordcount = stringfilter(wordcount) def wordwrap(value, arg): """ Wraps words at specified line length Argument: number of characters to wrap the text at. """ from django.utils.text import wrap return wrap(value, int(arg)) wordwrap = stringfilter(wordwrap) def ljust(value, arg): """ Left-aligns the value in a field of a given width Argument: field size """ return value.ljust(int(arg)) ljust = stringfilter(ljust) def rjust(value, arg): """ Right-aligns the value in a field of a given width Argument: field size """ return value.rjust(int(arg)) rjust = stringfilter(rjust) def center(value, arg): "Centers the value in a field of a given width" return value.center(int(arg)) center = stringfilter(center) def cut(value, arg): "Removes all values of arg from the given string" return value.replace(arg, '') cut = stringfilter(cut) ################### # HTML STRINGS # ################### def escape(value): "Escapes a string's HTML" from django.utils.html import escape return escape(value) escape = stringfilter(escape) def linebreaks(value): "Converts newlines into
and
s"
from django.utils.html import linebreaks
return linebreaks(value)
linebreaks = stringfilter(linebreaks)
def linebreaksbr(value):
"Converts newlines into
s"
return value.replace('\n', '
')
linebreaksbr = stringfilter(linebreaksbr)
def removetags(value, tags):
"Removes a space separated list of [X]HTML tags from the output"
tags = [re.escape(tag) for tag in tags.split()]
tags_re = '(%s)' % '|'.join(tags)
starttag_re = re.compile(r'<%s(/?>|(\s+[^>]*>))' % tags_re)
endtag_re = re.compile('%s>' % tags_re)
value = starttag_re.sub('', value)
value = endtag_re.sub('', value)
return value
removetags = stringfilter(removetags)
def striptags(value):
"Strips all [X]HTML tags"
from django.utils.html import strip_tags
return strip_tags(value)
striptags = stringfilter(striptags)
###################
# LISTS #
###################
def dictsort(value, arg):
"""
Takes a list of dicts, returns that list sorted by the property given in
the argument.
"""
decorated = [(resolve_variable('var.' + arg, {'var' : item}), item) for item in value]
decorated.sort()
return [item[1] for item in decorated]
def dictsortreversed(value, arg):
"""
Takes a list of dicts, returns that list sorted in reverse order by the
property given in the argument.
"""
decorated = [(resolve_variable('var.' + arg, {'var' : item}), item) for item in value]
decorated.sort()
decorated.reverse()
return [item[1] for item in decorated]
def first(value):
"Returns the first item in a list"
try:
return value[0]
except IndexError:
return ''
def join(value, arg):
"Joins a list with a string, like Python's ``str.join(list)``"
try:
return arg.join(map(smart_string, value))
except AttributeError: # fail silently but nicely
return value
def length(value):
"Returns the length of the value - useful for lists"
return len(value)
def length_is(value, arg):
"Returns a boolean of whether the value's length is the argument"
return len(value) == int(arg)
def random(value):
"Returns a random item from the list"
return random_module.choice(value)
def slice_(value, arg):
"""
Returns a slice of the list.
Uses the same syntax as Python's list slicing; see
http://diveintopython.org/native_data_types/lists.html#odbchelper.list.slice
for an introduction.
"""
try:
bits = []
for x in arg.split(':'):
if len(x) == 0:
bits.append(None)
else:
bits.append(int(x))
return value[slice(*bits)]
except (ValueError, TypeError):
return value # Fail silently.
def unordered_list(value):
"""
Recursively takes a self-nested list and returns an HTML unordered list --
WITHOUT opening and closing