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('' % {
- "attrs": flatatt(attrs),
- "contents": contents,
- })
+ return format_html('',
+ 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('%d ' % (i+1))
+ return format_html('{} ', i+1)
else:
- return mark_safe('%d ' % (escape(cl.get_query_string({PAGE_VAR: i})), (i == cl.paginator.num_pages-1 and ' class="end"' or ''), i+1))
+ return format_html('{2} ',
+ 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('
' % (icon_url, field_val))
+ return format_html('
', 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>%s%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}>{4}{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('
%s | ' % (row_class, result_repr))
+ yield format_html('{1} | ', row_class, result_repr)
if form and not form[cl.model._meta.pk.name].is_hidden:
- yield mark_safe('%s | ' % force_unicode(form[cl.model._meta.pk.name]))
+ yield format_html('{0} | ', 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: %s' %
- (escape(capfirst(opts.verbose_name)),
- admin_url,
- escape(obj)))
+ return format_html('{0}: {2}',
+ 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('%s %s
%s %s
' % \
- (_('Date:'), rendered_widgets[0], _('Time:'), rendered_widgets[1]))
+ return format_html('{0} {1}
{2} {3}
',
+ _('Date:'), rendered_widgets[0],
+ _('Time:'), rendered_widgets[1])
class AdminRadioFieldRenderer(RadioFieldRenderer):
def render(self):
"""Outputs a for this set of radio fields."""
- return mark_safe('' % (
- flatatt(self.attrs),
- '\n'.join(['- %s
' % force_unicode(w) for w in self]))
- )
+ return format_html('',
+ flatatt(self.attrs),
+ format_html_join('\n', '- {0}
',
+ ((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 = "Invalid password format or unknown hashing algorithm."
+ summary = mark_safe("Invalid password format or unknown hashing algorithm.")
else:
- summary = ""
- for key, value in hasher.safe_summary(encoded).iteritems():
- summary += "%(key)s: %(value)s " % {"key": ugettext(key), "value": value}
+ summary = format_html_join('',
+ "{0}: {1} ",
+ ((ugettext(key), value)
+ for key, value in hasher.safe_summary(encoded).items())
+ )
- return mark_safe("%(summary)s
" % {"attrs": flatatt(final_attrs), "summary": summary})
+ return format_html("{1}
", 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('View calendar by: %s
' % \
- ', '.join(['%s' % (f.name, force_unicode(capfirst(f.verbose_name))) for f in fields.values()]))
+ return format_html('View calendar by: {0}
',
+ format_html_join(', ', '{1}',
+ ((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('View by: %s
' % \
- ', '.join(['%s' % (f.name, force_unicode(capfirst(f.verbose_name))) for f in fields.values()]))
+ return format_html('View by: {0}
',
+ format_html_join(', ', '{1}',
+ ((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('' % (self.onload, self.onunload))
+ return format_html('', self.onload, self.onunload)
@property
def onload(self):
"Returns the `onload` HTML 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 ' % (self.api_url, self.key))
+ return format_html('',
+ self.api_url, self.key)
@property
def js(self):
@@ -131,17 +133,17 @@ class GoogleMap(object):
@property
def scripts(self):
"Returns all tags required with Google Maps JavaScript."
- return mark_safe('%s\n ' % (self.api_script, self.js))
+ return format_html('%s\n ', self.api_script, mark_safe(self.js))
@property
def style(self):
"Returns additional CSS styling needed for Google Maps on IE."
- return mark_safe('' % self.vml_css)
+ return format_html('', self.vml_css)
@property
def xhtml(self):
"Returns XHTML information needed for IE VML overlays."
- return mark_safe('' % self.xmlns)
+ return format_html('', 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