2007-11-04 10:05:56 +08:00
|
|
|
"""Default variable filters."""
|
|
|
|
import random as random_module
|
2015-01-28 20:35:27 +08:00
|
|
|
import re
|
2017-10-19 06:09:19 +08:00
|
|
|
import types
|
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
|
2017-01-26 21:25:15 +08:00
|
|
|
from urllib.parse import quote
|
2005-07-13 09:25:57 +08:00
|
|
|
|
2016-12-29 23:27:49 +08:00
|
|
|
from django.utils import formats
|
2011-05-28 01:05:16 +08:00
|
|
|
from django.utils.dateformat import format, time_format
|
2017-02-07 16:17:38 +08:00
|
|
|
from django.utils.encoding import iri_to_uri
|
2015-01-28 20:35:27 +08:00
|
|
|
from django.utils.html import (
|
2017-10-13 02:59:38 +08:00
|
|
|
avoid_wrapping, conditional_escape, escape, escapejs,
|
|
|
|
json_script as _json_script, linebreaks, strip_tags, urlize as _urlize,
|
2015-01-28 20:35:27 +08:00
|
|
|
)
|
2017-01-01 01:43:30 +08:00
|
|
|
from django.utils.safestring import SafeData, mark_safe
|
2015-01-28 20:35:27 +08:00
|
|
|
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
|
2017-01-27 03:58:33 +08:00
|
|
|
from django.utils.translation import gettext, ngettext
|
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):
|
|
|
|
"""
|
2017-01-21 05:04:05 +08:00
|
|
|
Decorator for filters which should only receive strings. The object
|
|
|
|
passed as the first positional argument will be converted to a string.
|
2007-02-24 02:02:51 +08:00
|
|
|
"""
|
|
|
|
def _dec(*args, **kwargs):
|
2017-10-01 00:13:30 +08:00
|
|
|
args = list(args)
|
|
|
|
args[0] = str(args[0])
|
|
|
|
if (isinstance(args[0], SafeData) and
|
|
|
|
getattr(_dec._decorated_function, 'is_safe', False)):
|
|
|
|
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
|
|
|
"""
|
2017-01-25 04:36:36 +08:00
|
|
|
Add slashes before quotes. Useful for escaping strings in CSV, for
|
2007-12-05 05:08:29 +08:00
|
|
|
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):
|
2017-01-25 04:36:36 +08:00
|
|
|
"""Capitalize 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):
|
2017-01-25 04:36:36 +08:00
|
|
|
"""Hex encode 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
|
|
|
|
2017-10-13 02:59:38 +08:00
|
|
|
@register.filter(is_safe=True)
|
|
|
|
def json_script(value, element_id):
|
|
|
|
"""
|
|
|
|
Output value JSON-encoded, wrapped in a <script type="application/json">
|
|
|
|
tag.
|
|
|
|
"""
|
|
|
|
return _json_script(value, element_id)
|
|
|
|
|
|
|
|
|
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
|
|
|
"""
|
2017-01-25 04:36:36 +08:00
|
|
|
Display a float to a specified number of decimal places.
|
2007-11-04 10:27:17 +08:00
|
|
|
|
2017-01-25 04:36:36 +08:00
|
|
|
If called without an argument, display 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"
|
|
|
|
|
2017-01-25 04:36:36 +08:00
|
|
|
If arg is positive, always display exactly arg number of decimal places:
|
2007-11-04 10:27:17 +08:00
|
|
|
|
|
|
|
* {{ num1|floatformat:3 }} displays "34.232"
|
|
|
|
* {{ num2|floatformat:3 }} displays "34.000"
|
|
|
|
* {{ num3|floatformat:3 }} displays "34.260"
|
|
|
|
|
2017-01-25 04:36:36 +08:00
|
|
|
If arg is negative, display arg number of decimal places -- but only if
|
|
|
|
there are places to be displayed:
|
2007-11-04 10:27:17 +08:00
|
|
|
|
|
|
|
* {{ 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
|
|
|
|
2017-01-25 04:36:36 +08:00
|
|
|
If the input float is infinity or NaN, display the string representation
|
|
|
|
of that value.
|
2005-07-13 09:25:57 +08:00
|
|
|
"""
|
2005-11-03 03:01:27 +08:00
|
|
|
try:
|
2016-11-15 22:07:15 +08:00
|
|
|
input_val = repr(text)
|
2008-11-08 09:44:46 +08:00
|
|
|
d = Decimal(input_val)
|
|
|
|
except InvalidOperation:
|
2009-04-01 02:17:21 +08:00
|
|
|
try:
|
2017-02-07 16:17:38 +08:00
|
|
|
d = Decimal(str(float(text)))
|
2017-01-22 02:53:35 +08:00
|
|
|
except (ValueError, InvalidOperation, TypeError):
|
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
|
|
|
|
2017-12-19 23:08:10 +08:00
|
|
|
exp = Decimal(1).scaleb(-abs(p))
|
2017-01-22 02:53:35 +08:00
|
|
|
# Set the precision high enough to avoid an exception (#15789).
|
|
|
|
tupl = d.as_tuple()
|
|
|
|
units = len(tupl[1])
|
|
|
|
units += -tupl[2] if m else tupl[2]
|
|
|
|
prec = abs(p) + units + 1
|
|
|
|
|
|
|
|
# Avoid conversion to scientific notation by accessing `sign`, `digits`,
|
|
|
|
# and `exponent` from Decimal.as_tuple() directly.
|
2019-10-31 10:05:26 +08:00
|
|
|
rounded_d = d.quantize(exp, ROUND_HALF_UP, Context(prec=prec))
|
|
|
|
sign, digits, exponent = rounded_d.as_tuple()
|
2017-01-22 02:53:35 +08:00
|
|
|
digits = [str(digit) for digit in reversed(digits)]
|
|
|
|
while len(digits) <= abs(exponent):
|
|
|
|
digits.append('0')
|
|
|
|
digits.insert(-exponent, '.')
|
2019-10-31 10:05:26 +08:00
|
|
|
if sign and rounded_d:
|
2017-01-22 02:53:35 +08:00
|
|
|
digits.append('-')
|
|
|
|
number = ''.join(reversed(digits))
|
|
|
|
return mark_safe(formats.number_format(number, abs(p)))
|
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):
|
2017-01-25 04:36:36 +08:00
|
|
|
"""Escape an IRI value for use in a URL."""
|
2017-01-24 00:44:25 +08:00
|
|
|
return 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):
|
2017-01-25 04:36:36 +08:00
|
|
|
"""Display 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
|
2016-12-29 23:27:49 +08:00
|
|
|
width = str(len(str(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):
|
2017-01-25 04:36:36 +08:00
|
|
|
"""Convert 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
|
|
|
"""
|
2017-01-25 04:36:36 +08:00
|
|
|
Return the value turned into a list.
|
2007-11-04 10:05:56 +08:00
|
|
|
|
|
|
|
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
|
|
|
"""
|
2017-01-25 04:36:36 +08:00
|
|
|
Convert to ASCII. Convert spaces to hyphens. Remove characters that aren't
|
|
|
|
alphanumerics, underscores, or hyphens. Convert to lowercase. Also strip
|
|
|
|
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):
|
|
|
|
"""
|
2017-01-25 04:36:36 +08:00
|
|
|
Format the variable according to the arg, a string formatting specifier.
|
2007-11-04 10:05:56 +08:00
|
|
|
|
2018-08-02 00:55:53 +08:00
|
|
|
This specifier uses Python string formatting syntax, with the exception
|
|
|
|
that the leading "%" is dropped.
|
2005-08-10 23:40:14 +08:00
|
|
|
|
2018-09-23 09:30:38 +08:00
|
|
|
See https://docs.python.org/library/stdtypes.html#printf-style-string-formatting
|
2016-05-09 06:07:43 +08:00
|
|
|
for documentation of Python string formatting.
|
2005-07-13 09:25:57 +08:00
|
|
|
"""
|
2017-08-22 20:45:08 +08:00
|
|
|
if isinstance(value, tuple):
|
|
|
|
value = str(value)
|
2005-07-13 09:25:57 +08:00
|
|
|
try:
|
2016-12-29 23:27:49 +08:00
|
|
|
return ("%" + str(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):
|
2017-01-25 04:36:36 +08:00
|
|
|
"""Convert a string into titlecase."""
|
2020-05-11 04:03:39 +08:00
|
|
|
t = re.sub("([a-z])'([A-Z])", lambda m: m[0].lower(), value.title())
|
|
|
|
return re.sub(r'\d([A-Z])', lambda m: m[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):
|
2017-01-25 04:36:36 +08:00
|
|
|
"""Truncate a string after `arg` number of characters."""
|
2011-07-14 21:47:10 +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-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):
|
|
|
|
"""
|
2017-01-25 04:36:36 +08:00
|
|
|
Truncate HTML after `arg` number of chars.
|
|
|
|
Preserve newlines in the HTML.
|
2013-01-10 17:27:20 +08:00
|
|
|
"""
|
|
|
|
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):
|
|
|
|
"""
|
2017-01-25 04:36:36 +08:00
|
|
|
Truncate a string after `arg` number of words.
|
|
|
|
Remove newlines within the string.
|
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.
|
2018-08-21 21:28:51 +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):
|
|
|
|
"""
|
2017-01-25 04:36:36 +08:00
|
|
|
Truncate HTML after `arg` number of words.
|
|
|
|
Preserve newlines in the HTML.
|
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.
|
2018-08-21 21:28:51 +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):
|
2017-01-25 04:36:36 +08:00
|
|
|
"""Convert 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):
|
|
|
|
"""
|
2017-01-25 04:36:36 +08:00
|
|
|
Escape a value for use in a URL.
|
2010-09-14 07:01:34 +08:00
|
|
|
|
2017-01-25 04:36:36 +08:00
|
|
|
The ``safe`` parameter determines the characters which should not be
|
|
|
|
escaped by Python's quote() function. If not provided, use the default safe
|
|
|
|
characters (but an empty string can be provided when *all* characters
|
|
|
|
should be escaped).
|
2010-09-14 07:01:34 +08:00
|
|
|
"""
|
|
|
|
kwargs = {}
|
|
|
|
if safe is not None:
|
|
|
|
kwargs['safe'] = safe
|
2017-01-26 21:25:15 +08:00
|
|
|
return quote(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):
|
2017-01-25 04:36:36 +08:00
|
|
|
"""Convert 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
|
|
|
"""
|
2017-01-25 04:36:36 +08:00
|
|
|
Convert URLs into clickable links, truncating URLs to the given character
|
2007-11-04 10:05:56 +08:00
|
|
|
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):
|
2017-01-25 04:36:36 +08:00
|
|
|
"""Return 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):
|
2017-01-25 04:36:36 +08:00
|
|
|
"""Wrap words at `arg` line length."""
|
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):
|
2017-01-25 04:36:36 +08:00
|
|
|
"""Left-align the value in a field of a given width."""
|
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):
|
2017-01-25 04:36:36 +08:00
|
|
|
"""Right-align the value in a field of a given width."""
|
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):
|
2017-01-25 04:36:36 +08:00
|
|
|
"""Center 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):
|
2017-01-25 04:36:36 +08:00
|
|
|
"""Remove all values of arg from the given string."""
|
2007-11-14 20:58:53 +08:00
|
|
|
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):
|
2017-01-25 04:36:36 +08:00
|
|
|
"""Mark the value as a string that should be auto-escaped."""
|
2017-01-01 01:43:30 +08:00
|
|
|
return conditional_escape(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):
|
|
|
|
"""
|
2017-01-25 04:36:36 +08:00
|
|
|
Escape a string's HTML. Return a new string containing the escaped
|
2007-11-14 20:58:53 +08:00
|
|
|
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
|
|
|
"""
|
2017-01-25 04:36:36 +08:00
|
|
|
Replace line breaks in plain text with appropriate HTML; a single
|
2018-01-21 15:09:10 +08:00
|
|
|
newline becomes an HTML line break (``<br>``) and a new line
|
2007-09-15 05:46:38 +08:00
|
|
|
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
|
|
|
"""
|
2017-01-25 04:36:36 +08:00
|
|
|
Convert all newlines in a piece of plain text to HTML line breaks
|
2018-01-21 15:09:10 +08:00
|
|
|
(``<br>``).
|
2007-09-15 05:46:38 +08:00
|
|
|
"""
|
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)
|
2018-01-21 15:09:10 +08:00
|
|
|
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):
|
2017-01-25 04:36:36 +08:00
|
|
|
"""Mark the value as a string that should not be auto-escaped."""
|
2007-11-14 20:58:53 +08:00
|
|
|
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):
|
|
|
|
"""
|
2017-01-25 04:36:36 +08:00
|
|
|
A "safe" filter for sequences. Mark each element in the sequence,
|
|
|
|
individually, as safe, after converting them to strings. Return a list
|
2009-03-02 16:16:33 +08:00
|
|
|
with the results.
|
|
|
|
"""
|
2018-07-09 23:25:11 +08:00
|
|
|
return [mark_safe(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):
|
2017-01-25 04:36:36 +08:00
|
|
|
"""Strip 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):
|
|
|
|
"""
|
2017-01-25 04:36:36 +08:00
|
|
|
Given a list of dicts, return that list sorted by the property given in
|
2005-07-13 09:25:57 +08:00
|
|
|
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):
|
|
|
|
"""
|
2017-01-25 04:36:36 +08:00
|
|
|
Given a list of dicts, return that list sorted in reverse order by the
|
2005-07-13 09:25:57 +08:00
|
|
|
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):
|
2017-01-25 04:36:36 +08:00
|
|
|
"""Return 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):
|
2017-01-25 04:36:36 +08:00
|
|
|
"""Join a list with a string, like Python's ``str.join(list)``."""
|
2005-07-13 09:25:57 +08:00
|
|
|
try:
|
2017-10-11 17:09:25 +08:00
|
|
|
if autoescape:
|
|
|
|
value = [conditional_escape(v) for v in value]
|
2010-08-03 23:42:39 +08:00
|
|
|
data = conditional_escape(arg).join(value)
|
2017-10-11 17:09:25 +08:00
|
|
|
except TypeError: # Fail silently if arg isn't iterable.
|
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):
|
2017-01-25 04:36:36 +08:00
|
|
|
"""Return the last item in a list."""
|
2008-01-06 11:53:33 +08:00
|
|
|
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):
|
2017-01-25 04:36:36 +08:00
|
|
|
"""Return 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):
|
2017-01-25 04:36:36 +08:00
|
|
|
"""Return 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):
|
2017-01-25 04:36:36 +08:00
|
|
|
"""Return 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
|
|
|
"""
|
2017-01-25 04:36:36 +08:00
|
|
|
Return a slice of the list using the same syntax as Python's list slicing.
|
2005-07-13 09:25:57 +08:00
|
|
|
"""
|
|
|
|
try:
|
2006-01-02 02:37:33 +08:00
|
|
|
bits = []
|
2018-05-27 08:56:51 +08:00
|
|
|
for x in str(arg).split(':'):
|
2017-11-30 00:54:34 +08:00
|
|
|
if not x:
|
2006-01-02 02:37:33 +08:00
|
|
|
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
|
|
|
"""
|
2017-01-25 04:36:36 +08:00
|
|
|
Recursively take a self-nested list and return an HTML unordered list --
|
2005-07-13 09:25:57 +08:00
|
|
|
WITHOUT opening and closing <ul> tags.
|
|
|
|
|
2017-01-25 04:36:36 +08:00
|
|
|
Assume the list is in the proper format. For example, if ``var`` contains:
|
|
|
|
``['States', ['Kansas', ['Lawrence', 'Topeka'], 'Illinois']]``, then
|
|
|
|
``{{ var|unordered_list }}`` returns::
|
2005-07-13 09:25:57 +08:00
|
|
|
|
|
|
|
<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)
|
2017-09-07 20:16:21 +08:00
|
|
|
try:
|
2015-06-25 18:46:11 +08:00
|
|
|
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
|
2017-10-19 06:09:19 +08:00
|
|
|
if isinstance(next_item, (list, tuple, types.GeneratorType)):
|
2015-06-25 18:46:11 +08:00
|
|
|
try:
|
|
|
|
iter(next_item)
|
|
|
|
except TypeError:
|
|
|
|
pass
|
|
|
|
else:
|
|
|
|
yield item, next_item
|
|
|
|
item = next(item_iterator)
|
|
|
|
continue
|
|
|
|
yield item, None
|
|
|
|
item = next_item
|
2017-09-07 20:16:21 +08:00
|
|
|
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>' % (
|
2017-02-07 16:17:38 +08:00
|
|
|
indent, escaper(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):
|
2017-01-25 04:36:36 +08:00
|
|
|
"""Add 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):
|
|
|
|
"""
|
2017-01-25 04:36:36 +08:00
|
|
|
Given a whole number, return the requested digit of it, where 1 is the
|
|
|
|
right-most digit, 2 is the second-right-most digit, etc. Return the
|
2005-07-13 09:25:57 +08:00
|
|
|
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):
|
2017-01-25 04:36:36 +08:00
|
|
|
"""Format 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):
|
2017-01-25 04:36:36 +08:00
|
|
|
"""Format 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):
|
2017-01-25 04:36:36 +08:00
|
|
|
"""Format 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):
|
2017-01-25 04:36:36 +08:00
|
|
|
"""Format 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):
|
2017-01-25 04:36:36 +08:00
|
|
|
"""Return 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
|
|
|
"""
|
2017-01-25 04:36:36 +08:00
|
|
|
Given a string mapping values for true, false, and (optionally) None,
|
|
|
|
return 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:
|
2020-03-17 18:38:49 +08:00
|
|
|
# Translators: Please do not add spaces around commas.
|
2017-01-27 03:58:33 +08:00
|
|
|
arg = gettext('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
|
|
|
"""
|
2017-01-25 04:36:36 +08:00
|
|
|
Format 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:
|
2019-06-10 03:48:20 +08:00
|
|
|
bytes_ = int(bytes_)
|
2013-10-25 01:30:03 +08:00
|
|
|
except (TypeError, ValueError, UnicodeDecodeError):
|
2017-01-27 03:58:33 +08:00
|
|
|
value = ngettext("%(size)d byte", "%(size)d bytes", 0) % {'size': 0}
|
2013-05-18 19:58:45 +08:00
|
|
|
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:
|
2017-01-27 03:58:33 +08:00
|
|
|
value = ngettext("%(size)d byte", "%(size)d bytes", bytes_) % {'size': bytes_}
|
2015-10-26 07:05:03 +08:00
|
|
|
elif bytes_ < MB:
|
2017-01-27 03:58:33 +08:00
|
|
|
value = gettext("%s KB") % filesize_number_format(bytes_ / KB)
|
2015-10-26 07:05:03 +08:00
|
|
|
elif bytes_ < GB:
|
2017-01-27 03:58:33 +08:00
|
|
|
value = gettext("%s MB") % filesize_number_format(bytes_ / MB)
|
2015-10-26 07:05:03 +08:00
|
|
|
elif bytes_ < TB:
|
2017-01-27 03:58:33 +08:00
|
|
|
value = gettext("%s GB") % filesize_number_format(bytes_ / GB)
|
2015-10-26 07:05:03 +08:00
|
|
|
elif bytes_ < PB:
|
2017-01-27 03:58:33 +08:00
|
|
|
value = gettext("%s TB") % filesize_number_format(bytes_ / TB)
|
2013-05-18 19:58:45 +08:00
|
|
|
else:
|
2017-01-27 03:58:33 +08:00
|
|
|
value = gettext("%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
|
|
|
"""
|
2019-04-28 00:08:39 +08:00
|
|
|
Return a plural suffix if the value is not 1, '1', or an object of
|
|
|
|
length 1. By default, use 's' as the suffix:
|
2007-11-04 10:08:04 +08:00
|
|
|
|
2018-02-24 01:15:40 +08:00
|
|
|
* If value is 0, vote{{ value|pluralize }} display "votes".
|
|
|
|
* If value is 1, vote{{ value|pluralize }} display "vote".
|
|
|
|
* If value is 2, vote{{ value|pluralize }} display "votes".
|
2007-11-04 10:08:04 +08:00
|
|
|
|
2017-01-25 04:36:36 +08:00
|
|
|
If an argument is provided, use that string instead:
|
2007-11-04 10:08:04 +08:00
|
|
|
|
2018-02-24 01:15:40 +08:00
|
|
|
* If value is 0, class{{ value|pluralize:"es" }} display "classes".
|
|
|
|
* If value is 1, class{{ value|pluralize:"es" }} display "class".
|
|
|
|
* If value is 2, class{{ value|pluralize:"es" }} display "classes".
|
2007-11-04 10:08:04 +08:00
|
|
|
|
2017-01-25 04:36:36 +08:00
|
|
|
If the provided argument contains a comma, use the text before the comma
|
|
|
|
for the singular case and the text after the comma for the plural case:
|
2007-11-04 10:08:04 +08:00
|
|
|
|
2018-02-24 01:15:40 +08:00
|
|
|
* If value is 0, cand{{ value|pluralize:"y,ies" }} display "candies".
|
|
|
|
* If value is 1, cand{{ value|pluralize:"y,ies" }} display "candy".
|
|
|
|
* If value is 2, cand{{ value|pluralize:"y,ies" }} display "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:
|
2019-04-28 00:09:22 +08:00
|
|
|
return singular_suffix if float(value) == 1 else 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?
|
2017-09-07 20:16:21 +08:00
|
|
|
try:
|
2019-04-28 00:09:22 +08:00
|
|
|
return singular_suffix if len(value) == 1 else plural_suffix
|
2017-09-07 20:16:21 +08:00
|
|
|
except TypeError: # len() of unsized object.
|
|
|
|
pass
|
2019-04-28 00:09:22 +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("phone2numeric", is_safe=True)
|
2011-05-27 12:22:37 +08:00
|
|
|
def phone2numeric_filter(value):
|
2017-01-25 04:36:36 +08:00
|
|
|
"""Take 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:
|
2017-02-07 16:17:38 +08:00
|
|
|
return "Error in formatting: %s: %s" % (e.__class__.__name__, e)
|