Switched {% cycle %} and {% firstof %} tags to auto-escape their variables per deprecation timeline.
refs #17906.
This commit is contained in:
parent
274048351a
commit
1ea44a3abd
|
@ -17,7 +17,6 @@ from django.template.base import (Node, NodeList, Template, Context, Library,
|
||||||
render_value_in_context)
|
render_value_in_context)
|
||||||
from django.template.smartif import IfParser, Literal
|
from django.template.smartif import IfParser, Literal
|
||||||
from django.template.defaultfilters import date
|
from django.template.defaultfilters import date
|
||||||
from django.utils.deprecation import RemovedInDjango18Warning
|
|
||||||
from django.utils.encoding import force_text, smart_text
|
from django.utils.encoding import force_text, smart_text
|
||||||
from django.utils.safestring import mark_safe
|
from django.utils.safestring import mark_safe
|
||||||
from django.utils.html import format_html
|
from django.utils.html import format_html
|
||||||
|
@ -65,11 +64,10 @@ class CsrfTokenNode(Node):
|
||||||
|
|
||||||
|
|
||||||
class CycleNode(Node):
|
class CycleNode(Node):
|
||||||
def __init__(self, cyclevars, variable_name=None, silent=False, escape=False):
|
def __init__(self, cyclevars, variable_name=None, silent=False):
|
||||||
self.cyclevars = cyclevars
|
self.cyclevars = cyclevars
|
||||||
self.variable_name = variable_name
|
self.variable_name = variable_name
|
||||||
self.silent = silent
|
self.silent = silent
|
||||||
self.escape = escape # only while the "future" version exists
|
|
||||||
|
|
||||||
def render(self, context):
|
def render(self, context):
|
||||||
if self not in context.render_context:
|
if self not in context.render_context:
|
||||||
|
@ -81,8 +79,6 @@ class CycleNode(Node):
|
||||||
context[self.variable_name] = value
|
context[self.variable_name] = value
|
||||||
if self.silent:
|
if self.silent:
|
||||||
return ''
|
return ''
|
||||||
if not self.escape:
|
|
||||||
value = mark_safe(value)
|
|
||||||
return render_value_in_context(value, context)
|
return render_value_in_context(value, context)
|
||||||
|
|
||||||
|
|
||||||
|
@ -107,16 +103,13 @@ class FilterNode(Node):
|
||||||
|
|
||||||
|
|
||||||
class FirstOfNode(Node):
|
class FirstOfNode(Node):
|
||||||
def __init__(self, variables, escape=False):
|
def __init__(self, variables):
|
||||||
self.vars = variables
|
self.vars = variables
|
||||||
self.escape = escape # only while the "future" version exists
|
|
||||||
|
|
||||||
def render(self, context):
|
def render(self, context):
|
||||||
for var in self.vars:
|
for var in self.vars:
|
||||||
value = var.resolve(context, True)
|
value = var.resolve(context, True)
|
||||||
if value:
|
if value:
|
||||||
if not self.escape:
|
|
||||||
value = mark_safe(value)
|
|
||||||
return render_value_in_context(value, context)
|
return render_value_in_context(value, context)
|
||||||
return ''
|
return ''
|
||||||
|
|
||||||
|
@ -554,7 +547,7 @@ def comment(parser, token):
|
||||||
|
|
||||||
|
|
||||||
@register.tag
|
@register.tag
|
||||||
def cycle(parser, token, escape=False):
|
def cycle(parser, token):
|
||||||
"""
|
"""
|
||||||
Cycles among the given strings each time this tag is encountered.
|
Cycles among the given strings each time this tag is encountered.
|
||||||
|
|
||||||
|
@ -587,13 +580,6 @@ def cycle(parser, token, escape=False):
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
|
||||||
"""
|
"""
|
||||||
if not escape:
|
|
||||||
warnings.warn(
|
|
||||||
"'The `cycle` template tag is changing to escape its arguments; "
|
|
||||||
"the non-autoescaping version is deprecated. Load it "
|
|
||||||
"from the `future` tag library to start using the new behavior.",
|
|
||||||
RemovedInDjango18Warning, stacklevel=2)
|
|
||||||
|
|
||||||
# Note: This returns the exact same node on each {% cycle name %} call;
|
# Note: This returns the exact same node on each {% cycle name %} call;
|
||||||
# that is, the node object returned from {% cycle a b c as name %} and the
|
# that is, the node object returned from {% cycle a b c as name %} and the
|
||||||
# one returned from {% cycle name %} are the exact same object. This
|
# one returned from {% cycle name %} are the exact same object. This
|
||||||
|
@ -640,13 +626,13 @@ def cycle(parser, token, escape=False):
|
||||||
if as_form:
|
if as_form:
|
||||||
name = args[-1]
|
name = args[-1]
|
||||||
values = [parser.compile_filter(arg) for arg in args[1:-2]]
|
values = [parser.compile_filter(arg) for arg in args[1:-2]]
|
||||||
node = CycleNode(values, name, silent=silent, escape=escape)
|
node = CycleNode(values, name, silent=silent)
|
||||||
if not hasattr(parser, '_namedCycleNodes'):
|
if not hasattr(parser, '_namedCycleNodes'):
|
||||||
parser._namedCycleNodes = {}
|
parser._namedCycleNodes = {}
|
||||||
parser._namedCycleNodes[name] = node
|
parser._namedCycleNodes[name] = node
|
||||||
else:
|
else:
|
||||||
values = [parser.compile_filter(arg) for arg in args[1:]]
|
values = [parser.compile_filter(arg) for arg in args[1:]]
|
||||||
node = CycleNode(values, escape=escape)
|
node = CycleNode(values)
|
||||||
return node
|
return node
|
||||||
|
|
||||||
|
|
||||||
|
@ -701,7 +687,7 @@ def do_filter(parser, token):
|
||||||
|
|
||||||
|
|
||||||
@register.tag
|
@register.tag
|
||||||
def firstof(parser, token, escape=False):
|
def firstof(parser, token):
|
||||||
"""
|
"""
|
||||||
Outputs the first variable passed that is not False, without escaping.
|
Outputs the first variable passed that is not False, without escaping.
|
||||||
|
|
||||||
|
@ -735,17 +721,10 @@ def firstof(parser, token, escape=False):
|
||||||
{% endfilter %}
|
{% endfilter %}
|
||||||
|
|
||||||
"""
|
"""
|
||||||
if not escape:
|
|
||||||
warnings.warn(
|
|
||||||
"'The `firstof` template tag is changing to escape its arguments; "
|
|
||||||
"the non-autoescaping version is deprecated. Load it "
|
|
||||||
"from the `future` tag library to start using the new behavior.",
|
|
||||||
RemovedInDjango18Warning, stacklevel=2)
|
|
||||||
|
|
||||||
bits = token.split_contents()[1:]
|
bits = token.split_contents()[1:]
|
||||||
if len(bits) < 1:
|
if len(bits) < 1:
|
||||||
raise TemplateSyntaxError("'firstof' statement requires at least one argument")
|
raise TemplateSyntaxError("'firstof' statement requires at least one argument")
|
||||||
return FirstOfNode([parser.compile_filter(bit) for bit in bits], escape=escape)
|
return FirstOfNode([parser.compile_filter(bit) for bit in bits])
|
||||||
|
|
||||||
|
|
||||||
@register.tag('for')
|
@register.tag('for')
|
||||||
|
|
|
@ -29,6 +29,8 @@ def url(parser, token):
|
||||||
def cycle(parser, token):
|
def cycle(parser, token):
|
||||||
"""
|
"""
|
||||||
This is the future version of `cycle` with auto-escaping.
|
This is the future version of `cycle` with auto-escaping.
|
||||||
|
The deprecation is now complete and this version is no different
|
||||||
|
from the non-future version so this can be deprecated (#22306)
|
||||||
|
|
||||||
By default all strings are escaped.
|
By default all strings are escaped.
|
||||||
|
|
||||||
|
@ -42,13 +44,15 @@ def cycle(parser, token):
|
||||||
|
|
||||||
{% cycle var1 var2|safe var3|safe as somecycle %}
|
{% cycle var1 var2|safe var3|safe as somecycle %}
|
||||||
"""
|
"""
|
||||||
return defaulttags.cycle(parser, token, escape=True)
|
return defaulttags.cycle(parser, token)
|
||||||
|
|
||||||
|
|
||||||
@register.tag
|
@register.tag
|
||||||
def firstof(parser, token):
|
def firstof(parser, token):
|
||||||
"""
|
"""
|
||||||
This is the future version of `firstof` with auto-escaping.
|
This is the future version of `firstof` with auto-escaping.
|
||||||
|
The deprecation is now complete and this version is no different
|
||||||
|
from the non-future version so this can be deprecated (#22306)
|
||||||
|
|
||||||
This is equivalent to::
|
This is equivalent to::
|
||||||
|
|
||||||
|
@ -71,4 +75,4 @@ def firstof(parser, token):
|
||||||
{% firstof var1 var2|safe var3 "<strong>fallback value</strong>"|safe %}
|
{% firstof var1 var2|safe var3 "<strong>fallback value</strong>"|safe %}
|
||||||
|
|
||||||
"""
|
"""
|
||||||
return defaulttags.firstof(parser, token, escape=True)
|
return defaulttags.firstof(parser, token)
|
||||||
|
|
|
@ -102,13 +102,11 @@ this::
|
||||||
</tr>
|
</tr>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
|
||||||
Note that the variables included in the cycle will not be escaped. Any HTML or
|
Variables included in the cycle will be escaped. You can disable auto-escaping
|
||||||
Javascript code contained in the printed variable will be rendered as-is, which
|
with::
|
||||||
could potentially lead to security issues. So either make sure that you trust
|
|
||||||
their values or use explicit escaping like this::
|
|
||||||
|
|
||||||
{% for o in some_list %}
|
{% for o in some_list %}
|
||||||
<tr class="{% filter force_escape %}{% cycle rowvalue1 rowvalue2 %}{% endfilter %}">
|
<tr class="{% autoescape off %}{% cycle rowvalue1 rowvalue2 %}{% endautoescape
|
||||||
...
|
...
|
||||||
</tr>
|
</tr>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
@ -196,21 +194,6 @@ In this syntax, each value gets interpreted as a literal string, and there's no
|
||||||
way to specify variable values. Or literal commas. Or spaces. Did we mention
|
way to specify variable values. Or literal commas. Or spaces. Did we mention
|
||||||
you shouldn't use this syntax in any new projects?
|
you shouldn't use this syntax in any new projects?
|
||||||
|
|
||||||
.. versionchanged:: 1.6
|
|
||||||
|
|
||||||
To improve safety, future versions of ``cycle`` will automatically escape
|
|
||||||
their output. You're encouraged to activate this behavior by loading
|
|
||||||
``cycle`` from the ``future`` template library::
|
|
||||||
|
|
||||||
{% load cycle from future %}
|
|
||||||
|
|
||||||
When using the ``future`` version, you can disable auto-escaping with::
|
|
||||||
|
|
||||||
{% for o in some_list %}
|
|
||||||
<tr class="{% autoescape off %}{% cycle rowvalue1 rowvalue2 %}{% endautoescape %}">
|
|
||||||
...
|
|
||||||
</tr>
|
|
||||||
{% endfor %}
|
|
||||||
|
|
||||||
.. templatetag:: debug
|
.. templatetag:: debug
|
||||||
|
|
||||||
|
@ -268,10 +251,8 @@ Sample usage::
|
||||||
firstof
|
firstof
|
||||||
^^^^^^^
|
^^^^^^^
|
||||||
|
|
||||||
Outputs the first argument variable that is not False. This tag does *not*
|
Outputs the first argument variable that is not ``False``. Outputs nothing if
|
||||||
auto-escape variable values.
|
all the passed variables are ``False``.
|
||||||
|
|
||||||
Outputs nothing if all the passed variables are False.
|
|
||||||
|
|
||||||
Sample usage::
|
Sample usage::
|
||||||
|
|
||||||
|
@ -292,30 +273,13 @@ passed variables are False::
|
||||||
|
|
||||||
{% firstof var1 var2 var3 "fallback value" %}
|
{% firstof var1 var2 var3 "fallback value" %}
|
||||||
|
|
||||||
Note that currently the variables included in the firstof tag will not be
|
This tag auto-escapes variable values. You can disable auto-escaping with::
|
||||||
escaped. Any HTML or Javascript code contained in the printed variable will be
|
|
||||||
rendered as-is, which could potentially lead to security issues. If you need
|
|
||||||
to escape the variables in the firstof tag, you must do so explicitly::
|
|
||||||
|
|
||||||
{% filter force_escape %}
|
|
||||||
{% firstof var1 var2 var3 "fallback value" %}
|
|
||||||
{% endfilter %}
|
|
||||||
|
|
||||||
.. versionchanged:: 1.6
|
|
||||||
|
|
||||||
To improve safety, future versions of ``firstof`` will automatically escape
|
|
||||||
their output. You're encouraged to activate this behavior by loading
|
|
||||||
``firstof`` from the ``future`` template library::
|
|
||||||
|
|
||||||
{% load firstof from future %}
|
|
||||||
|
|
||||||
When using the ``future`` version, you can disable auto-escaping with::
|
|
||||||
|
|
||||||
{% autoescape off %}
|
{% autoescape off %}
|
||||||
{% firstof var1 var2 var3 "<strong>fallback value</strong>" %}
|
{% firstof var1 var2 var3 "<strong>fallback value</strong>" %}
|
||||||
{% endautoescape %}
|
{% endautoescape %}
|
||||||
|
|
||||||
Or if only some variables should be escaped, you can use::
|
Or if only some variables should be escaped, you can use::
|
||||||
|
|
||||||
{% firstof var1 var2|safe var3 "<strong>fallback value</strong>"|safe %}
|
{% firstof var1 var2|safe var3 "<strong>fallback value</strong>"|safe %}
|
||||||
|
|
||||||
|
|
|
@ -883,13 +883,13 @@ class TemplateTests(TestCase):
|
||||||
'cycle17': ("{% cycle 'a' 'b' 'c' as abc silent %}{% cycle abc %}{% cycle abc %}{% cycle abc %}{% cycle abc %}", {}, ""),
|
'cycle17': ("{% cycle 'a' 'b' 'c' as abc silent %}{% cycle abc %}{% cycle abc %}{% cycle abc %}{% cycle abc %}", {}, ""),
|
||||||
'cycle18': ("{% cycle 'a' 'b' 'c' as foo invalid_flag %}", {}, template.TemplateSyntaxError),
|
'cycle18': ("{% cycle 'a' 'b' 'c' as foo invalid_flag %}", {}, template.TemplateSyntaxError),
|
||||||
'cycle19': ("{% cycle 'a' 'b' as silent %}{% cycle silent %}", {}, "ab"),
|
'cycle19': ("{% cycle 'a' 'b' as silent %}{% cycle silent %}", {}, "ab"),
|
||||||
'cycle20': ("{% cycle one two as foo %} & {% cycle foo %}", {'one': 'A & B', 'two': 'C & D'}, "A & B & C & D"),
|
'cycle20': ("{% cycle one two as foo %} & {% cycle foo %}", {'one': 'A & B', 'two': 'C & D'}, "A & B & C & D"),
|
||||||
'cycle21': ("{% filter force_escape %}{% cycle one two as foo %} & {% cycle foo %}{% endfilter %}", {'one': 'A & B', 'two': 'C & D'}, "A & B & C & D"),
|
'cycle21': ("{% filter force_escape %}{% cycle one two as foo %} & {% cycle foo %}{% endfilter %}", {'one': 'A & B', 'two': 'C & D'}, "A &amp; B & C &amp; D"),
|
||||||
'cycle22': ("{% for x in values %}{% cycle 'a' 'b' 'c' as abc silent %}{{ x }}{% endfor %}", {'values': [1, 2, 3, 4]}, "1234"),
|
'cycle22': ("{% for x in values %}{% cycle 'a' 'b' 'c' as abc silent %}{{ x }}{% endfor %}", {'values': [1, 2, 3, 4]}, "1234"),
|
||||||
'cycle23': ("{% for x in values %}{% cycle 'a' 'b' 'c' as abc silent %}{{ abc }}{{ x }}{% endfor %}", {'values': [1, 2, 3, 4]}, "a1b2c3a4"),
|
'cycle23': ("{% for x in values %}{% cycle 'a' 'b' 'c' as abc silent %}{{ abc }}{{ x }}{% endfor %}", {'values': [1, 2, 3, 4]}, "a1b2c3a4"),
|
||||||
'included-cycle': ('{{ abc }}', {'abc': 'xxx'}, 'xxx'),
|
'included-cycle': ('{{ abc }}', {'abc': 'xxx'}, 'xxx'),
|
||||||
'cycle24': ("{% for x in values %}{% cycle 'a' 'b' 'c' as abc silent %}{% include 'included-cycle' %}{% endfor %}", {'values': [1, 2, 3, 4]}, "abca"),
|
'cycle24': ("{% for x in values %}{% cycle 'a' 'b' 'c' as abc silent %}{% include 'included-cycle' %}{% endfor %}", {'values': [1, 2, 3, 4]}, "abca"),
|
||||||
'cycle25': ('{% cycle a as abc %}', {'a': '<'}, '<'),
|
'cycle25': ('{% cycle a as abc %}', {'a': '<'}, '<'),
|
||||||
|
|
||||||
'cycle26': ('{% load cycle from future %}{% cycle a b as ab %}{% cycle ab %}', {'a': '<', 'b': '>'}, '<>'),
|
'cycle26': ('{% load cycle from future %}{% cycle a b as ab %}{% cycle ab %}', {'a': '<', 'b': '>'}, '<>'),
|
||||||
'cycle27': ('{% load cycle from future %}{% autoescape off %}{% cycle a b as ab %}{% cycle ab %}{% endautoescape %}', {'a': '<', 'b': '>'}, '<>'),
|
'cycle27': ('{% load cycle from future %}{% autoescape off %}{% cycle a b as ab %}{% cycle ab %}{% endautoescape %}', {'a': '<', 'b': '>'}, '<>'),
|
||||||
|
@ -929,7 +929,7 @@ class TemplateTests(TestCase):
|
||||||
'firstof07': ('{% firstof a b "c" %}', {'a': 0}, 'c'),
|
'firstof07': ('{% firstof a b "c" %}', {'a': 0}, 'c'),
|
||||||
'firstof08': ('{% firstof a b "c and d" %}', {'a': 0, 'b': 0}, 'c and d'),
|
'firstof08': ('{% firstof a b "c and d" %}', {'a': 0, 'b': 0}, 'c and d'),
|
||||||
'firstof09': ('{% firstof %}', {}, template.TemplateSyntaxError),
|
'firstof09': ('{% firstof %}', {}, template.TemplateSyntaxError),
|
||||||
'firstof10': ('{% firstof a %}', {'a': '<'}, '<'),
|
'firstof10': ('{% firstof a %}', {'a': '<'}, '<'),
|
||||||
|
|
||||||
'firstof11': ('{% load firstof from future %}{% firstof a b %}', {'a': '<', 'b': '>'}, '<'),
|
'firstof11': ('{% load firstof from future %}{% firstof a b %}', {'a': '<', 'b': '>'}, '<'),
|
||||||
'firstof12': ('{% load firstof from future %}{% firstof a b %}', {'a': '', 'b': '>'}, '>'),
|
'firstof12': ('{% load firstof from future %}{% firstof a b %}', {'a': '', 'b': '>'}, '>'),
|
||||||
|
|
Loading…
Reference in New Issue