From 1ea44a3abd4e58777247a095afd03dd01efdef55 Mon Sep 17 00:00:00 2001 From: Tim Graham Date: Fri, 21 Mar 2014 13:17:10 -0400 Subject: [PATCH] Switched {% cycle %} and {% firstof %} tags to auto-escape their variables per deprecation timeline. refs #17906. --- django/template/defaulttags.py | 35 ++++---------------- django/templatetags/future.py | 8 +++-- docs/ref/templates/builtins.txt | 58 +++++++-------------------------- tests/template_tests/tests.py | 8 ++--- 4 files changed, 28 insertions(+), 81 deletions(-) diff --git a/django/template/defaulttags.py b/django/template/defaulttags.py index c61851bbd1..76663f01f8 100644 --- a/django/template/defaulttags.py +++ b/django/template/defaulttags.py @@ -17,7 +17,6 @@ from django.template.base import (Node, NodeList, Template, Context, Library, render_value_in_context) from django.template.smartif import IfParser, Literal from django.template.defaultfilters import date -from django.utils.deprecation import RemovedInDjango18Warning from django.utils.encoding import force_text, smart_text from django.utils.safestring import mark_safe from django.utils.html import format_html @@ -65,11 +64,10 @@ class CsrfTokenNode(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.variable_name = variable_name self.silent = silent - self.escape = escape # only while the "future" version exists def render(self, context): if self not in context.render_context: @@ -81,8 +79,6 @@ class CycleNode(Node): context[self.variable_name] = value if self.silent: return '' - if not self.escape: - value = mark_safe(value) return render_value_in_context(value, context) @@ -107,16 +103,13 @@ class FilterNode(Node): class FirstOfNode(Node): - def __init__(self, variables, escape=False): + def __init__(self, variables): self.vars = variables - self.escape = escape # only while the "future" version exists def render(self, context): for var in self.vars: value = var.resolve(context, True) if value: - if not self.escape: - value = mark_safe(value) return render_value_in_context(value, context) return '' @@ -554,7 +547,7 @@ def comment(parser, token): @register.tag -def cycle(parser, token, escape=False): +def cycle(parser, token): """ Cycles among the given strings each time this tag is encountered. @@ -587,13 +580,6 @@ def cycle(parser, token, escape=False): {% 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; # 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 @@ -640,13 +626,13 @@ def cycle(parser, token, escape=False): if as_form: name = args[-1] 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'): parser._namedCycleNodes = {} parser._namedCycleNodes[name] = node else: values = [parser.compile_filter(arg) for arg in args[1:]] - node = CycleNode(values, escape=escape) + node = CycleNode(values) return node @@ -701,7 +687,7 @@ def do_filter(parser, token): @register.tag -def firstof(parser, token, escape=False): +def firstof(parser, token): """ Outputs the first variable passed that is not False, without escaping. @@ -735,17 +721,10 @@ def firstof(parser, token, escape=False): {% 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:] if len(bits) < 1: 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') diff --git a/django/templatetags/future.py b/django/templatetags/future.py index f10d0854bf..4bd64e8477 100644 --- a/django/templatetags/future.py +++ b/django/templatetags/future.py @@ -29,6 +29,8 @@ def url(parser, token): def cycle(parser, token): """ 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. @@ -42,13 +44,15 @@ def cycle(parser, token): {% cycle var1 var2|safe var3|safe as somecycle %} """ - return defaulttags.cycle(parser, token, escape=True) + return defaulttags.cycle(parser, token) @register.tag def firstof(parser, token): """ 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:: @@ -71,4 +75,4 @@ def firstof(parser, token): {% firstof var1 var2|safe var3 "fallback value"|safe %} """ - return defaulttags.firstof(parser, token, escape=True) + return defaulttags.firstof(parser, token) diff --git a/docs/ref/templates/builtins.txt b/docs/ref/templates/builtins.txt index 4f8102c003..a73ba87d75 100644 --- a/docs/ref/templates/builtins.txt +++ b/docs/ref/templates/builtins.txt @@ -102,13 +102,11 @@ this:: {% endfor %} -Note that the variables included in the cycle will not be escaped. Any HTML or -Javascript code contained in the printed variable will be rendered as-is, which -could potentially lead to security issues. So either make sure that you trust -their values or use explicit escaping like this:: +Variables included in the cycle will be escaped. You can disable auto-escaping +with:: {% for o in some_list %} - + - ... - - {% endfor %} .. templatetag:: debug @@ -268,10 +251,8 @@ Sample usage:: firstof ^^^^^^^ -Outputs the first argument variable that is not False. This tag does *not* -auto-escape variable values. - -Outputs nothing if all the passed variables are False. +Outputs the first argument variable that is not ``False``. Outputs nothing if +all the passed variables are ``False``. Sample usage:: @@ -292,32 +273,15 @@ passed variables are False:: {% firstof var1 var2 var3 "fallback value" %} -Note that currently the variables included in the firstof tag will not be -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:: +This tag auto-escapes variable values. You can disable auto-escaping with:: - {% filter force_escape %} - {% firstof var1 var2 var3 "fallback value" %} - {% endfilter %} + {% autoescape off %} + {% firstof var1 var2 var3 "fallback value" %} + {% endautoescape %} -.. versionchanged:: 1.6 +Or if only some variables should be escaped, you can use:: - 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 %} - {% firstof var1 var2 var3 "fallback value" %} - {% endautoescape %} - - Or if only some variables should be escaped, you can use:: - - {% firstof var1 var2|safe var3 "fallback value"|safe %} + {% firstof var1 var2|safe var3 "fallback value"|safe %} .. templatetag:: for diff --git a/tests/template_tests/tests.py b/tests/template_tests/tests.py index 0f060d9a2a..5020ccd579 100644 --- a/tests/template_tests/tests.py +++ b/tests/template_tests/tests.py @@ -883,13 +883,13 @@ class TemplateTests(TestCase): '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), '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"), - 'cycle21': ("{% filter force_escape %}{% cycle one two as foo %} & {% cycle foo %}{% endfilter %}", {'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 &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"), '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'), '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': '>'}, '<>'), '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'), 'firstof08': ('{% firstof a b "c and d" %}', {'a': 0, 'b': 0}, 'c and d'), 'firstof09': ('{% firstof %}', {}, template.TemplateSyntaxError), - 'firstof10': ('{% firstof a %}', {'a': '<'}, '<'), + 'firstof10': ('{% firstof a %}', {'a': '<'}, '<'), 'firstof11': ('{% load firstof from future %}{% firstof a b %}', {'a': '<', 'b': '>'}, '<'), 'firstof12': ('{% load firstof from future %}{% firstof a b %}', {'a': '', 'b': '>'}, '>'),