mirror of
				https://github.com/django/django.git
				synced 2025-10-31 09:41:08 +00:00 
			
		
		
		
	Switched {% cycle %} and {% firstof %} tags to auto-escape their variables per deprecation timeline.
refs #17906.
This commit is contained in:
		| @@ -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') | ||||
|   | ||||
| @@ -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 "<strong>fallback value</strong>"|safe %} | ||||
|  | ||||
|     """ | ||||
|     return defaulttags.firstof(parser, token, escape=True) | ||||
|     return defaulttags.firstof(parser, token) | ||||
|   | ||||
| @@ -102,13 +102,11 @@ this:: | ||||
|         </tr> | ||||
|     {% 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 %} | ||||
|         <tr class="{% filter force_escape %}{% cycle rowvalue1 rowvalue2 %}{% endfilter %}"> | ||||
|         <tr class="{% autoescape off %}{% cycle rowvalue1 rowvalue2 %}{% endautoescape | ||||
|             ... | ||||
|         </tr> | ||||
|     {% 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 | ||||
| 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 | ||||
|  | ||||
| @@ -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,24 +273,7 @@ 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:: | ||||
|  | ||||
|     {% 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:: | ||||
| This tag auto-escapes variable values. You can disable auto-escaping with:: | ||||
|  | ||||
|     {% autoescape off %} | ||||
|         {% firstof var1 var2 var3 "<strong>fallback value</strong>" %} | ||||
|   | ||||
| @@ -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': '>'}, '>'), | ||||
|   | ||||
		Reference in New Issue
	
	Block a user