2007-11-04 10:05:56 +08:00
|
|
|
"""Default variable filters."""
|
2012-06-08 00:08:47 +08:00
|
|
|
from __future__ import unicode_literals
|
2007-11-04 10:05:56 +08:00
|
|
|
|
|
|
|
import random as random_module
|
2015-01-28 20:35:27 +08:00
|
|
|
import re
|
2016-05-11 00:46:47 +08:00
|
|
|
import warnings
|
2015-01-28 20:35:27 +08:00
|
|
|
from decimal import ROUND_HALF_UP, Context, Decimal, InvalidOperation
|
2011-03-28 10:11:19 +08:00
|
|
|
from functools import wraps
|
2015-11-05 18:59:56 +08:00
|
|
|
from operator import itemgetter
|
2011-05-27 12:22:37 +08:00
|
|
|
from pprint import pformat
|
2005-07-13 09:25:57 +08:00
|
|
|
|
2015-01-28 20:35:27 +08:00
|
|
|
from django.utils import formats, six
|
2011-05-28 01:05:16 +08:00
|
|
|
from django.utils.dateformat import format, time_format
|
2016-05-11 00:46:47 +08:00
|
|
|
from django.utils.deprecation import RemovedInDjango20Warning
|
2012-07-21 16:00:10 +08:00
|
|
|
from django.utils.encoding import force_text, iri_to_uri
|
2015-01-28 20:35:27 +08:00
|
|
|
from django.utils.html import (
|
|
|
|
avoid_wrapping, conditional_escape, escape, escapejs, linebreaks,
|
2015-08-18 22:09:17 +08:00
|
|
|
strip_tags, urlize as _urlize,
|
2015-01-28 20:35:27 +08:00
|
|
|
)
|
2011-05-27 12:22:37 +08:00
|
|
|
from django.utils.http import urlquote
|
2015-01-28 20:35:27 +08:00
|
|
|
from django.utils.safestring import SafeData, mark_for_escaping, mark_safe
|
|
|
|
from django.utils.text import (
|
|
|
|
Truncator, normalize_newlines, phone2numeric, slugify as _slugify, wrap,
|
|
|
|
)
|
2011-05-27 12:22:37 +08:00
|
|
|
from django.utils.timesince import timesince, timeuntil
|
2010-08-03 23:42:39 +08:00
|
|
|
from django.utils.translation import ugettext, ungettext
|
2005-07-13 09:25:57 +08:00
|
|
|
|
2015-05-09 04:10:36 +08:00
|
|
|
from .base import Variable, VariableDoesNotExist
|
|
|
|
from .library import Library
|
2015-04-25 03:35:30 +08:00
|
|
|
|
2005-11-27 06:46:31 +08:00
|
|
|
register = Library()
|
|
|
|
|
2013-11-03 04:12:09 +08:00
|
|
|
|
2007-02-24 02:02:51 +08:00
|
|
|
#######################
|
|
|
|
# STRING DECORATOR #
|
|
|
|
#######################
|
|
|
|
|
|
|
|
def stringfilter(func):
|
|
|
|
"""
|
Merged Unicode branch into trunk (r4952:5608). This should be fully
backwards compatible for all practical purposes.
Fixed #2391, #2489, #2996, #3322, #3344, #3370, #3406, #3432, #3454, #3492, #3582, #3690, #3878, #3891, #3937, #4039, #4141, #4227, #4286, #4291, #4300, #4452, #4702
git-svn-id: http://code.djangoproject.com/svn/django/trunk@5609 bcc190cf-cafb-0310-a4f2-bffc1f526a37
2007-07-04 20:11:04 +08:00
|
|
|
Decorator for filters which should only receive unicode objects. The object
|
|
|
|
passed as the first positional argument will be converted to a unicode
|
|
|
|
object.
|
2007-02-24 02:02:51 +08:00
|
|
|
"""
|
|
|
|
def _dec(*args, **kwargs):
|
|
|
|
if args:
|
|
|
|
args = list(args)
|
2012-07-21 16:00:10 +08:00
|
|
|
args[0] = force_text(args[0])
|
2011-10-30 15:32:21 +08:00
|
|
|
if (isinstance(args[0], SafeData) and
|
2013-11-26 17:43:46 +08:00
|
|
|
getattr(_dec._decorated_function, 'is_safe', False)):
|
2007-11-29 09:44:30 +08:00
|
|
|
return mark_safe(func(*args, **kwargs))
|
2007-02-24 02:02:51 +08:00
|
|
|
return func(*args, **kwargs)
|
Merged Unicode branch into trunk (r4952:5608). This should be fully
backwards compatible for all practical purposes.
Fixed #2391, #2489, #2996, #3322, #3344, #3370, #3406, #3432, #3454, #3492, #3582, #3690, #3878, #3891, #3937, #4039, #4141, #4227, #4286, #4291, #4300, #4452, #4702
git-svn-id: http://code.djangoproject.com/svn/django/trunk@5609 bcc190cf-cafb-0310-a4f2-bffc1f526a37
2007-07-04 20:11:04 +08:00
|
|
|
|
2007-02-24 02:02:51 +08:00
|
|
|
# Include a reference to the real function (used to check original
|
2011-10-30 15:32:21 +08:00
|
|
|
# arguments by the template parser, and to bear the 'is_safe' attribute
|
|
|
|
# when multiple decorators are applied).
|
2007-02-24 02:02:51 +08:00
|
|
|
_dec._decorated_function = getattr(func, '_decorated_function', func)
|
2011-10-30 15:32:21 +08:00
|
|
|
|
2008-02-25 14:02:35 +08:00
|
|
|
return wraps(func)(_dec)
|
2007-02-24 02:02:51 +08:00
|
|
|
|
2011-10-30 15:32:21 +08:00
|
|
|
|
2005-07-13 09:25:57 +08:00
|
|
|
###################
|
|
|
|
# STRINGS #
|
|
|
|
###################
|
|
|
|
|
2011-10-30 15:32:21 +08:00
|
|
|
@register.filter(is_safe=True)
|
2011-10-29 22:39:02 +08:00
|
|
|
@stringfilter
|
2005-11-27 06:46:31 +08:00
|
|
|
def addslashes(value):
|
2007-12-05 05:08:29 +08:00
|
|
|
"""
|
|
|
|
Adds slashes before quotes. Useful for escaping strings in CSV, for
|
|
|
|
example. Less useful for escaping JavaScript; use the ``escapejs``
|
|
|
|
filter instead.
|
|
|
|
"""
|
2006-09-23 16:41:09 +08:00
|
|
|
return value.replace('\\', '\\\\').replace('"', '\\"').replace("'", "\\'")
|
2005-07-13 09:25:57 +08:00
|
|
|
|
2013-11-03 08:37:15 +08:00
|
|
|
|
2011-10-30 15:32:21 +08:00
|
|
|
@register.filter(is_safe=True)
|
2011-10-29 22:39:02 +08:00
|
|
|
@stringfilter
|
2005-11-27 06:46:31 +08:00
|
|
|
def capfirst(value):
|
2007-11-04 10:05:56 +08:00
|
|
|
"""Capitalizes the first character of the value."""
|
2005-07-13 09:25:57 +08:00
|
|
|
return value and value[0].upper() + value[1:]
|
Merged Unicode branch into trunk (r4952:5608). This should be fully
backwards compatible for all practical purposes.
Fixed #2391, #2489, #2996, #3322, #3344, #3370, #3406, #3432, #3454, #3492, #3582, #3690, #3878, #3891, #3937, #4039, #4141, #4227, #4286, #4291, #4300, #4452, #4702
git-svn-id: http://code.djangoproject.com/svn/django/trunk@5609 bcc190cf-cafb-0310-a4f2-bffc1f526a37
2007-07-04 20:11:04 +08:00
|
|
|
|
2013-11-03 08:37:15 +08:00
|
|
|
|
2011-05-27 12:22:37 +08:00
|
|
|
@register.filter("escapejs")
|
|
|
|
@stringfilter
|
|
|
|
def escapejs_filter(value):
|
2008-08-26 15:56:32 +08:00
|
|
|
"""Hex encodes characters for use in JavaScript strings."""
|
2011-01-03 01:34:52 +08:00
|
|
|
return escapejs(value)
|
2007-12-05 05:08:29 +08:00
|
|
|
|
2013-11-03 08:37:15 +08:00
|
|
|
|
2008-11-08 09:44:46 +08:00
|
|
|
# Values for testing floatformat input against infinity and NaN representations,
|
|
|
|
# which differ across platforms and Python versions. Some (i.e. old Windows
|
2008-11-15 09:16:20 +08:00
|
|
|
# ones) are not recognized by Decimal but we want to return them unchanged vs.
|
2012-10-07 05:40:58 +08:00
|
|
|
# returning an empty string as we do for completely invalid input. Note these
|
2008-11-15 09:16:20 +08:00
|
|
|
# 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
|
2008-11-08 09:44:46 +08:00
|
|
|
# (see Python Issue757815 and Issue1080440).
|
|
|
|
pos_inf = 1e200 * 1e200
|
|
|
|
neg_inf = -1e200 * 1e200
|
2011-08-22 16:06:52 +08:00
|
|
|
nan = (1e200 * 1e200) // (1e200 * 1e200)
|
2008-11-08 09:44:46 +08:00
|
|
|
special_floats = [str(pos_inf), str(neg_inf), str(nan)]
|
|
|
|
|
2013-11-03 08:37:15 +08:00
|
|
|
|
2011-10-30 15:32:21 +08:00
|
|
|
@register.filter(is_safe=True)
|
2007-01-03 13:29:34 +08:00
|
|
|
def floatformat(text, arg=-1):
|
2005-07-13 09:25:57 +08:00
|
|
|
"""
|
2007-11-04 10:27:17 +08:00
|
|
|
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:
|
2007-03-10 16:11:28 +08:00
|
|
|
|
|
|
|
* num1 = 34.23234
|
|
|
|
* num2 = 34.00000
|
2007-11-04 10:27:17 +08:00
|
|
|
* 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"
|
2008-11-15 09:16:20 +08:00
|
|
|
|
2008-11-08 09:44:46 +08:00
|
|
|
If the input float is infinity or NaN, the (platform-dependent) string
|
|
|
|
representation of that value will be displayed.
|
2005-07-13 09:25:57 +08:00
|
|
|
"""
|
2008-11-15 09:16:20 +08:00
|
|
|
|
2005-11-03 03:01:27 +08:00
|
|
|
try:
|
2012-07-21 16:00:10 +08:00
|
|
|
input_val = force_text(text)
|
2008-11-08 09:44:46 +08:00
|
|
|
d = Decimal(input_val)
|
|
|
|
except UnicodeEncodeError:
|
2012-06-08 00:08:47 +08:00
|
|
|
return ''
|
2008-11-08 09:44:46 +08:00
|
|
|
except InvalidOperation:
|
|
|
|
if input_val in special_floats:
|
|
|
|
return input_val
|
2009-04-01 02:17:21 +08:00
|
|
|
try:
|
2012-07-21 16:00:10 +08:00
|
|
|
d = Decimal(force_text(float(text)))
|
2009-04-01 02:17:21 +08:00
|
|
|
except (ValueError, InvalidOperation, TypeError, UnicodeEncodeError):
|
2012-06-08 00:08:47 +08:00
|
|
|
return ''
|
2007-01-03 13:29:34 +08:00
|
|
|
try:
|
2008-11-08 09:44:46 +08:00
|
|
|
p = int(arg)
|
2007-01-03 13:29:34 +08:00
|
|
|
except ValueError:
|
2008-11-08 09:44:46 +08:00
|
|
|
return input_val
|
2008-11-15 09:16:20 +08:00
|
|
|
|
2008-03-20 15:36:33 +08:00
|
|
|
try:
|
2008-11-08 09:44:46 +08:00
|
|
|
m = int(d) - d
|
2009-10-13 00:53:23 +08:00
|
|
|
except (ValueError, OverflowError, InvalidOperation):
|
2008-11-08 09:44:46 +08:00
|
|
|
return input_val
|
2008-11-15 09:16:20 +08:00
|
|
|
|
2008-11-08 09:44:46 +08:00
|
|
|
if not m and p < 0:
|
2012-06-08 00:08:47 +08:00
|
|
|
return mark_safe(formats.number_format('%d' % (int(d)), 0))
|
2008-11-15 09:16:20 +08:00
|
|
|
|
2008-11-08 09:44:46 +08:00
|
|
|
if p == 0:
|
|
|
|
exp = Decimal(1)
|
2007-01-03 13:29:34 +08:00
|
|
|
else:
|
2012-06-08 00:08:47 +08:00
|
|
|
exp = Decimal('1.0') / (Decimal(10) ** abs(p))
|
2008-11-08 09:44:46 +08:00
|
|
|
try:
|
2011-12-30 22:35:29 +08:00
|
|
|
# Set the precision high enough to avoid an exception, see #15789.
|
|
|
|
tupl = d.as_tuple()
|
|
|
|
units = len(tupl[1]) - tupl[2]
|
2012-01-03 21:09:12 +08:00
|
|
|
prec = abs(p) + units + 1
|
2011-12-30 22:35:29 +08:00
|
|
|
|
2011-03-04 04:56:46 +08:00
|
|
|
# Avoid conversion to scientific notation by accessing `sign`, `digits`
|
|
|
|
# and `exponent` from `Decimal.as_tuple()` directly.
|
2016-03-29 06:33:29 +08:00
|
|
|
sign, digits, exponent = d.quantize(exp, ROUND_HALF_UP, Context(prec=prec)).as_tuple()
|
2012-07-20 20:48:51 +08:00
|
|
|
digits = [six.text_type(digit) for digit in reversed(digits)]
|
2011-03-04 04:56:46 +08:00
|
|
|
while len(digits) <= abs(exponent):
|
2012-06-08 00:08:47 +08:00
|
|
|
digits.append('0')
|
|
|
|
digits.insert(-exponent, '.')
|
2011-03-04 04:56:46 +08:00
|
|
|
if sign:
|
2012-06-08 00:08:47 +08:00
|
|
|
digits.append('-')
|
|
|
|
number = ''.join(reversed(digits))
|
2011-03-04 04:56:46 +08:00
|
|
|
return mark_safe(formats.number_format(number, abs(p)))
|
2008-11-08 09:44:46 +08:00
|
|
|
except InvalidOperation:
|
|
|
|
return input_val
|
2005-07-13 09:25:57 +08:00
|
|
|
|
2013-11-03 08:37:15 +08:00
|
|
|
|
2011-10-30 15:32:21 +08:00
|
|
|
@register.filter(is_safe=True)
|
2011-10-29 22:39:02 +08:00
|
|
|
@stringfilter
|
Merged Unicode branch into trunk (r4952:5608). This should be fully
backwards compatible for all practical purposes.
Fixed #2391, #2489, #2996, #3322, #3344, #3370, #3406, #3432, #3454, #3492, #3582, #3690, #3878, #3891, #3937, #4039, #4141, #4227, #4286, #4291, #4300, #4452, #4702
git-svn-id: http://code.djangoproject.com/svn/django/trunk@5609 bcc190cf-cafb-0310-a4f2-bffc1f526a37
2007-07-04 20:11:04 +08:00
|
|
|
def iriencode(value):
|
2007-11-04 10:05:56 +08:00
|
|
|
"""Escapes an IRI value for use in a URL."""
|
2012-07-21 16:00:10 +08:00
|
|
|
return force_text(iri_to_uri(value))
|
Merged Unicode branch into trunk (r4952:5608). This should be fully
backwards compatible for all practical purposes.
Fixed #2391, #2489, #2996, #3322, #3344, #3370, #3406, #3432, #3454, #3492, #3582, #3690, #3878, #3891, #3937, #4039, #4141, #4227, #4286, #4291, #4300, #4452, #4702
git-svn-id: http://code.djangoproject.com/svn/django/trunk@5609 bcc190cf-cafb-0310-a4f2-bffc1f526a37
2007-07-04 20:11:04 +08:00
|
|
|
|
2013-11-03 08:37:15 +08:00
|
|
|
|
2011-10-30 15:32:21 +08:00
|
|
|
@register.filter(is_safe=True, needs_autoescape=True)
|
2011-10-29 22:39:02 +08:00
|
|
|
@stringfilter
|
2015-03-08 19:34:55 +08:00
|
|
|
def linenumbers(value, autoescape=True):
|
2007-11-04 10:05:56 +08:00
|
|
|
"""Displays text with line numbers."""
|
2012-06-08 00:08:47 +08:00
|
|
|
lines = value.split('\n')
|
2007-11-04 10:05:56 +08:00
|
|
|
# Find the maximum width of the line count, for use with zero padding
|
2007-11-14 20:58:53 +08:00
|
|
|
# string format command
|
2012-07-20 20:48:51 +08:00
|
|
|
width = six.text_type(len(six.text_type(len(lines))))
|
2007-11-14 20:58:53 +08:00
|
|
|
if not autoescape or isinstance(value, SafeData):
|
|
|
|
for i, line in enumerate(lines):
|
2013-10-22 21:31:43 +08:00
|
|
|
lines[i] = ("%0" + width + "d. %s") % (i + 1, line)
|
2007-11-14 20:58:53 +08:00
|
|
|
else:
|
|
|
|
for i, line in enumerate(lines):
|
2013-10-22 21:31:43 +08:00
|
|
|
lines[i] = ("%0" + width + "d. %s") % (i + 1, escape(line))
|
2012-06-08 00:08:47 +08:00
|
|
|
return mark_safe('\n'.join(lines))
|
2005-07-13 09:25:57 +08:00
|
|
|
|
2013-11-03 08:37:15 +08:00
|
|
|
|
2011-10-30 15:32:21 +08:00
|
|
|
@register.filter(is_safe=True)
|
2011-10-29 22:39:02 +08:00
|
|
|
@stringfilter
|
2005-11-27 06:46:31 +08:00
|
|
|
def lower(value):
|
2007-11-04 10:05:56 +08:00
|
|
|
"""Converts a string into all lowercase."""
|
2005-07-13 09:25:57 +08:00
|
|
|
return value.lower()
|
|
|
|
|
2013-11-03 08:37:15 +08:00
|
|
|
|
2011-10-30 15:32:21 +08:00
|
|
|
@register.filter(is_safe=False)
|
2011-10-29 22:39:02 +08:00
|
|
|
@stringfilter
|
2005-11-27 06:46:31 +08:00
|
|
|
def make_list(value):
|
2005-07-13 09:25:57 +08:00
|
|
|
"""
|
2007-11-04 10:05:56 +08:00
|
|
|
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.
|
2005-07-13 09:25:57 +08:00
|
|
|
"""
|
2007-02-24 02:02:51 +08:00
|
|
|
return list(value)
|
2005-07-13 09:25:57 +08:00
|
|
|
|
2013-11-03 08:37:15 +08:00
|
|
|
|
2011-10-30 15:32:21 +08:00
|
|
|
@register.filter(is_safe=True)
|
2011-10-29 22:39:02 +08:00
|
|
|
@stringfilter
|
2005-11-27 06:46:31 +08:00
|
|
|
def slugify(value):
|
Merged Unicode branch into trunk (r4952:5608). This should be fully
backwards compatible for all practical purposes.
Fixed #2391, #2489, #2996, #3322, #3344, #3370, #3406, #3432, #3454, #3492, #3582, #3690, #3878, #3891, #3937, #4039, #4141, #4227, #4286, #4291, #4300, #4452, #4702
git-svn-id: http://code.djangoproject.com/svn/django/trunk@5609 bcc190cf-cafb-0310-a4f2-bffc1f526a37
2007-07-04 20:11:04 +08:00
|
|
|
"""
|
2014-10-14 06:15:36 +08:00
|
|
|
Converts to ASCII. Converts spaces to hyphens. Removes characters that
|
|
|
|
aren't alphanumerics, underscores, or hyphens. Converts to lowercase.
|
|
|
|
Also strips leading and trailing whitespace.
|
Merged Unicode branch into trunk (r4952:5608). This should be fully
backwards compatible for all practical purposes.
Fixed #2391, #2489, #2996, #3322, #3344, #3370, #3406, #3432, #3454, #3492, #3582, #3690, #3878, #3891, #3937, #4039, #4141, #4227, #4286, #4291, #4300, #4452, #4702
git-svn-id: http://code.djangoproject.com/svn/django/trunk@5609 bcc190cf-cafb-0310-a4f2-bffc1f526a37
2007-07-04 20:11:04 +08:00
|
|
|
"""
|
2013-12-11 04:32:58 +08:00
|
|
|
return _slugify(value)
|
2005-08-10 23:40:14 +08:00
|
|
|
|
2013-11-03 08:37:15 +08:00
|
|
|
|
2011-10-30 15:32:21 +08:00
|
|
|
@register.filter(is_safe=True)
|
2005-07-13 09:25:57 +08:00
|
|
|
def stringformat(value, arg):
|
|
|
|
"""
|
2007-11-04 10:05:56 +08:00
|
|
|
Formats the variable according to the arg, a string formatting specifier.
|
|
|
|
|
2005-07-13 09:25:57 +08:00
|
|
|
This specifier uses Python string formating syntax, with the exception that
|
|
|
|
the leading "%" is dropped.
|
2005-08-10 23:40:14 +08:00
|
|
|
|
2016-05-09 06:07:43 +08:00
|
|
|
See https://docs.python.org/3/library/stdtypes.html#printf-style-string-formatting
|
|
|
|
for documentation of Python string formatting.
|
2005-07-13 09:25:57 +08:00
|
|
|
"""
|
|
|
|
try:
|
2012-07-20 20:48:51 +08:00
|
|
|
return ("%" + six.text_type(arg)) % value
|
2005-07-13 09:25:57 +08:00
|
|
|
except (ValueError, TypeError):
|
2012-06-08 00:08:47 +08:00
|
|
|
return ""
|
2005-07-13 09:25:57 +08:00
|
|
|
|
2013-11-03 08:37:15 +08:00
|
|
|
|
2011-10-30 15:32:21 +08:00
|
|
|
@register.filter(is_safe=True)
|
2011-10-29 22:39:02 +08:00
|
|
|
@stringfilter
|
2005-11-27 06:46:31 +08:00
|
|
|
def title(value):
|
2007-11-04 10:05:56 +08:00
|
|
|
"""Converts a string into titlecase."""
|
2009-12-13 01:20:32 +08:00
|
|
|
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)
|
2005-07-13 09:25:57 +08:00
|
|
|
|
2013-11-03 08:37:15 +08:00
|
|
|
|
2011-10-30 15:32:21 +08:00
|
|
|
@register.filter(is_safe=True)
|
2011-10-29 22:39:02 +08:00
|
|
|
@stringfilter
|
2011-07-14 21:47:10 +08:00
|
|
|
def truncatechars(value, arg):
|
|
|
|
"""
|
|
|
|
Truncates a string after a certain number of characters.
|
|
|
|
|
|
|
|
Argument: Number of characters to truncate after.
|
|
|
|
"""
|
|
|
|
try:
|
|
|
|
length = int(arg)
|
2013-11-03 03:27:47 +08:00
|
|
|
except ValueError: # Invalid literal for int().
|
|
|
|
return value # Fail silently.
|
2011-07-25 17:19:47 +08:00
|
|
|
return Truncator(value).chars(length)
|
2011-07-14 21:47:10 +08:00
|
|
|
|
2013-11-03 08:37:15 +08:00
|
|
|
|
2013-01-10 17:27:20 +08:00
|
|
|
@register.filter(is_safe=True)
|
|
|
|
@stringfilter
|
|
|
|
def truncatechars_html(value, arg):
|
|
|
|
"""
|
|
|
|
Truncates HTML after a certain number of chars.
|
|
|
|
|
|
|
|
Argument: Number of chars to truncate after.
|
|
|
|
|
|
|
|
Newlines in the HTML are preserved.
|
|
|
|
"""
|
|
|
|
try:
|
|
|
|
length = int(arg)
|
|
|
|
except ValueError: # invalid literal for int()
|
|
|
|
return value # Fail silently.
|
|
|
|
return Truncator(value).chars(length, html=True)
|
|
|
|
|
|
|
|
|
2011-10-30 15:32:21 +08:00
|
|
|
@register.filter(is_safe=True)
|
2011-10-29 22:39:02 +08:00
|
|
|
@stringfilter
|
2005-07-13 09:25:57 +08:00
|
|
|
def truncatewords(value, arg):
|
|
|
|
"""
|
2007-11-04 10:05:56 +08:00
|
|
|
Truncates a string after a certain number of words.
|
2005-07-13 09:25:57 +08:00
|
|
|
|
2007-11-04 10:05:56 +08:00
|
|
|
Argument: Number of words to truncate after.
|
2010-08-07 22:57:24 +08:00
|
|
|
|
|
|
|
Newlines within the string are removed.
|
2005-07-13 09:25:57 +08:00
|
|
|
"""
|
|
|
|
try:
|
|
|
|
length = int(arg)
|
2013-11-03 03:27:47 +08:00
|
|
|
except ValueError: # Invalid literal for int().
|
|
|
|
return value # Fail silently.
|
2011-07-14 21:47:10 +08:00
|
|
|
return Truncator(value).words(length, truncate=' ...')
|
2005-07-13 09:25:57 +08:00
|
|
|
|
2013-11-03 08:37:15 +08:00
|
|
|
|
2011-10-30 15:32:21 +08:00
|
|
|
@register.filter(is_safe=True)
|
2011-10-29 22:39:02 +08:00
|
|
|
@stringfilter
|
2007-02-10 10:51:27 +08:00
|
|
|
def truncatewords_html(value, arg):
|
|
|
|
"""
|
2007-11-04 10:05:56 +08:00
|
|
|
Truncates HTML after a certain number of words.
|
2007-02-10 10:51:27 +08:00
|
|
|
|
2007-11-04 10:05:56 +08:00
|
|
|
Argument: Number of words to truncate after.
|
2010-08-07 22:57:24 +08:00
|
|
|
|
|
|
|
Newlines in the HTML are preserved.
|
2007-02-10 10:51:27 +08:00
|
|
|
"""
|
|
|
|
try:
|
|
|
|
length = int(arg)
|
2013-11-03 03:27:47 +08:00
|
|
|
except ValueError: # invalid literal for int()
|
|
|
|
return value # Fail silently.
|
2011-07-14 21:47:10 +08:00
|
|
|
return Truncator(value).words(length, html=True, truncate=' ...')
|
2007-02-10 10:51:27 +08:00
|
|
|
|
2013-11-03 08:37:15 +08:00
|
|
|
|
2011-10-30 15:32:21 +08:00
|
|
|
@register.filter(is_safe=False)
|
2011-10-29 22:39:02 +08:00
|
|
|
@stringfilter
|
2005-11-27 06:46:31 +08:00
|
|
|
def upper(value):
|
2007-11-04 10:05:56 +08:00
|
|
|
"""Converts a string into all uppercase."""
|
2005-07-13 09:25:57 +08:00
|
|
|
return value.upper()
|
|
|
|
|
2013-11-03 08:37:15 +08:00
|
|
|
|
2011-10-30 15:32:21 +08:00
|
|
|
@register.filter(is_safe=False)
|
2011-10-29 22:39:02 +08:00
|
|
|
@stringfilter
|
2010-09-14 07:01:34 +08:00
|
|
|
def urlencode(value, safe=None):
|
|
|
|
"""
|
|
|
|
Escapes a value for use in a URL.
|
|
|
|
|
|
|
|
Takes an optional ``safe`` parameter used to determine the characters which
|
|
|
|
should not be escaped by Django's ``urlquote`` method. If not provided, the
|
|
|
|
default safe characters will be used (but an empty string can be provided
|
|
|
|
when *all* characters should be escaped).
|
|
|
|
"""
|
|
|
|
kwargs = {}
|
|
|
|
if safe is not None:
|
|
|
|
kwargs['safe'] = safe
|
|
|
|
return urlquote(value, **kwargs)
|
2005-07-13 09:25:57 +08:00
|
|
|
|
2013-11-03 08:37:15 +08:00
|
|
|
|
2011-10-30 15:32:21 +08:00
|
|
|
@register.filter(is_safe=True, needs_autoescape=True)
|
2011-05-27 12:22:37 +08:00
|
|
|
@stringfilter
|
2015-03-08 19:34:55 +08:00
|
|
|
def urlize(value, autoescape=True):
|
2007-11-04 10:05:56 +08:00
|
|
|
"""Converts URLs in plain text into clickable links."""
|
2013-12-11 04:32:58 +08:00
|
|
|
return mark_safe(_urlize(value, nofollow=True, autoescape=autoescape))
|
2005-07-13 09:25:57 +08:00
|
|
|
|
2013-11-03 08:37:15 +08:00
|
|
|
|
2011-10-30 15:32:21 +08:00
|
|
|
@register.filter(is_safe=True, needs_autoescape=True)
|
2011-10-29 22:39:02 +08:00
|
|
|
@stringfilter
|
2015-03-08 19:34:55 +08:00
|
|
|
def urlizetrunc(value, limit, autoescape=True):
|
2005-07-13 09:25:57 +08:00
|
|
|
"""
|
2007-11-04 10:05:56 +08:00
|
|
|
Converts URLs into clickable links, truncating URLs to the given character
|
|
|
|
limit, and adding 'rel=nofollow' attribute to discourage spamming.
|
2005-07-13 09:25:57 +08:00
|
|
|
|
|
|
|
Argument: Length to truncate URLs to.
|
|
|
|
"""
|
2016-03-29 06:33:29 +08:00
|
|
|
return mark_safe(_urlize(value, trim_url_limit=int(limit), nofollow=True, autoescape=autoescape))
|
2005-07-13 09:25:57 +08:00
|
|
|
|
2013-11-03 08:37:15 +08:00
|
|
|
|
2011-10-30 15:32:21 +08:00
|
|
|
@register.filter(is_safe=False)
|
2011-10-29 22:39:02 +08:00
|
|
|
@stringfilter
|
2005-11-27 06:46:31 +08:00
|
|
|
def wordcount(value):
|
2007-11-04 10:05:56 +08:00
|
|
|
"""Returns the number of words."""
|
2005-07-13 09:25:57 +08:00
|
|
|
return len(value.split())
|
|
|
|
|
2013-11-03 08:37:15 +08:00
|
|
|
|
2011-10-30 15:32:21 +08:00
|
|
|
@register.filter(is_safe=True)
|
2011-10-29 22:39:02 +08:00
|
|
|
@stringfilter
|
2005-07-13 09:25:57 +08:00
|
|
|
def wordwrap(value, arg):
|
|
|
|
"""
|
2007-11-04 10:05:56 +08:00
|
|
|
Wraps words at specified line length.
|
2005-07-13 09:25:57 +08:00
|
|
|
|
2006-07-15 19:47:09 +08:00
|
|
|
Argument: number of characters to wrap the text at.
|
2005-07-13 09:25:57 +08:00
|
|
|
"""
|
2007-02-24 02:02:51 +08:00
|
|
|
return wrap(value, int(arg))
|
2005-07-13 09:25:57 +08:00
|
|
|
|
2013-11-03 08:37:15 +08:00
|
|
|
|
2011-10-30 15:32:21 +08:00
|
|
|
@register.filter(is_safe=True)
|
2011-10-29 22:39:02 +08:00
|
|
|
@stringfilter
|
2005-07-13 09:25:57 +08:00
|
|
|
def ljust(value, arg):
|
|
|
|
"""
|
2007-11-04 10:05:56 +08:00
|
|
|
Left-aligns the value in a field of a given width.
|
2005-07-13 09:25:57 +08:00
|
|
|
|
2007-11-04 10:05:56 +08:00
|
|
|
Argument: field size.
|
2005-07-13 09:25:57 +08:00
|
|
|
"""
|
2007-02-24 02:02:51 +08:00
|
|
|
return value.ljust(int(arg))
|
2005-07-13 09:25:57 +08:00
|
|
|
|
2013-11-03 08:37:15 +08:00
|
|
|
|
2011-10-30 15:32:21 +08:00
|
|
|
@register.filter(is_safe=True)
|
2011-10-29 22:39:02 +08:00
|
|
|
@stringfilter
|
2005-07-13 09:25:57 +08:00
|
|
|
def rjust(value, arg):
|
|
|
|
"""
|
2007-11-04 10:05:56 +08:00
|
|
|
Right-aligns the value in a field of a given width.
|
2005-07-13 09:25:57 +08:00
|
|
|
|
2007-11-04 10:05:56 +08:00
|
|
|
Argument: field size.
|
2005-07-13 09:25:57 +08:00
|
|
|
"""
|
2007-02-24 02:02:51 +08:00
|
|
|
return value.rjust(int(arg))
|
2005-07-13 09:25:57 +08:00
|
|
|
|
2013-11-03 08:37:15 +08:00
|
|
|
|
2011-10-30 15:32:21 +08:00
|
|
|
@register.filter(is_safe=True)
|
2011-10-29 22:39:02 +08:00
|
|
|
@stringfilter
|
2005-07-13 09:25:57 +08:00
|
|
|
def center(value, arg):
|
2007-11-04 10:05:56 +08:00
|
|
|
"""Centers the value in a field of a given width."""
|
2007-02-24 02:02:51 +08:00
|
|
|
return value.center(int(arg))
|
2005-07-13 09:25:57 +08:00
|
|
|
|
2013-11-03 08:37:15 +08:00
|
|
|
|
2011-10-29 22:51:43 +08:00
|
|
|
@register.filter
|
2011-10-29 22:39:02 +08:00
|
|
|
@stringfilter
|
2005-07-13 09:25:57 +08:00
|
|
|
def cut(value, arg):
|
2007-11-14 20:58:53 +08:00
|
|
|
"""
|
|
|
|
Removes all values of arg from the given string.
|
|
|
|
"""
|
|
|
|
safe = isinstance(value, SafeData)
|
2012-06-08 00:08:47 +08:00
|
|
|
value = value.replace(arg, '')
|
2007-11-14 20:58:53 +08:00
|
|
|
if safe and arg != ';':
|
|
|
|
return mark_safe(value)
|
|
|
|
return value
|
2005-07-13 09:25:57 +08:00
|
|
|
|
2013-11-03 08:37:15 +08:00
|
|
|
|
2005-07-13 09:25:57 +08:00
|
|
|
###################
|
|
|
|
# HTML STRINGS #
|
|
|
|
###################
|
|
|
|
|
2011-10-30 15:32:21 +08:00
|
|
|
@register.filter("escape", is_safe=True)
|
2011-05-27 12:22:37 +08:00
|
|
|
@stringfilter
|
|
|
|
def escape_filter(value):
|
2007-11-14 20:58:53 +08:00
|
|
|
"""
|
2015-03-19 15:09:34 +08:00
|
|
|
Marks the value as a string that should be auto-escaped.
|
2007-11-14 20:58:53 +08:00
|
|
|
"""
|
2016-05-11 00:46:47 +08:00
|
|
|
with warnings.catch_warnings():
|
|
|
|
# Ignore mark_for_escaping deprecation -- this will use
|
|
|
|
# conditional_escape() in Django 2.0.
|
|
|
|
warnings.simplefilter('ignore', category=RemovedInDjango20Warning)
|
|
|
|
return mark_for_escaping(value)
|
2007-11-14 20:58:53 +08:00
|
|
|
|
2013-11-03 08:37:15 +08:00
|
|
|
|
2011-10-30 15:32:21 +08:00
|
|
|
@register.filter(is_safe=True)
|
2011-10-29 22:39:02 +08:00
|
|
|
@stringfilter
|
2007-11-14 20:58:53 +08:00
|
|
|
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).
|
|
|
|
"""
|
2012-04-07 23:16:11 +08:00
|
|
|
return escape(value)
|
2005-07-13 09:25:57 +08:00
|
|
|
|
2013-11-03 08:37:15 +08:00
|
|
|
|
2011-10-30 15:32:21 +08:00
|
|
|
@register.filter("linebreaks", is_safe=True, needs_autoescape=True)
|
2011-05-27 12:22:37 +08:00
|
|
|
@stringfilter
|
2015-03-08 19:34:55 +08:00
|
|
|
def linebreaks_filter(value, autoescape=True):
|
2007-09-15 05:46:38 +08:00
|
|
|
"""
|
|
|
|
Replaces line breaks in plain text with appropriate HTML; a single
|
|
|
|
newline becomes an HTML line break (``<br />``) and a new line
|
|
|
|
followed by a blank line becomes a paragraph break (``</p>``).
|
|
|
|
"""
|
2007-11-14 20:58:53 +08:00
|
|
|
autoescape = autoescape and not isinstance(value, SafeData)
|
|
|
|
return mark_safe(linebreaks(value, autoescape))
|
2005-07-13 09:25:57 +08:00
|
|
|
|
2013-11-03 08:37:15 +08:00
|
|
|
|
2011-10-30 15:32:21 +08:00
|
|
|
@register.filter(is_safe=True, needs_autoescape=True)
|
2011-10-29 22:39:02 +08:00
|
|
|
@stringfilter
|
2015-03-08 19:34:55 +08:00
|
|
|
def linebreaksbr(value, autoescape=True):
|
2007-09-15 05:46:38 +08:00
|
|
|
"""
|
|
|
|
Converts all newlines in a piece of plain text to HTML line breaks
|
|
|
|
(``<br />``).
|
|
|
|
"""
|
2011-07-29 18:22:25 +08:00
|
|
|
autoescape = autoescape and not isinstance(value, SafeData)
|
|
|
|
value = normalize_newlines(value)
|
|
|
|
if autoescape:
|
2007-11-14 20:58:53 +08:00
|
|
|
value = escape(value)
|
|
|
|
return mark_safe(value.replace('\n', '<br />'))
|
2005-07-13 09:25:57 +08:00
|
|
|
|
2013-11-03 08:37:15 +08:00
|
|
|
|
2011-10-30 15:32:21 +08:00
|
|
|
@register.filter(is_safe=True)
|
2011-10-29 22:39:02 +08:00
|
|
|
@stringfilter
|
2007-11-14 20:58:53 +08:00
|
|
|
def safe(value):
|
|
|
|
"""
|
|
|
|
Marks the value as a string that should not be auto-escaped.
|
|
|
|
"""
|
|
|
|
return mark_safe(value)
|
|
|
|
|
2013-11-03 08:37:15 +08:00
|
|
|
|
2011-10-30 15:32:21 +08:00
|
|
|
@register.filter(is_safe=True)
|
2009-03-02 16:16:33 +08:00
|
|
|
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.
|
|
|
|
"""
|
2012-07-21 16:00:10 +08:00
|
|
|
return [mark_safe(force_text(obj)) for obj in value]
|
2009-03-02 16:16:33 +08:00
|
|
|
|
2013-11-03 08:37:15 +08:00
|
|
|
|
2011-10-30 15:32:21 +08:00
|
|
|
@register.filter(is_safe=True)
|
2011-10-29 22:39:02 +08:00
|
|
|
@stringfilter
|
2005-11-27 06:46:31 +08:00
|
|
|
def striptags(value):
|
2007-11-04 10:05:56 +08:00
|
|
|
"""Strips all [X]HTML tags."""
|
2005-07-13 09:25:57 +08:00
|
|
|
return strip_tags(value)
|
|
|
|
|
2013-11-03 08:37:15 +08:00
|
|
|
|
2005-07-13 09:25:57 +08:00
|
|
|
###################
|
|
|
|
# LISTS #
|
|
|
|
###################
|
|
|
|
|
2015-11-05 18:59:56 +08:00
|
|
|
def _property_resolver(arg):
|
|
|
|
"""
|
|
|
|
When arg is convertible to float, behave like operator.itemgetter(arg)
|
|
|
|
Otherwise, behave like Variable(arg).resolve
|
|
|
|
|
|
|
|
>>> _property_resolver(1)('abc')
|
|
|
|
'b'
|
|
|
|
>>> _property_resolver('1')('abc')
|
|
|
|
Traceback (most recent call last):
|
|
|
|
...
|
|
|
|
TypeError: string indices must be integers
|
|
|
|
>>> class Foo:
|
|
|
|
... a = 42
|
|
|
|
... b = 3.14
|
|
|
|
... c = 'Hey!'
|
|
|
|
>>> _property_resolver('b')(Foo())
|
|
|
|
3.14
|
|
|
|
"""
|
|
|
|
try:
|
|
|
|
float(arg)
|
|
|
|
except ValueError:
|
|
|
|
return Variable(arg).resolve
|
|
|
|
else:
|
|
|
|
return itemgetter(arg)
|
|
|
|
|
|
|
|
|
2011-10-30 15:32:21 +08:00
|
|
|
@register.filter(is_safe=False)
|
2005-07-13 09:25:57 +08:00
|
|
|
def dictsort(value, arg):
|
|
|
|
"""
|
|
|
|
Takes a list of dicts, returns that list sorted by the property given in
|
|
|
|
the argument.
|
|
|
|
"""
|
2012-01-15 10:01:21 +08:00
|
|
|
try:
|
2015-11-05 18:59:56 +08:00
|
|
|
return sorted(value, key=_property_resolver(arg))
|
2012-01-15 10:01:21 +08:00
|
|
|
except (TypeError, VariableDoesNotExist):
|
2012-06-08 00:08:47 +08:00
|
|
|
return ''
|
2005-07-13 09:25:57 +08:00
|
|
|
|
2013-11-03 08:37:15 +08:00
|
|
|
|
2011-10-30 15:32:21 +08:00
|
|
|
@register.filter(is_safe=False)
|
2005-07-13 09:25:57 +08:00
|
|
|
def dictsortreversed(value, arg):
|
|
|
|
"""
|
|
|
|
Takes a list of dicts, returns that list sorted in reverse order by the
|
|
|
|
property given in the argument.
|
|
|
|
"""
|
2012-01-15 10:01:21 +08:00
|
|
|
try:
|
2015-11-05 18:59:56 +08:00
|
|
|
return sorted(value, key=_property_resolver(arg), reverse=True)
|
2012-01-15 10:01:21 +08:00
|
|
|
except (TypeError, VariableDoesNotExist):
|
2012-06-08 00:08:47 +08:00
|
|
|
return ''
|
2005-07-13 09:25:57 +08:00
|
|
|
|
2013-11-03 08:37:15 +08:00
|
|
|
|
2011-10-30 15:32:21 +08:00
|
|
|
@register.filter(is_safe=False)
|
2005-11-27 06:46:31 +08:00
|
|
|
def first(value):
|
2007-11-04 10:05:56 +08:00
|
|
|
"""Returns the first item in a list."""
|
2005-07-13 09:25:57 +08:00
|
|
|
try:
|
|
|
|
return value[0]
|
|
|
|
except IndexError:
|
2012-06-08 00:08:47 +08:00
|
|
|
return ''
|
2005-07-13 09:25:57 +08:00
|
|
|
|
2013-11-03 08:37:15 +08:00
|
|
|
|
2011-10-30 15:32:21 +08:00
|
|
|
@register.filter(is_safe=True, needs_autoescape=True)
|
2015-03-08 19:34:55 +08:00
|
|
|
def join(value, arg, autoescape=True):
|
2008-11-15 09:16:20 +08:00
|
|
|
"""
|
|
|
|
Joins a list with a string, like Python's ``str.join(list)``.
|
|
|
|
"""
|
2012-07-21 16:00:10 +08:00
|
|
|
value = map(force_text, value)
|
2008-11-15 09:16:20 +08:00
|
|
|
if autoescape:
|
|
|
|
value = [conditional_escape(v) for v in value]
|
2005-07-13 09:25:57 +08:00
|
|
|
try:
|
2010-08-03 23:42:39 +08:00
|
|
|
data = conditional_escape(arg).join(value)
|
2013-11-03 03:27:47 +08:00
|
|
|
except AttributeError: # fail silently but nicely
|
2005-07-13 09:25:57 +08:00
|
|
|
return value
|
2008-11-15 09:16:20 +08:00
|
|
|
return mark_safe(data)
|
2005-07-13 09:25:57 +08:00
|
|
|
|
2013-11-03 08:37:15 +08:00
|
|
|
|
2011-10-30 15:32:21 +08:00
|
|
|
@register.filter(is_safe=True)
|
2008-01-06 11:53:33 +08:00
|
|
|
def last(value):
|
|
|
|
"Returns the last item in a list"
|
|
|
|
try:
|
|
|
|
return value[-1]
|
|
|
|
except IndexError:
|
2012-06-08 00:08:47 +08:00
|
|
|
return ''
|
2008-01-06 11:53:33 +08:00
|
|
|
|
2013-11-03 08:37:15 +08:00
|
|
|
|
2014-03-22 23:33:37 +08:00
|
|
|
@register.filter(is_safe=False)
|
2005-11-27 06:46:31 +08:00
|
|
|
def length(value):
|
2007-11-04 10:05:56 +08:00
|
|
|
"""Returns the length of the value - useful for lists."""
|
2009-03-31 00:46:27 +08:00
|
|
|
try:
|
|
|
|
return len(value)
|
|
|
|
except (ValueError, TypeError):
|
2013-10-15 13:15:13 +08:00
|
|
|
return 0
|
2005-07-13 09:25:57 +08:00
|
|
|
|
2013-11-03 08:37:15 +08:00
|
|
|
|
2011-10-30 15:32:21 +08:00
|
|
|
@register.filter(is_safe=False)
|
2005-07-13 09:25:57 +08:00
|
|
|
def length_is(value, arg):
|
2007-11-04 10:05:56 +08:00
|
|
|
"""Returns a boolean of whether the value's length is the argument."""
|
2009-03-31 00:46:27 +08:00
|
|
|
try:
|
|
|
|
return len(value) == int(arg)
|
|
|
|
except (ValueError, TypeError):
|
|
|
|
return ''
|
2005-07-13 09:25:57 +08:00
|
|
|
|
2013-11-03 08:37:15 +08:00
|
|
|
|
2011-10-30 15:32:21 +08:00
|
|
|
@register.filter(is_safe=True)
|
2005-11-27 06:46:31 +08:00
|
|
|
def random(value):
|
2007-11-04 10:05:56 +08:00
|
|
|
"""Returns a random item from the list."""
|
2005-08-19 22:59:09 +08:00
|
|
|
return random_module.choice(value)
|
2005-07-13 09:25:57 +08:00
|
|
|
|
2013-11-03 08:37:15 +08:00
|
|
|
|
2011-10-30 15:32:21 +08:00
|
|
|
@register.filter("slice", is_safe=True)
|
2011-10-29 23:28:44 +08:00
|
|
|
def slice_filter(value, arg):
|
2005-07-13 09:25:57 +08:00
|
|
|
"""
|
|
|
|
Returns a slice of the list.
|
|
|
|
|
|
|
|
Uses the same syntax as Python's list slicing; see
|
2013-04-01 20:03:55 +08:00
|
|
|
http://www.diveintopython3.net/native-datatypes.html#slicinglists
|
2005-07-13 09:25:57 +08:00
|
|
|
for an introduction.
|
|
|
|
"""
|
|
|
|
try:
|
2006-01-02 02:37:33 +08:00
|
|
|
bits = []
|
2012-06-08 00:08:47 +08:00
|
|
|
for x in arg.split(':'):
|
2006-01-02 02:37:33 +08:00
|
|
|
if len(x) == 0:
|
|
|
|
bits.append(None)
|
|
|
|
else:
|
|
|
|
bits.append(int(x))
|
|
|
|
return value[slice(*bits)]
|
|
|
|
|
2005-09-23 08:12:24 +08:00
|
|
|
except (ValueError, TypeError):
|
2013-11-03 03:27:47 +08:00
|
|
|
return value # Fail silently.
|
2005-07-13 09:25:57 +08:00
|
|
|
|
2013-11-03 08:37:15 +08:00
|
|
|
|
2011-10-30 15:32:21 +08:00
|
|
|
@register.filter(is_safe=True, needs_autoescape=True)
|
2015-03-08 19:34:55 +08:00
|
|
|
def unordered_list(value, autoescape=True):
|
2005-07-13 09:25:57 +08:00
|
|
|
"""
|
|
|
|
Recursively takes a self-nested list and returns an HTML unordered list --
|
|
|
|
WITHOUT opening and closing <ul> tags.
|
|
|
|
|
2007-08-26 09:11:20 +08:00
|
|
|
The list is assumed to be in the proper format. For example, if ``var``
|
|
|
|
contains: ``['States', ['Kansas', ['Lawrence', 'Topeka'], 'Illinois']]``,
|
2005-07-13 09:25:57 +08:00
|
|
|
then ``{{ var|unordered_list }}`` would return::
|
|
|
|
|
|
|
|
<li>States
|
|
|
|
<ul>
|
|
|
|
<li>Kansas
|
|
|
|
<ul>
|
|
|
|
<li>Lawrence</li>
|
|
|
|
<li>Topeka</li>
|
|
|
|
</ul>
|
|
|
|
</li>
|
|
|
|
<li>Illinois</li>
|
|
|
|
</ul>
|
|
|
|
</li>
|
|
|
|
"""
|
2007-11-14 20:58:53 +08:00
|
|
|
if autoescape:
|
|
|
|
escaper = conditional_escape
|
|
|
|
else:
|
2016-01-24 00:47:07 +08:00
|
|
|
def escaper(x):
|
|
|
|
return x
|
2013-10-22 18:21:07 +08:00
|
|
|
|
2014-08-08 21:40:44 +08:00
|
|
|
def walk_items(item_list):
|
|
|
|
item_iterator = iter(item_list)
|
2015-06-25 18:46:11 +08:00
|
|
|
try:
|
|
|
|
item = next(item_iterator)
|
|
|
|
while True:
|
2014-08-08 21:40:44 +08:00
|
|
|
try:
|
2015-06-25 18:46:11 +08:00
|
|
|
next_item = next(item_iterator)
|
|
|
|
except StopIteration:
|
|
|
|
yield item, None
|
|
|
|
break
|
|
|
|
if not isinstance(next_item, six.string_types):
|
|
|
|
try:
|
|
|
|
iter(next_item)
|
|
|
|
except TypeError:
|
|
|
|
pass
|
|
|
|
else:
|
|
|
|
yield item, next_item
|
|
|
|
item = next(item_iterator)
|
|
|
|
continue
|
|
|
|
yield item, None
|
|
|
|
item = next_item
|
|
|
|
except StopIteration:
|
|
|
|
pass
|
2014-08-08 21:40:44 +08:00
|
|
|
|
|
|
|
def list_formatter(item_list, tabs=1):
|
2012-06-08 00:08:47 +08:00
|
|
|
indent = '\t' * tabs
|
2007-08-26 09:11:20 +08:00
|
|
|
output = []
|
2014-08-08 21:40:44 +08:00
|
|
|
for item, children in walk_items(item_list):
|
2007-08-26 09:11:20 +08:00
|
|
|
sublist = ''
|
2014-08-08 21:40:44 +08:00
|
|
|
if children:
|
|
|
|
sublist = '\n%s<ul>\n%s\n%s</ul>\n%s' % (
|
|
|
|
indent, list_formatter(children, tabs + 1), indent, indent)
|
|
|
|
output.append('%s<li>%s%s</li>' % (
|
|
|
|
indent, escaper(force_text(item)), sublist))
|
2007-08-26 09:11:20 +08:00
|
|
|
return '\n'.join(output)
|
2014-08-08 21:40:44 +08:00
|
|
|
|
|
|
|
return mark_safe(list_formatter(value))
|
2005-07-13 09:25:57 +08:00
|
|
|
|
2013-11-03 08:37:15 +08:00
|
|
|
|
2005-07-13 09:25:57 +08:00
|
|
|
###################
|
|
|
|
# INTEGERS #
|
|
|
|
###################
|
|
|
|
|
2011-10-30 15:32:21 +08:00
|
|
|
@register.filter(is_safe=False)
|
2005-07-13 09:25:57 +08:00
|
|
|
def add(value, arg):
|
2007-11-04 10:05:56 +08:00
|
|
|
"""Adds the arg to the value."""
|
2010-02-23 07:34:33 +08:00
|
|
|
try:
|
|
|
|
return int(value) + int(arg)
|
|
|
|
except (ValueError, TypeError):
|
|
|
|
try:
|
|
|
|
return value + arg
|
2011-09-18 16:33:39 +08:00
|
|
|
except Exception:
|
|
|
|
return ''
|
2005-07-13 09:25:57 +08:00
|
|
|
|
2013-11-03 08:37:15 +08:00
|
|
|
|
2011-10-30 15:32:21 +08:00
|
|
|
@register.filter(is_safe=False)
|
2005-07-13 09:25:57 +08:00
|
|
|
def get_digit(value, arg):
|
|
|
|
"""
|
|
|
|
Given a whole number, returns the requested digit of it, where 1 is the
|
|
|
|
right-most digit, 2 is the second-right-most digit, etc. Returns the
|
|
|
|
original value for invalid input (if input or argument is not an integer,
|
|
|
|
or if argument is less than 1). Otherwise, output is always an integer.
|
|
|
|
"""
|
|
|
|
try:
|
|
|
|
arg = int(arg)
|
|
|
|
value = int(value)
|
|
|
|
except ValueError:
|
2013-11-03 03:27:47 +08:00
|
|
|
return value # Fail silently for an invalid argument
|
2005-07-13 09:25:57 +08:00
|
|
|
if arg < 1:
|
|
|
|
return value
|
|
|
|
try:
|
|
|
|
return int(str(value)[-arg])
|
|
|
|
except IndexError:
|
|
|
|
return 0
|
|
|
|
|
2013-11-03 08:37:15 +08:00
|
|
|
|
2005-07-13 09:25:57 +08:00
|
|
|
###################
|
|
|
|
# DATES #
|
|
|
|
###################
|
|
|
|
|
2011-11-18 21:01:06 +08:00
|
|
|
@register.filter(expects_localtime=True, is_safe=False)
|
2006-05-17 05:28:06 +08:00
|
|
|
def date(value, arg=None):
|
2007-11-04 10:05:56 +08:00
|
|
|
"""Formats a date according to the given format."""
|
2012-11-28 04:22:18 +08:00
|
|
|
if value in (None, ''):
|
2012-06-08 00:08:47 +08:00
|
|
|
return ''
|
2009-04-02 13:20:25 +08:00
|
|
|
try:
|
2010-01-02 05:36:36 +08:00
|
|
|
return formats.date_format(value, arg)
|
2009-04-02 13:20:25 +08:00
|
|
|
except AttributeError:
|
2009-12-23 01:58:49 +08:00
|
|
|
try:
|
|
|
|
return format(value, arg)
|
|
|
|
except AttributeError:
|
|
|
|
return ''
|
2005-07-13 09:25:57 +08:00
|
|
|
|
2013-11-03 08:37:15 +08:00
|
|
|
|
2011-11-18 21:01:06 +08:00
|
|
|
@register.filter(expects_localtime=True, is_safe=False)
|
2006-05-17 05:28:06 +08:00
|
|
|
def time(value, arg=None):
|
2007-11-04 10:05:56 +08:00
|
|
|
"""Formats a time according to the given format."""
|
2012-06-08 00:08:47 +08:00
|
|
|
if value in (None, ''):
|
|
|
|
return ''
|
2009-04-02 13:20:25 +08:00
|
|
|
try:
|
2010-01-02 05:36:36 +08:00
|
|
|
return formats.time_format(value, arg)
|
2016-04-04 01:31:35 +08:00
|
|
|
except (AttributeError, TypeError):
|
2009-12-23 01:58:49 +08:00
|
|
|
try:
|
2011-05-28 01:05:16 +08:00
|
|
|
return time_format(value, arg)
|
2016-04-04 01:31:35 +08:00
|
|
|
except (AttributeError, TypeError):
|
2009-12-23 01:58:49 +08:00
|
|
|
return ''
|
2005-07-13 09:25:57 +08:00
|
|
|
|
2013-11-03 08:37:15 +08:00
|
|
|
|
2011-10-30 15:32:21 +08:00
|
|
|
@register.filter("timesince", is_safe=False)
|
2011-05-27 12:22:37 +08:00
|
|
|
def timesince_filter(value, arg=None):
|
2007-11-04 10:05:56 +08:00
|
|
|
"""Formats a date as the time since that date (i.e. "4 days, 6 hours")."""
|
2006-06-11 08:33:44 +08:00
|
|
|
if not value:
|
2012-06-08 00:08:47 +08:00
|
|
|
return ''
|
2008-08-26 16:08:55 +08:00
|
|
|
try:
|
|
|
|
if arg:
|
|
|
|
return timesince(value, arg)
|
|
|
|
return timesince(value)
|
|
|
|
except (ValueError, TypeError):
|
2012-06-08 00:08:47 +08:00
|
|
|
return ''
|
2005-07-13 09:25:57 +08:00
|
|
|
|
2013-11-03 08:37:15 +08:00
|
|
|
|
2011-10-30 15:32:21 +08:00
|
|
|
@register.filter("timeuntil", is_safe=False)
|
2011-05-27 12:22:37 +08:00
|
|
|
def timeuntil_filter(value, arg=None):
|
2007-11-04 10:05:56 +08:00
|
|
|
"""Formats a date as the time until that date (i.e. "4 days, 6 hours")."""
|
2006-06-21 14:56:08 +08:00
|
|
|
if not value:
|
2012-06-08 00:08:47 +08:00
|
|
|
return ''
|
2008-08-26 16:08:55 +08:00
|
|
|
try:
|
|
|
|
return timeuntil(value, arg)
|
|
|
|
except (ValueError, TypeError):
|
2012-06-08 00:08:47 +08:00
|
|
|
return ''
|
2006-06-21 14:56:08 +08:00
|
|
|
|
2013-11-03 08:37:15 +08:00
|
|
|
|
2005-07-13 09:25:57 +08:00
|
|
|
###################
|
|
|
|
# LOGIC #
|
|
|
|
###################
|
|
|
|
|
2011-10-30 15:32:21 +08:00
|
|
|
@register.filter(is_safe=False)
|
2005-07-13 09:25:57 +08:00
|
|
|
def default(value, arg):
|
2007-11-04 10:05:56 +08:00
|
|
|
"""If value is unavailable, use given default."""
|
2005-07-13 09:25:57 +08:00
|
|
|
return value or arg
|
|
|
|
|
2013-11-03 08:37:15 +08:00
|
|
|
|
2011-10-30 15:32:21 +08:00
|
|
|
@register.filter(is_safe=False)
|
2005-10-14 11:48:27 +08:00
|
|
|
def default_if_none(value, arg):
|
2007-11-04 10:05:56 +08:00
|
|
|
"""If value is None, use given default."""
|
2005-10-14 11:48:27 +08:00
|
|
|
if value is None:
|
|
|
|
return arg
|
|
|
|
return value
|
|
|
|
|
2013-11-03 08:37:15 +08:00
|
|
|
|
2011-10-30 15:32:21 +08:00
|
|
|
@register.filter(is_safe=False)
|
2005-07-13 09:25:57 +08:00
|
|
|
def divisibleby(value, arg):
|
2015-12-03 07:55:50 +08:00
|
|
|
"""Returns True if the value is divisible by the argument."""
|
2005-07-13 09:25:57 +08:00
|
|
|
return int(value) % int(arg) == 0
|
|
|
|
|
2013-11-03 08:37:15 +08:00
|
|
|
|
2011-10-30 15:32:21 +08:00
|
|
|
@register.filter(is_safe=False)
|
2005-12-15 23:56:26 +08:00
|
|
|
def yesno(value, arg=None):
|
2005-07-13 09:25:57 +08:00
|
|
|
"""
|
|
|
|
Given a string mapping values for true, false and (optionally) None,
|
2011-12-17 10:27:00 +08:00
|
|
|
returns one of those strings according to the value:
|
2005-07-13 09:25:57 +08:00
|
|
|
|
|
|
|
========== ====================== ==================================
|
|
|
|
Value Argument Outputs
|
|
|
|
========== ====================== ==================================
|
|
|
|
``True`` ``"yeah,no,maybe"`` ``yeah``
|
|
|
|
``False`` ``"yeah,no,maybe"`` ``no``
|
|
|
|
``None`` ``"yeah,no,maybe"`` ``maybe``
|
|
|
|
``None`` ``"yeah,no"`` ``"no"`` (converts None to False
|
|
|
|
if no mapping for None is given.
|
|
|
|
========== ====================== ==================================
|
2005-12-15 23:56:26 +08:00
|
|
|
"""
|
|
|
|
if arg is None:
|
Merged Unicode branch into trunk (r4952:5608). This should be fully
backwards compatible for all practical purposes.
Fixed #2391, #2489, #2996, #3322, #3344, #3370, #3406, #3432, #3454, #3492, #3582, #3690, #3878, #3891, #3937, #4039, #4141, #4227, #4286, #4291, #4300, #4452, #4702
git-svn-id: http://code.djangoproject.com/svn/django/trunk@5609 bcc190cf-cafb-0310-a4f2-bffc1f526a37
2007-07-04 20:11:04 +08:00
|
|
|
arg = ugettext('yes,no,maybe')
|
2012-06-08 00:08:47 +08:00
|
|
|
bits = arg.split(',')
|
2005-07-13 09:25:57 +08:00
|
|
|
if len(bits) < 2:
|
2013-11-03 03:27:47 +08:00
|
|
|
return value # Invalid arg.
|
2005-07-13 09:25:57 +08:00
|
|
|
try:
|
|
|
|
yes, no, maybe = bits
|
2007-11-04 10:05:56 +08:00
|
|
|
except ValueError:
|
|
|
|
# Unpack list of wrong size (no "maybe" value provided).
|
2005-09-01 10:22:46 +08:00
|
|
|
yes, no, maybe = bits[0], bits[1], bits[1]
|
2005-07-13 09:25:57 +08:00
|
|
|
if value is None:
|
|
|
|
return maybe
|
|
|
|
if value:
|
|
|
|
return yes
|
|
|
|
return no
|
|
|
|
|
2013-11-03 08:37:15 +08:00
|
|
|
|
2005-07-13 09:25:57 +08:00
|
|
|
###################
|
|
|
|
# MISC #
|
|
|
|
###################
|
|
|
|
|
2011-10-30 15:32:21 +08:00
|
|
|
@register.filter(is_safe=True)
|
2015-10-26 07:05:03 +08:00
|
|
|
def filesizeformat(bytes_):
|
2005-07-13 09:25:57 +08:00
|
|
|
"""
|
2007-11-04 10:05:56 +08:00
|
|
|
Formats the value like a 'human-readable' file size (i.e. 13 KB, 4.1 MB,
|
2016-01-11 00:48:16 +08:00
|
|
|
102 bytes, etc.).
|
2005-07-13 09:25:57 +08:00
|
|
|
"""
|
2006-11-07 12:58:10 +08:00
|
|
|
try:
|
2015-10-26 07:05:03 +08:00
|
|
|
bytes_ = float(bytes_)
|
2013-10-25 01:30:03 +08:00
|
|
|
except (TypeError, ValueError, UnicodeDecodeError):
|
2013-05-18 19:58:45 +08:00
|
|
|
value = ungettext("%(size)d byte", "%(size)d bytes", 0) % {'size': 0}
|
|
|
|
return avoid_wrapping(value)
|
2011-01-24 16:16:09 +08:00
|
|
|
|
2016-01-24 00:47:07 +08:00
|
|
|
def filesize_number_format(value):
|
|
|
|
return formats.number_format(round(value, 1), 1)
|
Merged Unicode branch into trunk (r4952:5608). This should be fully
backwards compatible for all practical purposes.
Fixed #2391, #2489, #2996, #3322, #3344, #3370, #3406, #3432, #3454, #3492, #3582, #3690, #3878, #3891, #3937, #4039, #4141, #4227, #4286, #4291, #4300, #4452, #4702
git-svn-id: http://code.djangoproject.com/svn/django/trunk@5609 bcc190cf-cafb-0310-a4f2-bffc1f526a37
2007-07-04 20:11:04 +08:00
|
|
|
|
2013-10-21 20:52:21 +08:00
|
|
|
KB = 1 << 10
|
|
|
|
MB = 1 << 20
|
|
|
|
GB = 1 << 30
|
|
|
|
TB = 1 << 40
|
|
|
|
PB = 1 << 50
|
2012-07-18 20:59:12 +08:00
|
|
|
|
2015-10-26 07:05:03 +08:00
|
|
|
negative = bytes_ < 0
|
2015-10-25 09:51:20 +08:00
|
|
|
if negative:
|
2015-10-26 07:05:03 +08:00
|
|
|
bytes_ = -bytes_ # Allow formatting of negative numbers.
|
|
|
|
|
|
|
|
if bytes_ < KB:
|
|
|
|
value = ungettext("%(size)d byte", "%(size)d bytes", bytes_) % {'size': bytes_}
|
|
|
|
elif bytes_ < MB:
|
|
|
|
value = ugettext("%s KB") % filesize_number_format(bytes_ / KB)
|
|
|
|
elif bytes_ < GB:
|
|
|
|
value = ugettext("%s MB") % filesize_number_format(bytes_ / MB)
|
|
|
|
elif bytes_ < TB:
|
|
|
|
value = ugettext("%s GB") % filesize_number_format(bytes_ / GB)
|
|
|
|
elif bytes_ < PB:
|
|
|
|
value = ugettext("%s TB") % filesize_number_format(bytes_ / TB)
|
2013-05-18 19:58:45 +08:00
|
|
|
else:
|
2015-10-26 07:05:03 +08:00
|
|
|
value = ugettext("%s PB") % filesize_number_format(bytes_ / PB)
|
2013-05-18 19:58:45 +08:00
|
|
|
|
2015-10-25 09:51:20 +08:00
|
|
|
if negative:
|
|
|
|
value = "-%s" % value
|
2013-05-18 19:58:45 +08:00
|
|
|
return avoid_wrapping(value)
|
2005-07-13 09:25:57 +08:00
|
|
|
|
2013-11-03 08:37:15 +08:00
|
|
|
|
2011-10-30 15:32:21 +08:00
|
|
|
@register.filter(is_safe=False)
|
2012-06-08 00:08:47 +08:00
|
|
|
def pluralize(value, arg='s'):
|
2006-07-04 14:18:39 +08:00
|
|
|
"""
|
2007-12-02 00:58:52 +08:00
|
|
|
Returns a plural suffix if the value is not 1. By default, 's' is used as
|
2007-11-04 10:08:04 +08:00
|
|
|
the suffix:
|
|
|
|
|
2007-11-14 20:58:53 +08:00
|
|
|
* If value is 0, vote{{ value|pluralize }} displays "0 votes".
|
|
|
|
* If value is 1, vote{{ value|pluralize }} displays "1 vote".
|
|
|
|
* If value is 2, vote{{ value|pluralize }} displays "2 votes".
|
2007-11-04 10:08:04 +08:00
|
|
|
|
|
|
|
If an argument is provided, that string is used instead:
|
|
|
|
|
2007-11-14 20:58:53 +08:00
|
|
|
* If value is 0, class{{ value|pluralize:"es" }} displays "0 classes".
|
|
|
|
* If value is 1, class{{ value|pluralize:"es" }} displays "1 class".
|
|
|
|
* If value is 2, class{{ value|pluralize:"es" }} displays "2 classes".
|
2007-11-04 10:08:04 +08:00
|
|
|
|
|
|
|
If the provided argument contains a comma, the text before the comma is
|
|
|
|
used for the singular case and the text after the comma is used for the
|
|
|
|
plural case:
|
|
|
|
|
2007-11-14 20:58:53 +08:00
|
|
|
* If value is 0, cand{{ value|pluralize:"y,ies" }} displays "0 candies".
|
|
|
|
* If value is 1, cand{{ value|pluralize:"y,ies" }} displays "1 candy".
|
|
|
|
* If value is 2, cand{{ value|pluralize:"y,ies" }} displays "2 candies".
|
2006-07-04 14:18:39 +08:00
|
|
|
"""
|
2014-03-31 03:11:05 +08:00
|
|
|
if ',' not in arg:
|
2012-06-08 00:08:47 +08:00
|
|
|
arg = ',' + arg
|
|
|
|
bits = arg.split(',')
|
2006-07-04 14:31:26 +08:00
|
|
|
if len(bits) > 2:
|
2012-06-08 00:08:47 +08:00
|
|
|
return ''
|
2006-07-04 14:31:26 +08:00
|
|
|
singular_suffix, plural_suffix = bits[:2]
|
|
|
|
|
2005-07-13 09:25:57 +08:00
|
|
|
try:
|
2014-06-11 02:28:32 +08:00
|
|
|
if float(value) != 1:
|
2006-07-04 14:18:39 +08:00
|
|
|
return plural_suffix
|
2013-11-03 03:27:47 +08:00
|
|
|
except ValueError: # Invalid string that's not a number.
|
2005-07-13 09:25:57 +08:00
|
|
|
pass
|
2013-11-03 03:27:47 +08:00
|
|
|
except TypeError: # Value isn't a string or a number; maybe it's a list?
|
2005-07-13 09:25:57 +08:00
|
|
|
try:
|
|
|
|
if len(value) != 1:
|
2006-07-04 14:18:39 +08:00
|
|
|
return plural_suffix
|
2013-11-03 03:27:47 +08:00
|
|
|
except TypeError: # len() of unsized object.
|
2005-07-13 09:25:57 +08:00
|
|
|
pass
|
2006-07-04 14:18:39 +08:00
|
|
|
return singular_suffix
|
2005-07-13 09:25:57 +08:00
|
|
|
|
2013-11-03 08:37:15 +08:00
|
|
|
|
2011-10-30 15:32:21 +08:00
|
|
|
@register.filter("phone2numeric", is_safe=True)
|
2011-05-27 12:22:37 +08:00
|
|
|
def phone2numeric_filter(value):
|
2007-11-04 10:05:56 +08:00
|
|
|
"""Takes a phone number and converts it in to its numerical equivalent."""
|
2005-07-13 09:25:57 +08:00
|
|
|
return phone2numeric(value)
|
|
|
|
|
2013-11-03 08:37:15 +08:00
|
|
|
|
2011-10-30 15:32:21 +08:00
|
|
|
@register.filter(is_safe=True)
|
2005-11-27 06:46:31 +08:00
|
|
|
def pprint(value):
|
2007-11-04 10:05:56 +08:00
|
|
|
"""A wrapper around pprint.pprint -- for debugging, really."""
|
2006-05-02 09:31:56 +08:00
|
|
|
try:
|
|
|
|
return pformat(value)
|
2012-04-29 00:09:37 +08:00
|
|
|
except Exception as e:
|
2014-08-27 03:09:50 +08:00
|
|
|
return "Error in formatting: %s: %s" % (e.__class__.__name__, force_text(e, errors="replace"))
|