" % self.filter_expression
def render(self, context):
- return self.filter_expression.resolve(context)
+ output = self.filter_expression.resolve(context)
+ if (context.autoescape and not isinstance(output, SafeData)) or isinstance(output, EscapeData):
+ return escape(output)
+ else:
+ return output
class DebugVariableNode(VariableNode):
def render(self, context):
try:
- return self.filter_expression.resolve(context)
+ output = self.filter_expression.resolve(context)
except TemplateSyntaxError, e:
if not hasattr(e, 'source'):
e.source = self.source
raise
+ if context.autoescape and not isinstance(output, SafeData):
+ return escape(output)
+ else:
+ return output
def generic_tag_compiler(params, defaults, name, node_class, parser, token):
"Returns a template.Node subclass."
diff --git a/django/template/context.py b/django/template/context.py
index 59650b05fe5c1ab862fa557cf7a4642fc24c9d0e..0f229ce2ec51166719e2b3547f69cc06659d62e8 100644
--- a/django/template/context.py
+++ b/django/template/context.py
@@ -9,6 +9,9 @@ class ContextPopException(Exception):
class Context(object):
"A stack container for variable context"
+
+ autoescape = False
+
def __init__(self, dict_=None):
dict_ = dict_ or {}
self.dicts = [dict_]
@@ -98,3 +101,4 @@ class RequestContext(Context):
processors = tuple(processors)
for processor in get_standard_processors() + processors:
self.update(processor(request))
+
diff --git a/django/template/defaultfilters.py b/django/template/defaultfilters.py
index 6779b9b5f2b16b2961818a6230ff6b1caf3bb6e4..8e24f67287a883fa3297fc11f2a5fd3f31fba822 100644
--- a/django/template/defaultfilters.py
+++ b/django/template/defaultfilters.py
@@ -4,6 +4,7 @@ from django.template import resolve_variable, Library
from django.conf import settings
from django.utils.translation import ugettext, ungettext
from django.utils.encoding import force_unicode, smart_str, iri_to_uri
+from django.utils.safestring import mark_safe, SafeData
import re
import random as random_module
@@ -39,17 +40,20 @@ def addslashes(value):
"Adds slashes - useful for passing strings to JavaScript, for example."
return value.replace('\\', '\\\\').replace('"', '\\"').replace("'", "\\'")
addslashes = stringfilter(addslashes)
+addslashes.is_safe = True
def capfirst(value):
"Capitalizes the first character of the value"
return value and value[0].upper() + value[1:]
capfirst = stringfilter(capfirst)
+capfirst.is_safe = True
def fix_ampersands(value):
"Replaces ampersands with ``&`` entities"
from django.utils.html import fix_ampersands
return fix_ampersands(value)
fix_ampersands = stringfilter(fix_ampersands)
+fix_ampersands.is_safe = True
def floatformat(text, arg=-1):
"""
@@ -83,28 +87,36 @@ def floatformat(text, arg=-1):
return u'%d' % int(f)
else:
formatstr = u'%%.%df' % abs(d)
- return formatstr % f
+ return mark_safe(formatstr % f)
+floatformat.is_safe = True
def iriencode(value):
"Escapes an IRI value for use in a URL"
return force_unicode(iri_to_uri(value))
iriencode = stringfilter(iriencode)
-def linenumbers(value):
+def linenumbers(value, autoescape = None):
"Displays text with line numbers"
from django.utils.html import escape
lines = value.split(u'\n')
# Find the maximum width of the line count, for use with zero padding string format command
width = unicode(len(unicode(len(lines))))
- for i, line in enumerate(lines):
- lines[i] = (u"%0" + width + u"d. %s") % (i + 1, escape(line))
- return u'\n'.join(lines)
+ if not autoescape or isinstance(value, SafeData):
+ for i, line in enumerate(lines):
+ lines[i] = (u"%0" + width + u"d. %s") % (i + 1, line)
+ else:
+ for i, line in enumerate(lines):
+ lines[i] = (u"%0" + width + u"d. %s") % (i + 1, escape(line))
+ return mark_safe(u'\n'.join(lines))
linenumbers = stringfilter(linenumbers)
+linenumbers.is_safe = True
+linenumbers.needs_autoescape = True
def lower(value):
"Converts a string into all lowercase"
return value.lower()
lower = stringfilter(lower)
+lower.is_safe = True
def make_list(value):
"""
@@ -113,6 +125,7 @@ def make_list(value):
"""
return list(value)
make_list = stringfilter(make_list)
+make_list.is_safe = False
def slugify(value):
"""
@@ -122,8 +135,9 @@ def slugify(value):
import unicodedata
value = unicodedata.normalize('NFKD', value).encode('ascii', 'ignore')
value = unicode(re.sub('[^\w\s-]', '', value).strip().lower())
- return re.sub('[-\s]+', '-', value)
+ return mark_safe(re.sub('[-\s]+', '-', value))
slugify = stringfilter(slugify)
+slugify.is_safe = True
def stringformat(value, arg):
"""
@@ -138,11 +152,13 @@ def stringformat(value, arg):
return (u"%" + unicode(arg)) % value
except (ValueError, TypeError):
return u""
+stringformat.is_safe = True
def title(value):
"Converts a string into titlecase"
return re.sub("([a-z])'([A-Z])", lambda m: m.group(0).lower(), value.title())
title = stringfilter(title)
+title.is_safe = False
def truncatewords(value, arg):
"""
@@ -157,6 +173,7 @@ def truncatewords(value, arg):
return value # Fail silently.
return truncate_words(value, length)
truncatewords = stringfilter(truncatewords)
+truncatewords.is_safe = True
def truncatewords_html(value, arg):
"""
@@ -176,18 +193,21 @@ def upper(value):
"Converts a string into all uppercase"
return value.upper()
upper = stringfilter(upper)
+upper.is_safe = False
def urlencode(value):
"Escapes a value for use in a URL"
from django.utils.http import urlquote
return urlquote(value)
urlencode = stringfilter(urlencode)
+urlencode.is_safe = False
def urlize(value):
"Converts URLs in plain text into clickable links"
from django.utils.html import urlize
- return urlize(value, nofollow=True)
+ return mark_safe(urlize(value, nofollow=True))
urlize = stringfilter(urlize)
+urlize.is_safe = True
def urlizetrunc(value, limit):
"""
@@ -197,13 +217,15 @@ def urlizetrunc(value, limit):
Argument: Length to truncate URLs to.
"""
from django.utils.html import urlize
- return urlize(value, trim_url_limit=int(limit), nofollow=True)
+ return mark_safe(urlize(value, trim_url_limit=int(limit), nofollow=True))
urlizetrunc = stringfilter(urlizetrunc)
+urlize.is_safe = True
def wordcount(value):
"Returns the number of words"
return len(value.split())
wordcount = stringfilter(wordcount)
+wordcount.is_safe = False
def wordwrap(value, arg):
"""
@@ -214,6 +236,7 @@ def wordwrap(value, arg):
from django.utils.text import wrap
return wrap(value, int(arg))
wordwrap = stringfilter(wordwrap)
+wordwrap.is_safe = True
def ljust(value, arg):
"""
@@ -223,6 +246,7 @@ def ljust(value, arg):
"""
return value.ljust(int(arg))
ljust = stringfilter(ljust)
+ljust.is_safe = True
def rjust(value, arg):
"""
@@ -232,37 +256,67 @@ def rjust(value, arg):
"""
return value.rjust(int(arg))
rjust = stringfilter(rjust)
+rjust.is_safe = True
def center(value, arg):
"Centers the value in a field of a given width"
return value.center(int(arg))
center = stringfilter(center)
+center.is_safe = True
def cut(value, arg):
"Removes all values of arg from the given string"
return value.replace(arg, u'')
cut = stringfilter(cut)
+cut.is_safe = False
###################
# HTML STRINGS #
###################
def escape(value):
- "Escapes a string's HTML"
+ "Marks the value as a string that should not be auto-escaped."
+ from django.utils.safestring import mark_for_escaping
+ return mark_for_escaping(value)
+escape = stringfilter(escape)
+escape.is_safe = True
+
+def force_escape(value):
+ """Escapes a string's HTML. This returns a new string containing the escaped
+ characters (as opposed to "escape", which marks the content for later
+ possible escaping)."""
from django.utils.html import escape
- return escape(value)
+ return mark_safe(escape(value))
escape = stringfilter(escape)
+force_escape.is_safe = True
-def linebreaks(value):
+def linebreaks(value, autoescape = None):
"Converts newlines into and
s"
from django.utils.html import linebreaks
- return linebreaks(value)
+ autoescape = autoescape and not isinstance(value, SafeData)
+ return mark_safe(linebreaks(value, autoescape))
linebreaks = stringfilter(linebreaks)
+linebreaks.is_safe = True
+linebreaks.needs_autoescape = True
-def linebreaksbr(value):
+def linebreaksbr(value, autoescape = None):
"Converts newlines into
s"
- return value.replace('\n', '
')
+ if autoescape and not isinstance(value, SafeData):
+ from django.utils.html import escape
+ data = escape(value)
+ else:
+ data = value
+ return mark_safe(data.replace('\n', '
'))
linebreaksbr = stringfilter(linebreaksbr)
+linebreaksbr.is_safe = True
+linebreaksbr.needs_autoescape = True
+
+def safe(value):
+ "Marks the value as a string that should not be auto-escaped."
+ from django.utils.safestring import mark_safe
+ return mark_safe(value)
+safe = stringfilter(safe)
+safe.is_safe = True
def removetags(value, tags):
"Removes a space separated list of [X]HTML tags from the output"
@@ -274,12 +328,14 @@ def removetags(value, tags):
value = endtag_re.sub(u'', value)
return value
removetags = stringfilter(removetags)
+removetags.is_safe = True
def striptags(value):
"Strips all [X]HTML tags"
from django.utils.html import strip_tags
return strip_tags(value)
striptags = stringfilter(striptags)
+striptags.is_safe = True
###################
# LISTS #
@@ -293,6 +349,7 @@ def dictsort(value, arg):
decorated = [(resolve_variable(u'var.' + arg, {u'var' : item}), item) for item in value]
decorated.sort()
return [item[1] for item in decorated]
+dictsort.is_safe = False
def dictsortreversed(value, arg):
"""
@@ -303,6 +360,7 @@ def dictsortreversed(value, arg):
decorated.sort()
decorated.reverse()
return [item[1] for item in decorated]
+dictsortreversed.is_safe = False
def first(value):
"Returns the first item in a list"
@@ -310,25 +368,35 @@ def first(value):
return value[0]
except IndexError:
return u''
+first.is_safe = True
def join(value, arg):
"Joins a list with a string, like Python's ``str.join(list)``"
try:
- return arg.join(map(force_unicode, value))
+ data = arg.join(map(force_unicode, value))
except AttributeError: # fail silently but nicely
return value
+ safe_args = reduce(lambda lhs, rhs: lhs and isinstance(rhs, SafeData), value, True)
+ if safe_args:
+ return mark_safe(data)
+ else:
+ return data
+join.is_safe = True
def length(value):
"Returns the length of the value - useful for lists"
return len(value)
+length.is_safe = False
def length_is(value, arg):
"Returns a boolean of whether the value's length is the argument"
return len(value) == int(arg)
+length.is_safe = False
def random(value):
"Returns a random item from the list"
return random_module.choice(value)
+length.is_safe = True
def slice_(value, arg):
"""
@@ -349,8 +417,9 @@ def slice_(value, arg):
except (ValueError, TypeError):
return value # Fail silently.
+slice_.is_safe = True
-def unordered_list(value):
+def unordered_list(value, autoescape = None):
"""
Recursively takes a self-nested list and returns an HTML unordered list --
WITHOUT opening and closing
tags.
@@ -371,14 +440,22 @@ def unordered_list(value):