diff -r 8d941af65caf -r 77b6da96e6f1 web/lib/django/template/defaultfilters.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/web/lib/django/template/defaultfilters.py Wed Jun 02 18:57:35 2010 +0200 @@ -0,0 +1,930 @@ +"""Default variable filters.""" + +import re +from decimal import Decimal, InvalidOperation, ROUND_HALF_UP +import random as random_module +try: + from functools import wraps +except ImportError: + from django.utils.functional import wraps # Python 2.4 fallback. + +from django.template import Variable, Library +from django.conf import settings +from django.utils import formats +from django.utils.translation import ugettext, ungettext +from django.utils.encoding import force_unicode, iri_to_uri +from django.utils.safestring import mark_safe, SafeData + +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]) + if isinstance(args[0], SafeData) and getattr(func, 'is_safe', False): + return mark_safe(func(*args, **kwargs)) + 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) + for attr in ('is_safe', 'needs_autoescape'): + if hasattr(func, attr): + setattr(_dec, attr, getattr(func, attr)) + return wraps(func)(_dec) + +################### +# STRINGS # +################### + +def addslashes(value): + """ + Adds slashes before quotes. Useful for escaping strings in CSV, for + example. Less useful for escaping JavaScript; use the ``escapejs`` + filter instead. + """ + return value.replace('\\', '\\\\').replace('"', '\\"').replace("'", "\\'") +addslashes.is_safe = True +addslashes = stringfilter(addslashes) + +def capfirst(value): + """Capitalizes the first character of the value.""" + return value and value[0].upper() + value[1:] +capfirst.is_safe=True +capfirst = stringfilter(capfirst) + +_base_js_escapes = ( + ('\\', r'\u005C'), + ('\'', r'\u0027'), + ('"', r'\u0022'), + ('>', r'\u003E'), + ('<', r'\u003C'), + ('&', r'\u0026'), + ('=', r'\u003D'), + ('-', r'\u002D'), + (';', r'\u003B'), + (u'\u2028', r'\u2028'), + (u'\u2029', r'\u2029') +) + +# Escape every ASCII character with a value less than 32. +_js_escapes = (_base_js_escapes + + tuple([('%c' % z, '\\u%04X' % z) for z in range(32)])) + +def escapejs(value): + """Hex encodes characters for use in JavaScript strings.""" + for bad, good in _js_escapes: + value = value.replace(bad, good) + return value +escapejs = stringfilter(escapejs) + +def fix_ampersands(value): + """Replaces ampersands with ``&`` entities.""" + from django.utils.html import fix_ampersands + return fix_ampersands(value) +fix_ampersands.is_safe=True +fix_ampersands = stringfilter(fix_ampersands) + +# Values for testing floatformat input against infinity and NaN representations, +# which differ across platforms and Python versions. Some (i.e. old Windows +# ones) are not recognized by Decimal but we want to return them unchanged vs. +# returning an empty string as we do for completley invalid input. Note these +# need to be built up from values that are not inf/nan, since inf/nan values do +# not reload properly from .pyc files on Windows prior to some level of Python 2.5 +# (see Python Issue757815 and Issue1080440). +pos_inf = 1e200 * 1e200 +neg_inf = -1e200 * 1e200 +nan = (1e200 * 1e200) / (1e200 * 1e200) +special_floats = [str(pos_inf), str(neg_inf), str(nan)] + +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" + + If the input float is infinity or NaN, the (platform-dependent) string + representation of that value will be displayed. + """ + + try: + input_val = force_unicode(text) + d = Decimal(input_val) + except UnicodeEncodeError: + return u'' + except InvalidOperation: + if input_val in special_floats: + return input_val + try: + d = Decimal(force_unicode(float(text))) + except (ValueError, InvalidOperation, TypeError, UnicodeEncodeError): + return u'' + try: + p = int(arg) + except ValueError: + return input_val + + try: + m = int(d) - d + except (ValueError, OverflowError, InvalidOperation): + return input_val + + if not m and p < 0: + return mark_safe(formats.number_format(u'%d' % (int(d)), 0)) + + if p == 0: + exp = Decimal(1) + else: + exp = Decimal('1.0') / (Decimal(10) ** abs(p)) + try: + return mark_safe(formats.number_format(u'%s' % str(d.quantize(exp, ROUND_HALF_UP)), abs(p))) + except InvalidOperation: + return input_val +floatformat.is_safe = True + +def iriencode(value): + """Escapes an IRI value for use in a URL.""" + return force_unicode(iri_to_uri(value)) +iriencode.is_safe = True +iriencode = stringfilter(iriencode) + +def linenumbers(value, autoescape=None): + """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)))) + if not autoescape or isinstance(value, SafeData): + for i, line in enumerate(lines): + lines[i] = (u"%0" + width + u"d. %s") % (i + 1, line) + else: + for i, line in enumerate(lines): + lines[i] = (u"%0" + width + u"d. %s") % (i + 1, escape(line)) + return mark_safe(u'\n'.join(lines)) +linenumbers.is_safe = True +linenumbers.needs_autoescape = True +linenumbers = stringfilter(linenumbers) + +def lower(value): + """Converts a string into all lowercase.""" + return value.lower() +lower.is_safe = True +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.is_safe = False +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 mark_safe(re.sub('[-\s]+', '-', value)) +slugify.is_safe = True +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"" +stringformat.is_safe = True + +def title(value): + """Converts a string into titlecase.""" + t = re.sub("([a-z])'([A-Z])", lambda m: m.group(0).lower(), value.title()) + return re.sub("\d([A-Z])", lambda m: m.group(0).lower(), t) +title.is_safe = True +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.is_safe = True +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.is_safe = True +truncatewords_html = stringfilter(truncatewords_html) + +def upper(value): + """Converts a string into all uppercase.""" + return value.upper() +upper.is_safe = False +upper = stringfilter(upper) + +def urlencode(value): + """Escapes a value for use in a URL.""" + from django.utils.http import urlquote + return urlquote(value) +urlencode.is_safe = False +urlencode = stringfilter(urlencode) + +def urlize(value, autoescape=None): + """Converts URLs in plain text into clickable links.""" + from django.utils.html import urlize + return mark_safe(urlize(value, nofollow=True, autoescape=autoescape)) +urlize.is_safe=True +urlize.needs_autoescape = True +urlize = stringfilter(urlize) + +def urlizetrunc(value, limit, autoescape=None): + """ + 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 mark_safe(urlize(value, trim_url_limit=int(limit), nofollow=True, + autoescape=autoescape)) +urlizetrunc.is_safe = True +urlizetrunc.needs_autoescape = True +urlizetrunc = stringfilter(urlizetrunc) + +def wordcount(value): + """Returns the number of words.""" + return len(value.split()) +wordcount.is_safe = False +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.is_safe = True +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.is_safe = True +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.is_safe = True +rjust = stringfilter(rjust) + +def center(value, arg): + """Centers the value in a field of a given width.""" + return value.center(int(arg)) +center.is_safe = True +center = stringfilter(center) + +def cut(value, arg): + """ + Removes all values of arg from the given string. + """ + safe = isinstance(value, SafeData) + value = value.replace(arg, u'') + if safe and arg != ';': + return mark_safe(value) + return value +cut = stringfilter(cut) + +################### +# HTML STRINGS # +################### + +def escape(value): + """ + Marks the value as a string that should not be auto-escaped. + """ + from django.utils.safestring import mark_for_escaping + return mark_for_escaping(value) +escape.is_safe = True +escape = stringfilter(escape) + +def force_escape(value): + """ + Escapes a string's HTML. This returns a new string containing the escaped + characters (as opposed to "escape", which marks the content for later + possible escaping). + """ + from django.utils.html import escape + return mark_safe(escape(value)) +force_escape = stringfilter(force_escape) +force_escape.is_safe = True + +def linebreaks(value, autoescape=None): + """ + 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 + autoescape = autoescape and not isinstance(value, SafeData) + return mark_safe(linebreaks(value, autoescape)) +linebreaks.is_safe = True +linebreaks.needs_autoescape = True +linebreaks = stringfilter(linebreaks) + +def linebreaksbr(value, autoescape=None): + """ + Converts all newlines in a piece of plain text to HTML line breaks + (``
``). + """ + if autoescape and not isinstance(value, SafeData): + from django.utils.html import escape + value = escape(value) + return mark_safe(value.replace('\n', '
')) +linebreaksbr.is_safe = True +linebreaksbr.needs_autoescape = True +linebreaksbr = stringfilter(linebreaksbr) + +def safe(value): + """ + Marks the value as a string that should not be auto-escaped. + """ + return mark_safe(value) +safe.is_safe = True +safe = stringfilter(safe) + +def safeseq(value): + """ + A "safe" filter for sequences. Marks each element in the sequence, + individually, as safe, after converting them to unicode. Returns a list + with the results. + """ + return [mark_safe(force_unicode(obj)) for obj in value] +safeseq.is_safe = True + +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.is_safe = True +removetags = stringfilter(removetags) + +def striptags(value): + """Strips all [X]HTML tags.""" + from django.utils.html import strip_tags + return strip_tags(value) +striptags.is_safe = True +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] +dictsort.is_safe = False + +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] +dictsortreversed.is_safe = False + +def first(value): + """Returns the first item in a list.""" + try: + return value[0] + except IndexError: + return u'' +first.is_safe = False + +def join(value, arg, autoescape=None): + """ + Joins a list with a string, like Python's ``str.join(list)``. + """ + value = map(force_unicode, value) + if autoescape: + from django.utils.html import conditional_escape + value = [conditional_escape(v) for v in value] + try: + data = arg.join(value) + except AttributeError: # fail silently but nicely + return value + return mark_safe(data) +join.is_safe = True +join.needs_autoescape = True + +def last(value): + "Returns the last item in a list" + try: + return value[-1] + except IndexError: + return u'' +last.is_safe = True + +def length(value): + """Returns the length of the value - useful for lists.""" + try: + return len(value) + except (ValueError, TypeError): + return '' +length.is_safe = True + +def length_is(value, arg): + """Returns a boolean of whether the value's length is the argument.""" + try: + return len(value) == int(arg) + except (ValueError, TypeError): + return '' +length_is.is_safe = False + +def random(value): + """Returns a random item from the list.""" + return random_module.choice(value) +random.is_safe = True + +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. +slice_.is_safe = True + +def unordered_list(value, autoescape=None): + """ + Recursively takes a self-nested list and returns an HTML unordered list -- + WITHOUT opening and closing