"""Default variable filters.""" import re import random as random_module from django.template import Variable, Library from django.conf import settings from django.utils.translation import ugettext, ungettext from django.utils.encoding import force_unicode, iri_to_uri register = Library() ####################### # STRING DECORATOR # ####################### def stringfilter(func): """ Decorator for filters which should only receive unicode objects. The object passed as the first positional argument will be converted to a unicode object. """ def _dec(*args, **kwargs): if args: args = list(args) args[0] = force_unicode(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): """ Displays a float to a specified number of decimal places. If called without an argument, it displays the floating point number with one decimal place -- but only if there's a decimal place to be displayed: * num1 = 34.23234 * num2 = 34.00000 * num3 = 34.26000 * {{ num1|floatformat }} displays "34.2" * {{ num2|floatformat }} displays "34" * {{ num3|floatformat }} displays "34.3" If arg is positive, it will always display exactly arg number of decimal places: * {{ num1|floatformat:3 }} displays "34.232" * {{ num2|floatformat:3 }} displays "34.000" * {{ num3|floatformat:3 }} displays "34.260" If arg is negative, it will display arg number of decimal places -- but only if there are places to be displayed: * {{ num1|floatformat:"-3" }} displays "34.232" * {{ num2|floatformat:"-3" }} displays "34" * {{ num3|floatformat:"-3" }} displays "34.260" """ try: f = float(text) except ValueError: return u'' try: d = int(arg) except ValueError: return force_unicode(f) m = f - int(f) if not m and d < 0: return u'%d' % int(f) else: formatstr = u'%%.%df' % abs(d) return formatstr % f def iriencode(value): """Escapes an IRI value for use in a URL.""" return force_unicode(iri_to_uri(value)) iriencode = stringfilter(iriencode) def linenumbers(value): """Displays text with line numbers.""" from django.utils.html import escape lines = value.split(u'\n') # Find the maximum width of the line count, for use with zero padding # string format command. width = unicode(len(unicode(len(lines)))) for i, line in enumerate(lines): lines[i] = (u"%0" + width + u"d. %s") % (i + 1, escape(line)) return u'\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): """ Normalizes string, converts to lowercase, removes non-alpha characters, and converts spaces to hyphens. """ import unicodedata value = unicodedata.normalize('NFKD', value).encode('ascii', 'ignore') value = unicode(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 arg, 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 (u"%" + unicode(arg)) % value except (ValueError, TypeError): return u"" 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. 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. 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.""" from django.utils.http import urlquote return urlquote(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, u'') 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): """ Replaces line breaks in plain text with appropriate HTML; a single newline becomes an HTML line break (``
``) and a new line followed by a blank line becomes a paragraph break (``

``). """ from django.utils.html import linebreaks return linebreaks(value) linebreaks = stringfilter(linebreaks) def linebreaksbr(value): """ Converts all newlines in a piece of plain text to HTML line breaks (``
``). """ 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 = u'(%s)' % u'|'.join(tags) starttag_re = re.compile(ur'<%s(/?>|(\s+[^>]*>))' % tags_re, re.U) endtag_re = re.compile(u'' % tags_re) value = starttag_re.sub(u'', value) value = endtag_re.sub(u'', 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. """ var_resolve = Variable(arg).resolve decorated = [(var_resolve(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. """ var_resolve = Variable(arg).resolve decorated = [(var_resolve(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 u'' def join(value, arg): """Joins a list with a string, like Python's ``str.join(list)``.""" try: return arg.join(map(force_unicode, 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(u':'): 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