diff options
| author | Luke Plant <L.Plant.98@cantab.net> | 2012-07-03 00:31:14 +0100 |
|---|---|---|
| committer | Luke Plant <L.Plant.98@cantab.net> | 2012-07-03 22:20:12 +0100 |
| commit | a92e7f37c4ae84b6b8d8016cc6783211e9047219 (patch) | |
| tree | 2023a961e7e9fb9c5ef2ce45e8d10cf2146d5896 | |
| parent | bee498f3a2f66210db39f0be244ec4fa888b6940 (diff) | |
Changed a lot of internal code to use 'format_html' where appropriate/possible
| -rw-r--r-- | django/contrib/admin/helpers.py | 10 | ||||
| -rw-r--r-- | django/contrib/admin/templatetags/admin_list.py | 42 | ||||
| -rw-r--r-- | django/contrib/admin/util.py | 11 | ||||
| -rw-r--r-- | django/contrib/admin/widgets.py | 15 | ||||
| -rw-r--r-- | django/contrib/auth/forms.py | 13 | ||||
| -rw-r--r-- | django/contrib/databrowse/plugins/calendars.py | 6 | ||||
| -rw-r--r-- | django/contrib/databrowse/plugins/fieldchoices.py | 6 | ||||
| -rw-r--r-- | django/contrib/gis/maps/google/gmap.py | 14 | ||||
| -rw-r--r-- | django/forms/forms.py | 12 | ||||
| -rw-r--r-- | django/forms/util.py | 21 | ||||
| -rw-r--r-- | django/forms/widgets.py | 59 | ||||
| -rw-r--r-- | django/template/defaulttags.py | 5 |
12 files changed, 121 insertions, 93 deletions
diff --git a/django/contrib/admin/helpers.py b/django/contrib/admin/helpers.py index 6c648ecb4a..ac29d19469 100644 --- a/django/contrib/admin/helpers.py +++ b/django/contrib/admin/helpers.py @@ -10,7 +10,7 @@ from django.db.models.fields.related import ManyToManyRel from django.forms.util import flatatt from django.template.defaultfilters import capfirst from django.utils.encoding import force_unicode, smart_unicode -from django.utils.html import escape, conditional_escape +from django.utils.html import conditional_escape, format_html from django.utils.safestring import mark_safe from django.utils.translation import ugettext_lazy as _ from django.conf import settings @@ -163,11 +163,9 @@ class AdminReadonlyField(object): if not self.is_first: attrs["class"] = "inline" label = self.field['label'] - contents = capfirst(force_unicode(escape(label))) + ":" - return mark_safe('<label%(attrs)s>%(contents)s</label>' % { - "attrs": flatatt(attrs), - "contents": contents, - }) + return format_html('<label{0}>{1}:</label>', + flatatt(attrs), + capfirst(force_unicode(label))) def contents(self): from django.contrib.admin.templatetags.admin_list import _boolean_icon diff --git a/django/contrib/admin/templatetags/admin_list.py b/django/contrib/admin/templatetags/admin_list.py index 30a85ab7f7..29b5d71a16 100644 --- a/django/contrib/admin/templatetags/admin_list.py +++ b/django/contrib/admin/templatetags/admin_list.py @@ -10,7 +10,7 @@ from django.contrib.admin.templatetags.admin_static import static from django.core.exceptions import ObjectDoesNotExist from django.db import models from django.utils import formats -from django.utils.html import escape, conditional_escape +from django.utils.html import format_html from django.utils.safestring import mark_safe from django.utils.text import capfirst from django.utils.translation import ugettext as _ @@ -31,9 +31,12 @@ def paginator_number(cl,i): if i == DOT: return '... ' elif i == cl.page_num: - return mark_safe('<span class="this-page">%d</span> ' % (i+1)) + return format_html('<span class="this-page">{}</span> ', i+1) else: - return mark_safe('<a href="%s"%s>%d</a> ' % (escape(cl.get_query_string({PAGE_VAR: i})), (i == cl.paginator.num_pages-1 and ' class="end"' or ''), i+1)) + return format_html('<a href="{0}"{1}>{2}</a> ', + cl.get_query_string({PAGE_VAR: i}), + mark_safe(' class="end"' if i == cl.paginator.num_pages-1 else ''), + i+1) @register.inclusion_tag('admin/pagination.html') def pagination(cl): @@ -159,13 +162,14 @@ def result_headers(cl): "url_primary": cl.get_query_string({ORDER_VAR: '.'.join(o_list_primary)}), "url_remove": cl.get_query_string({ORDER_VAR: '.'.join(o_list_remove)}), "url_toggle": cl.get_query_string({ORDER_VAR: '.'.join(o_list_toggle)}), - "class_attrib": mark_safe(th_classes and ' class="%s"' % ' '.join(th_classes) or '') + "class_attrib": format_html(' class="{}"', ' '.join(th_classes)) + if th_classes else '', } def _boolean_icon(field_val): icon_url = static('admin/img/icon-%s.gif' % {True: 'yes', False: 'no', None: 'unknown'}[field_val]) - return mark_safe('<img src="%s" alt="%s" />' % (icon_url, field_val)) + return format_html('<img src="{0}" alt="{1}" />', icon_url, field_val) def items_for_result(cl, result, form): """ @@ -182,7 +186,7 @@ def items_for_result(cl, result, form): else: if f is None: if field_name == 'action_checkbox': - row_class = ' class="action-checkbox"' + row_class = mark_safe(' class="action-checkbox"') allow_tags = getattr(attr, 'allow_tags', False) boolean = getattr(attr, 'boolean', False) if boolean: @@ -190,23 +194,21 @@ def items_for_result(cl, result, form): result_repr = display_for_value(value, boolean) # Strip HTML tags in the resulting text, except if the # function has an "allow_tags" attribute set to True. - if not allow_tags: - result_repr = escape(result_repr) - else: + if allow_tags: result_repr = mark_safe(result_repr) if isinstance(value, (datetime.date, datetime.time)): - row_class = ' class="nowrap"' + row_class = mark_safe(' class="nowrap"') else: if isinstance(f.rel, models.ManyToOneRel): field_val = getattr(result, f.name) if field_val is None: result_repr = EMPTY_CHANGELIST_VALUE else: - result_repr = escape(field_val) + result_repr = field_val else: result_repr = display_for_field(value, f) if isinstance(f, (models.DateField, models.TimeField, models.ForeignKey)): - row_class = ' class="nowrap"' + row_class = mark_safe(' class="nowrap"') if force_unicode(result_repr) == '': result_repr = mark_safe(' ') # If list_display_links not defined, add the link tag to the first field @@ -222,8 +224,14 @@ def items_for_result(cl, result, form): attr = pk value = result.serializable_value(attr) result_id = repr(force_unicode(value))[1:] - yield mark_safe('<%s%s><a href="%s"%s>%s</a></%s>' % \ - (table_tag, row_class, url, (cl.is_popup and ' onclick="opener.dismissRelatedLookupPopup(window, %s); return false;"' % result_id or ''), conditional_escape(result_repr), table_tag)) + yield format_html('<{0}{1}><a href="{2}"{3}>{4}</a></{5}>', + table_tag, + row_class, + url, + format_html(' onclick="opener.dismissRelatedLookupPopup(window, {0}); return false;"', result_id) + if cl.is_popup else '', + result_repr, + table_tag) else: # By default the fields come from ModelAdmin.list_editable, but if we pull # the fields out of the form instead of list_editable custom admins @@ -233,11 +241,9 @@ def items_for_result(cl, result, form): form[cl.model._meta.pk.name].is_hidden)): bf = form[field_name] result_repr = mark_safe(force_unicode(bf.errors) + force_unicode(bf)) - else: - result_repr = conditional_escape(result_repr) - yield mark_safe('<td%s>%s</td>' % (row_class, result_repr)) + yield format_html('<td{0}>{1}</td>', row_class, result_repr) if form and not form[cl.model._meta.pk.name].is_hidden: - yield mark_safe('<td>%s</td>' % force_unicode(form[cl.model._meta.pk.name])) + yield format_html('<td>{0}</td>', force_unicode(form[cl.model._meta.pk.name])) class ResultList(list): # Wrapper class used to return items in a list_editable diff --git a/django/contrib/admin/util.py b/django/contrib/admin/util.py index 18b10f3cfa..92e1c0efd5 100644 --- a/django/contrib/admin/util.py +++ b/django/contrib/admin/util.py @@ -9,8 +9,7 @@ from django.db.models.deletion import Collector from django.db.models.related import RelatedObject from django.forms.forms import pretty_name from django.utils import formats -from django.utils.html import escape -from django.utils.safestring import mark_safe +from django.utils.html import format_html from django.utils.text import capfirst from django.utils import timezone from django.utils.encoding import force_unicode, smart_unicode, smart_str @@ -124,10 +123,10 @@ def get_deleted_objects(objs, opts, user, admin_site, using): if not user.has_perm(p): perms_needed.add(opts.verbose_name) # Display a link to the admin page. - return mark_safe('%s: <a href="%s">%s</a>' % - (escape(capfirst(opts.verbose_name)), - admin_url, - escape(obj))) + return format_html('{0}: <a href="{1}">{2}</a>', + capfirst(opts.verbose_name), + admin_url, + obj) else: # Don't display link to edit, because it either has no # admin or is edited inline. diff --git a/django/contrib/admin/widgets.py b/django/contrib/admin/widgets.py index 18897bdeb1..37f286d0d4 100644 --- a/django/contrib/admin/widgets.py +++ b/django/contrib/admin/widgets.py @@ -10,7 +10,7 @@ from django.contrib.admin.templatetags.admin_static import static from django.core.urlresolvers import reverse from django.forms.widgets import RadioFieldRenderer from django.forms.util import flatatt -from django.utils.html import escape +from django.utils.html import escape, format_html, format_html_join from django.utils.text import Truncator from django.utils.translation import ugettext as _ from django.utils.safestring import mark_safe @@ -85,16 +85,17 @@ class AdminSplitDateTime(forms.SplitDateTimeWidget): forms.MultiWidget.__init__(self, widgets, attrs) def format_output(self, rendered_widgets): - return mark_safe('<p class="datetime">%s %s<br />%s %s</p>' % \ - (_('Date:'), rendered_widgets[0], _('Time:'), rendered_widgets[1])) + return format_html('<p class="datetime">{0} {1}<br />{2} {3}</p>', + _('Date:'), rendered_widgets[0], + _('Time:'), rendered_widgets[1]) class AdminRadioFieldRenderer(RadioFieldRenderer): def render(self): """Outputs a <ul> for this set of radio fields.""" - return mark_safe('<ul%s>\n%s\n</ul>' % ( - flatatt(self.attrs), - '\n'.join(['<li>%s</li>' % force_unicode(w) for w in self])) - ) + return format_html('<ul{0}>\n{1}\n</ul>', + flatatt(self.attrs), + format_html_join('\n', '<li>{0}</li>', + ((force_unicode(w),) for w in self))) class AdminRadioSelect(forms.RadioSelect): renderer = AdminRadioFieldRenderer diff --git a/django/contrib/auth/forms.py b/django/contrib/auth/forms.py index 780b0c015e..bce8747661 100644 --- a/django/contrib/auth/forms.py +++ b/django/contrib/auth/forms.py @@ -1,6 +1,7 @@ from django import forms from django.forms.util import flatatt from django.template import loader +from django.utils.html import format_html, format_html_join from django.utils.http import int_to_base36 from django.utils.safestring import mark_safe from django.utils.translation import ugettext, ugettext_lazy as _ @@ -28,13 +29,15 @@ class ReadOnlyPasswordHashWidget(forms.Widget): try: hasher = identify_hasher(encoded) except ValueError: - summary = "<strong>Invalid password format or unknown hashing algorithm.</strong>" + summary = mark_safe("<strong>Invalid password format or unknown hashing algorithm.</strong>") else: - summary = "" - for key, value in hasher.safe_summary(encoded).iteritems(): - summary += "<strong>%(key)s</strong>: %(value)s " % {"key": ugettext(key), "value": value} + summary = format_html_join('', + "<strong>{0}</strong>: {1} ", + ((ugettext(key), value) + for key, value in hasher.safe_summary(encoded).items()) + ) - return mark_safe("<div%(attrs)s>%(summary)s</div>" % {"attrs": flatatt(final_attrs), "summary": summary}) + return format_html("<div{0}>{1}</div>", flatatt(final_attrs), summary) class ReadOnlyPasswordHashField(forms.Field): diff --git a/django/contrib/databrowse/plugins/calendars.py b/django/contrib/databrowse/plugins/calendars.py index 587c752a94..c842498934 100644 --- a/django/contrib/databrowse/plugins/calendars.py +++ b/django/contrib/databrowse/plugins/calendars.py @@ -5,6 +5,7 @@ from django.db import models from django.contrib.databrowse.datastructures import EasyModel from django.contrib.databrowse.sites import DatabrowsePlugin from django.shortcuts import render_to_response +from django.utils.html import format_html, format_html_join from django.utils.text import capfirst from django.utils.encoding import force_unicode from django.utils.safestring import mark_safe @@ -64,8 +65,9 @@ class CalendarPlugin(DatabrowsePlugin): fields = self.field_dict(model) if not fields: return '' - return mark_safe('<p class="filter"><strong>View calendar by:</strong> %s</p>' % \ - ', '.join(['<a href="calendars/%s/">%s</a>' % (f.name, force_unicode(capfirst(f.verbose_name))) for f in fields.values()])) + return format_html('<p class="filter"><strong>View calendar by:</strong> {0}</p>', + format_html_join(', ', '<a href="calendars/{0}/">{1}</a>', + ((f.name, force_unicode(capfirst(f.verbose_name))) for f in fields.values()))) def urls(self, plugin_name, easy_instance_field): if isinstance(easy_instance_field.field, models.DateField): diff --git a/django/contrib/databrowse/plugins/fieldchoices.py b/django/contrib/databrowse/plugins/fieldchoices.py index 5a13252ab3..f3bd829e61 100644 --- a/django/contrib/databrowse/plugins/fieldchoices.py +++ b/django/contrib/databrowse/plugins/fieldchoices.py @@ -5,6 +5,7 @@ from django.db import models from django.contrib.databrowse.datastructures import EasyModel from django.contrib.databrowse.sites import DatabrowsePlugin from django.shortcuts import render_to_response +from django.utils.html import format_html, format_html_join from django.utils.text import capfirst from django.utils.encoding import smart_str, force_unicode from django.utils.safestring import mark_safe @@ -32,8 +33,9 @@ class FieldChoicePlugin(DatabrowsePlugin): fields = self.field_dict(model) if not fields: return '' - return mark_safe('<p class="filter"><strong>View by:</strong> %s</p>' % \ - ', '.join(['<a href="fields/%s/">%s</a>' % (f.name, force_unicode(capfirst(f.verbose_name))) for f in fields.values()])) + return format_html('<p class="filter"><strong>View by:</strong> {0}</p>', + format_html_join(', ', '<a href="fields/{0}/">{1}</a>', + ((f.name, force_unicode(capfirst(f.verbose_name))) for f in fields.values()))) def urls(self, plugin_name, easy_instance_field): if easy_instance_field.field in self.field_dict(easy_instance_field.model.model).values(): diff --git a/django/contrib/gis/maps/google/gmap.py b/django/contrib/gis/maps/google/gmap.py index f0e8d73399..49515c0dce 100644 --- a/django/contrib/gis/maps/google/gmap.py +++ b/django/contrib/gis/maps/google/gmap.py @@ -1,5 +1,6 @@ from django.conf import settings from django.template.loader import render_to_string +from django.utils.html import format_html from django.utils.safestring import mark_safe from django.contrib.gis.maps.google.overlays import GPolygon, GPolyline, GMarker @@ -111,17 +112,18 @@ class GoogleMap(object): @property def body(self): "Returns HTML body tag for loading and unloading Google Maps javascript." - return mark_safe('<body %s %s>' % (self.onload, self.onunload)) + return format_html('<body {0} {1}>', self.onload, self.onunload) @property def onload(self): "Returns the `onload` HTML <body> attribute." - return mark_safe('onload="%s.%s_load()"' % (self.js_module, self.dom_id)) + return format_html('onload="{0}.{1}_load()"', self.js_module, self.dom_id) @property def api_script(self): "Returns the <script> tag for the Google Maps API javascript." - return mark_safe('<script src="%s%s" type="text/javascript"></script>' % (self.api_url, self.key)) + return format_html('<script src="{0}{1}" type="text/javascript"></script>', + self.api_url, self.key) @property def js(self): @@ -131,17 +133,17 @@ class GoogleMap(object): @property def scripts(self): "Returns all <script></script> tags required with Google Maps JavaScript." - return mark_safe('%s\n <script type="text/javascript">\n//<![CDATA[\n%s//]]>\n </script>' % (self.api_script, self.js)) + return format_html('%s\n <script type="text/javascript">\n//<![CDATA[\n%s//]]>\n </script>', self.api_script, mark_safe(self.js)) @property def style(self): "Returns additional CSS styling needed for Google Maps on IE." - return mark_safe('<style type="text/css">%s</style>' % self.vml_css) + return format_html('<style type="text/css">{0}</style>', self.vml_css) @property def xhtml(self): "Returns XHTML information needed for IE VML overlays." - return mark_safe('<html xmlns="http://www.w3.org/1999/xhtml" %s>' % self.xmlns) + return format_html('<html xmlns="http://www.w3.org/1999/xhtml" {0}>', self.xmlns) @property def icons(self): diff --git a/django/forms/forms.py b/django/forms/forms.py index 22c3057c62..880873a273 100644 --- a/django/forms/forms.py +++ b/django/forms/forms.py @@ -11,7 +11,7 @@ from django.forms.fields import Field, FileField from django.forms.util import flatatt, ErrorDict, ErrorList from django.forms.widgets import Media, media_property, TextInput, Textarea from django.utils.datastructures import SortedDict -from django.utils.html import conditional_escape +from django.utils.html import conditional_escape, format_html from django.utils.encoding import StrAndUnicode, smart_unicode, force_unicode from django.utils.safestring import mark_safe @@ -167,7 +167,7 @@ class BaseForm(StrAndUnicode): # punctuation. if self.label_suffix: if label[-1] not in ':?.!': - label += self.label_suffix + label = format_html('{}{}', label, self.label_suffix) label = bf.label_tag(label) or '' else: label = '' @@ -498,8 +498,8 @@ class BoundField(StrAndUnicode): def label_tag(self, contents=None, attrs=None): """ Wraps the given contents in a <label>, if the field has an ID attribute. - Does not HTML-escape the contents. If contents aren't given, uses the - field's HTML-escaped label. + contents should be 'mark_safe'd to avoid HTML escaping. If contents + aren't given, uses the field's HTML-escaped label. If attrs are given, they're used as HTML attributes on the <label> tag. """ @@ -508,7 +508,9 @@ class BoundField(StrAndUnicode): id_ = widget.attrs.get('id') or self.auto_id if id_: attrs = attrs and flatatt(attrs) or '' - contents = '<label for="%s"%s>%s</label>' % (widget.id_for_label(id_), attrs, unicode(contents)) + contents = format_html('<label for="{0}"{1}>{2}</label>', + widget.id_for_label(id_), attrs, contents + ) return mark_safe(contents) def css_classes(self, extra_classes=None): diff --git a/django/forms/util.py b/django/forms/util.py index ca53b1f3d4..91a5686886 100644 --- a/django/forms/util.py +++ b/django/forms/util.py @@ -1,7 +1,7 @@ from __future__ import unicode_literals from django.conf import settings -from django.utils.html import conditional_escape +from django.utils.html import format_html, format_html_join from django.utils.encoding import StrAndUnicode, force_unicode from django.utils.safestring import mark_safe from django.utils import timezone @@ -17,8 +17,10 @@ def flatatt(attrs): The returned string will contain a leading space followed by key="value", XML-style pairs. It is assumed that the keys do not need to be XML-escaped. If the passed dictionary is empty, then return an empty string. + + The result is passed through 'mark_safe'. """ - return ''.join([' %s="%s"' % (k, conditional_escape(v)) for k, v in attrs.items()]) + return format_html_join('', ' {}="{}"', attrs.items()) class ErrorDict(dict, StrAndUnicode): """ @@ -31,9 +33,11 @@ class ErrorDict(dict, StrAndUnicode): def as_ul(self): if not self: return '' - return mark_safe('<ul class="errorlist">%s</ul>' - % ''.join(['<li>%s%s</li>' % (k, conditional_escape(force_unicode(v))) - for k, v in self.items()])) + return format_html('<ul class="errorlist">{}</ul>', + format_html_join('', '<li>{0}{1}</li>', + ((k, force_unicode(v)) + for k, v in self.items()) + )) def as_text(self): return '\n'.join(['* %s\n%s' % (k, '\n'.join([' * %s' % force_unicode(i) for i in v])) for k, v in self.items()]) @@ -47,8 +51,11 @@ class ErrorList(list, StrAndUnicode): def as_ul(self): if not self: return '' - return mark_safe('<ul class="errorlist">%s</ul>' - % ''.join(['<li>%s</li>' % conditional_escape(force_unicode(e)) for e in self])) + return format_html('<ul class="errorlist">{}</ul>', + format_html_join('', '<li>{}</li>', + ((force_unicode(e),) for e in self) + ) + ) def as_text(self): if not self: return '' diff --git a/django/forms/widgets.py b/django/forms/widgets.py index 2fddee918a..65caa68db2 100644 --- a/django/forms/widgets.py +++ b/django/forms/widgets.py @@ -12,7 +12,7 @@ from urlparse import urljoin from django.conf import settings from django.forms.util import flatatt, to_current_timezone from django.utils.datastructures import MultiValueDict, MergeDict -from django.utils.html import escape, conditional_escape +from django.utils.html import conditional_escape, format_html, format_html_join from django.utils.translation import ugettext, ugettext_lazy from django.utils.encoding import StrAndUnicode, force_unicode from django.utils.safestring import mark_safe @@ -53,7 +53,7 @@ class Media(StrAndUnicode): return mark_safe('\n'.join(chain(*[getattr(self, 'render_' + name)() for name in MEDIA_TYPES]))) def render_js(self): - return ['<script type="text/javascript" src="%s"></script>' % self.absolute_path(path) for path in self._js] + return [format_html('<script type="text/javascript" src="{0}"></script>', self.absolute_path(path)) for path in self._js] def render_css(self): # To keep rendering order consistent, we can't just iterate over items(). @@ -61,7 +61,7 @@ class Media(StrAndUnicode): media = self._css.keys() media.sort() return chain(*[ - ['<link href="%s" type="text/css" media="%s" rel="stylesheet" />' % (self.absolute_path(path), medium) + [format_html('<link href="{0}" type="text/css" media="{1}" rel="stylesheet" />', self.absolute_path(path), medium) for path in self._css[medium]] for medium in media]) @@ -254,7 +254,7 @@ class Input(Widget): if value != '': # Only add the 'value' attribute if a value is non-empty. final_attrs['value'] = force_unicode(self._format_value(value)) - return mark_safe('<input%s />' % flatatt(final_attrs)) + return format_html('<input{} />', flatatt(final_attrs)) class TextInput(Input): input_type = 'text' @@ -295,7 +295,7 @@ class MultipleHiddenInput(HiddenInput): # An ID attribute was given. Add a numeric index as a suffix # so that the inputs don't all have the same ID attribute. input_attrs['id'] = '%s_%s' % (id_, i) - inputs.append('<input%s />' % flatatt(input_attrs)) + inputs.append(format_html('<input{} />', flatatt(input_attrs))) return mark_safe('\n'.join(inputs)) def value_from_datadict(self, data, files, name): @@ -355,9 +355,9 @@ class ClearableFileInput(FileInput): if value and hasattr(value, "url"): template = self.template_with_initial - substitutions['initial'] = ('<a href="%s">%s</a>' - % (escape(value.url), - escape(force_unicode(value)))) + substitutions['initial'] = format_html('<a href="{0}">{1}</a>', + value.url, + force_unicode(value)) if not self.is_required: checkbox_name = self.clear_checkbox_name(name) checkbox_id = self.clear_checkbox_id(checkbox_name) @@ -392,8 +392,9 @@ class Textarea(Widget): def render(self, name, value, attrs=None): if value is None: value = '' final_attrs = self.build_attrs(attrs, name=name) - return mark_safe('<textarea%s>%s</textarea>' % (flatatt(final_attrs), - conditional_escape(force_unicode(value)))) + return format_html('<textarea{0}>{1}</textarea>', + flatatt(final_attrs), + force_unicode(value)) class DateInput(Input): input_type = 'text' @@ -511,7 +512,7 @@ class CheckboxInput(Widget): if not (value is True or value is False or value is None or value == ''): # Only add the 'value' attribute if a value is non-empty. final_attrs['value'] = force_unicode(value) - return mark_safe('<input%s />' % flatatt(final_attrs)) + return format_html('<input{} />', flatatt(final_attrs)) def value_from_datadict(self, data, files, name): if name not in data: @@ -543,7 +544,7 @@ class Select(Widget): def render(self, name, value, attrs=None, choices=()): if value is None: value = '' final_attrs = self.build_attrs(attrs, name=name) - output = ['<select%s>' % flatatt(final_attrs)] + output = [format_html('<select{}>', flatatt(final_attrs))] options = self.render_options(choices, [value]) if options: output.append(options) @@ -553,15 +554,16 @@ class Select(Widget): def render_option(self, selected_choices, option_value, option_label): option_value = force_unicode(option_value) if option_value in selected_choices: - selected_html = ' selected="selected"' + selected_html = mark_safe(' selected="selected"') if not self.allow_multiple_selected: # Only allow for a single selection. selected_choices.remove(option_value) else: selected_html = '' - return '<option value="%s"%s>%s</option>' % ( - escape(option_value), selected_html, - conditional_escape(force_unicode(option_label))) + return format_html('<option value="{0}"{1}>{2}</option>', + option_value, + selected_html, + force_unicode(option_label)) def render_options(self, choices, selected_choices): # Normalize to strings. @@ -569,7 +571,7 @@ class Select(Widget): output = [] for option_value, option_label in chain(self.choices, choices): if isinstance(option_label, (list, tuple)): - output.append('<optgroup label="%s">' % escape(force_unicode(option_value))) + output.append(format_html('<optgroup label="{0}">', force_unicode(option_value))) for option in option_label: output.append(self.render_option(selected_choices, *option)) output.append('</optgroup>') @@ -618,7 +620,7 @@ class SelectMultiple(Select): def render(self, name, value, attrs=None, choices=()): if value is None: value = [] final_attrs = self.build_attrs(attrs, name=name) - output = ['<select multiple="multiple"%s>' % flatatt(final_attrs)] + output = [format_html('<select multiple="multiple"{}>', flatatt(final_attrs))] options = self.render_options(choices, value) if options: output.append(options) @@ -662,11 +664,11 @@ class RadioInput(SubWidget): value = value or self.value attrs = attrs or self.attrs if 'id' in self.attrs: - label_for = ' for="%s_%s"' % (self.attrs['id'], self.index) + label_for = format_html(' for="{0}_{1}"', self.attrs['id'], self.index) else: label_for = '' - choice_label = conditional_escape(force_unicode(self.choice_label)) - return mark_safe('<label%s>%s %s</label>' % (label_for, self.tag(), choice_label)) + choice_label = force_unicode(self.choice_label) + return format_html('<label{0}>{1} {2}</label>', label_for, self.tag(), choice_label) def is_checked(self): return self.value == self.choice_value @@ -677,7 +679,7 @@ class RadioInput(SubWidget): final_attrs = dict(self.attrs, type='radio', name=self.name, value=self.choice_value) if self.is_checked(): final_attrs['checked'] = 'checked' - return mark_safe('<input%s />' % flatatt(final_attrs)) + return format_html('<input{} />', flatatt(final_attrs)) class RadioFieldRenderer(StrAndUnicode): """ @@ -701,8 +703,10 @@ class RadioFieldRenderer(StrAndUnicode): def render(self): """Outputs a <ul> for this set of radio fields.""" - return mark_safe('<ul>\n%s\n</ul>' % '\n'.join(['<li>%s</li>' - % force_unicode(w) for w in self])) + return format_html('<ul>\n{0}\n</ul>', + format_html_join('\n', '<li>{0}</li>', + [(force_unicode(w),) for w in self] + )) class RadioSelect(Select): renderer = RadioFieldRenderer @@ -751,15 +755,16 @@ class CheckboxSelectMultiple(SelectMultiple): # so that the checkboxes don't all have the same ID attribute. if has_id: final_attrs = dict(final_attrs, id='%s_%s' % (attrs['id'], i)) - label_for = ' for="%s"' % final_attrs['id'] + label_for = format_html(' for="{0}"', final_attrs['id']) else: label_for = '' cb = CheckboxInput(final_attrs, check_test=lambda value: value in str_values) option_value = force_unicode(option_value) rendered_cb = cb.render(name, option_value) - option_label = conditional_escape(force_unicode(option_label)) - output.append('<li><label%s>%s %s</label></li>' % (label_for, rendered_cb, option_label)) + option_label = force_unicode(option_label) + output.append(format_html('<li><label{0}>{1} {2}</label></li>', + label_for, rendered_cb, option_label)) output.append('</ul>') return mark_safe('\n'.join(output)) diff --git a/django/template/defaulttags.py b/django/template/defaulttags.py index 83b72e120b..d27439b3b8 100644 --- a/django/template/defaulttags.py +++ b/django/template/defaulttags.py @@ -16,6 +16,7 @@ from django.template.smartif import IfParser, Literal from django.template.defaultfilters import date from django.utils.encoding import smart_unicode from django.utils.safestring import mark_safe +from django.utils.html import format_html from django.utils import timezone register = Library() @@ -44,9 +45,9 @@ class CsrfTokenNode(Node): csrf_token = context.get('csrf_token', None) if csrf_token: if csrf_token == 'NOTPROVIDED': - return mark_safe("") + return format_html("") else: - return mark_safe("<div style='display:none'><input type='hidden' name='csrfmiddlewaretoken' value='%s' /></div>" % csrf_token) + return format_html("<div style='display:none'><input type='hidden' name='csrfmiddlewaretoken' value='{}' /></div>", csrf_token) else: # It's very probable that the token is missing because of # misconfiguration, so we raise a warning |
