Changed a lot of internal code to use 'format_html' where appropriate/possible

This commit is contained in:
Luke Plant 2012-07-03 00:31:14 +01:00
parent bee498f3a2
commit a92e7f37c4
12 changed files with 121 additions and 93 deletions

View File

@ -10,7 +10,7 @@ from django.db.models.fields.related import ManyToManyRel
from django.forms.util import flatatt from django.forms.util import flatatt
from django.template.defaultfilters import capfirst from django.template.defaultfilters import capfirst
from django.utils.encoding import force_unicode, smart_unicode 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.safestring import mark_safe
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
from django.conf import settings from django.conf import settings
@ -163,11 +163,9 @@ class AdminReadonlyField(object):
if not self.is_first: if not self.is_first:
attrs["class"] = "inline" attrs["class"] = "inline"
label = self.field['label'] label = self.field['label']
contents = capfirst(force_unicode(escape(label))) + ":" return format_html('<label{0}>{1}:</label>',
return mark_safe('<label%(attrs)s>%(contents)s</label>' % { flatatt(attrs),
"attrs": flatatt(attrs), capfirst(force_unicode(label)))
"contents": contents,
})
def contents(self): def contents(self):
from django.contrib.admin.templatetags.admin_list import _boolean_icon from django.contrib.admin.templatetags.admin_list import _boolean_icon

View File

@ -10,7 +10,7 @@ from django.contrib.admin.templatetags.admin_static import static
from django.core.exceptions import ObjectDoesNotExist from django.core.exceptions import ObjectDoesNotExist
from django.db import models from django.db import models
from django.utils import formats 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.safestring import mark_safe
from django.utils.text import capfirst from django.utils.text import capfirst
from django.utils.translation import ugettext as _ from django.utils.translation import ugettext as _
@ -31,9 +31,12 @@ def paginator_number(cl,i):
if i == DOT: if i == DOT:
return '... ' return '... '
elif i == cl.page_num: 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: 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') @register.inclusion_tag('admin/pagination.html')
def pagination(cl): def pagination(cl):
@ -159,13 +162,14 @@ def result_headers(cl):
"url_primary": cl.get_query_string({ORDER_VAR: '.'.join(o_list_primary)}), "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_remove": cl.get_query_string({ORDER_VAR: '.'.join(o_list_remove)}),
"url_toggle": cl.get_query_string({ORDER_VAR: '.'.join(o_list_toggle)}), "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): def _boolean_icon(field_val):
icon_url = static('admin/img/icon-%s.gif' % icon_url = static('admin/img/icon-%s.gif' %
{True: 'yes', False: 'no', None: 'unknown'}[field_val]) {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): def items_for_result(cl, result, form):
""" """
@ -182,7 +186,7 @@ def items_for_result(cl, result, form):
else: else:
if f is None: if f is None:
if field_name == 'action_checkbox': if field_name == 'action_checkbox':
row_class = ' class="action-checkbox"' row_class = mark_safe(' class="action-checkbox"')
allow_tags = getattr(attr, 'allow_tags', False) allow_tags = getattr(attr, 'allow_tags', False)
boolean = getattr(attr, 'boolean', False) boolean = getattr(attr, 'boolean', False)
if boolean: if boolean:
@ -190,23 +194,21 @@ def items_for_result(cl, result, form):
result_repr = display_for_value(value, boolean) result_repr = display_for_value(value, boolean)
# Strip HTML tags in the resulting text, except if the # Strip HTML tags in the resulting text, except if the
# function has an "allow_tags" attribute set to True. # function has an "allow_tags" attribute set to True.
if not allow_tags: if allow_tags:
result_repr = escape(result_repr)
else:
result_repr = mark_safe(result_repr) result_repr = mark_safe(result_repr)
if isinstance(value, (datetime.date, datetime.time)): if isinstance(value, (datetime.date, datetime.time)):
row_class = ' class="nowrap"' row_class = mark_safe(' class="nowrap"')
else: else:
if isinstance(f.rel, models.ManyToOneRel): if isinstance(f.rel, models.ManyToOneRel):
field_val = getattr(result, f.name) field_val = getattr(result, f.name)
if field_val is None: if field_val is None:
result_repr = EMPTY_CHANGELIST_VALUE result_repr = EMPTY_CHANGELIST_VALUE
else: else:
result_repr = escape(field_val) result_repr = field_val
else: else:
result_repr = display_for_field(value, f) result_repr = display_for_field(value, f)
if isinstance(f, (models.DateField, models.TimeField, models.ForeignKey)): if isinstance(f, (models.DateField, models.TimeField, models.ForeignKey)):
row_class = ' class="nowrap"' row_class = mark_safe(' class="nowrap"')
if force_unicode(result_repr) == '': if force_unicode(result_repr) == '':
result_repr = mark_safe('&nbsp;') result_repr = mark_safe('&nbsp;')
# If list_display_links not defined, add the link tag to the first field # 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 attr = pk
value = result.serializable_value(attr) value = result.serializable_value(attr)
result_id = repr(force_unicode(value))[1:] result_id = repr(force_unicode(value))[1:]
yield mark_safe('<%s%s><a href="%s"%s>%s</a></%s>' % \ yield format_html('<{0}{1}><a href="{2}"{3}>{4}</a></{5}>',
(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)) 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: else:
# By default the fields come from ModelAdmin.list_editable, but if we pull # 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 # 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)): form[cl.model._meta.pk.name].is_hidden)):
bf = form[field_name] bf = form[field_name]
result_repr = mark_safe(force_unicode(bf.errors) + force_unicode(bf)) result_repr = mark_safe(force_unicode(bf.errors) + force_unicode(bf))
else: yield format_html('<td{0}>{1}</td>', row_class, result_repr)
result_repr = conditional_escape(result_repr)
yield mark_safe('<td%s>%s</td>' % (row_class, result_repr))
if form and not form[cl.model._meta.pk.name].is_hidden: 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): class ResultList(list):
# Wrapper class used to return items in a list_editable # Wrapper class used to return items in a list_editable

View File

@ -9,8 +9,7 @@ from django.db.models.deletion import Collector
from django.db.models.related import RelatedObject from django.db.models.related import RelatedObject
from django.forms.forms import pretty_name from django.forms.forms import pretty_name
from django.utils import formats from django.utils import formats
from django.utils.html import escape from django.utils.html import format_html
from django.utils.safestring import mark_safe
from django.utils.text import capfirst from django.utils.text import capfirst
from django.utils import timezone from django.utils import timezone
from django.utils.encoding import force_unicode, smart_unicode, smart_str 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): if not user.has_perm(p):
perms_needed.add(opts.verbose_name) perms_needed.add(opts.verbose_name)
# Display a link to the admin page. # Display a link to the admin page.
return mark_safe('%s: <a href="%s">%s</a>' % return format_html('{0}: <a href="{1}">{2}</a>',
(escape(capfirst(opts.verbose_name)), capfirst(opts.verbose_name),
admin_url, admin_url,
escape(obj))) obj)
else: else:
# Don't display link to edit, because it either has no # Don't display link to edit, because it either has no
# admin or is edited inline. # admin or is edited inline.

View File

@ -10,7 +10,7 @@ from django.contrib.admin.templatetags.admin_static import static
from django.core.urlresolvers import reverse from django.core.urlresolvers import reverse
from django.forms.widgets import RadioFieldRenderer from django.forms.widgets import RadioFieldRenderer
from django.forms.util import flatatt 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.text import Truncator
from django.utils.translation import ugettext as _ from django.utils.translation import ugettext as _
from django.utils.safestring import mark_safe from django.utils.safestring import mark_safe
@ -85,16 +85,17 @@ class AdminSplitDateTime(forms.SplitDateTimeWidget):
forms.MultiWidget.__init__(self, widgets, attrs) forms.MultiWidget.__init__(self, widgets, attrs)
def format_output(self, rendered_widgets): def format_output(self, rendered_widgets):
return mark_safe('<p class="datetime">%s %s<br />%s %s</p>' % \ return format_html('<p class="datetime">{0} {1}<br />{2} {3}</p>',
(_('Date:'), rendered_widgets[0], _('Time:'), rendered_widgets[1])) _('Date:'), rendered_widgets[0],
_('Time:'), rendered_widgets[1])
class AdminRadioFieldRenderer(RadioFieldRenderer): class AdminRadioFieldRenderer(RadioFieldRenderer):
def render(self): def render(self):
"""Outputs a <ul> for this set of radio fields.""" """Outputs a <ul> for this set of radio fields."""
return mark_safe('<ul%s>\n%s\n</ul>' % ( return format_html('<ul{0}>\n{1}\n</ul>',
flatatt(self.attrs), flatatt(self.attrs),
'\n'.join(['<li>%s</li>' % force_unicode(w) for w in self])) format_html_join('\n', '<li>{0}</li>',
) ((force_unicode(w),) for w in self)))
class AdminRadioSelect(forms.RadioSelect): class AdminRadioSelect(forms.RadioSelect):
renderer = AdminRadioFieldRenderer renderer = AdminRadioFieldRenderer

View File

@ -1,6 +1,7 @@
from django import forms from django import forms
from django.forms.util import flatatt from django.forms.util import flatatt
from django.template import loader 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.http import int_to_base36
from django.utils.safestring import mark_safe from django.utils.safestring import mark_safe
from django.utils.translation import ugettext, ugettext_lazy as _ from django.utils.translation import ugettext, ugettext_lazy as _
@ -28,13 +29,15 @@ class ReadOnlyPasswordHashWidget(forms.Widget):
try: try:
hasher = identify_hasher(encoded) hasher = identify_hasher(encoded)
except ValueError: 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: else:
summary = "" summary = format_html_join('',
for key, value in hasher.safe_summary(encoded).iteritems(): "<strong>{0}</strong>: {1} ",
summary += "<strong>%(key)s</strong>: %(value)s " % {"key": ugettext(key), "value": value} ((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): class ReadOnlyPasswordHashField(forms.Field):

View File

@ -5,6 +5,7 @@ from django.db import models
from django.contrib.databrowse.datastructures import EasyModel from django.contrib.databrowse.datastructures import EasyModel
from django.contrib.databrowse.sites import DatabrowsePlugin from django.contrib.databrowse.sites import DatabrowsePlugin
from django.shortcuts import render_to_response 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.text import capfirst
from django.utils.encoding import force_unicode from django.utils.encoding import force_unicode
from django.utils.safestring import mark_safe from django.utils.safestring import mark_safe
@ -64,8 +65,9 @@ class CalendarPlugin(DatabrowsePlugin):
fields = self.field_dict(model) fields = self.field_dict(model)
if not fields: if not fields:
return '' return ''
return mark_safe('<p class="filter"><strong>View calendar by:</strong> %s</p>' % \ return format_html('<p class="filter"><strong>View calendar by:</strong> {0}</p>',
', '.join(['<a href="calendars/%s/">%s</a>' % (f.name, force_unicode(capfirst(f.verbose_name))) for f in fields.values()])) 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): def urls(self, plugin_name, easy_instance_field):
if isinstance(easy_instance_field.field, models.DateField): if isinstance(easy_instance_field.field, models.DateField):

View File

@ -5,6 +5,7 @@ from django.db import models
from django.contrib.databrowse.datastructures import EasyModel from django.contrib.databrowse.datastructures import EasyModel
from django.contrib.databrowse.sites import DatabrowsePlugin from django.contrib.databrowse.sites import DatabrowsePlugin
from django.shortcuts import render_to_response 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.text import capfirst
from django.utils.encoding import smart_str, force_unicode from django.utils.encoding import smart_str, force_unicode
from django.utils.safestring import mark_safe from django.utils.safestring import mark_safe
@ -32,8 +33,9 @@ class FieldChoicePlugin(DatabrowsePlugin):
fields = self.field_dict(model) fields = self.field_dict(model)
if not fields: if not fields:
return '' return ''
return mark_safe('<p class="filter"><strong>View by:</strong> %s</p>' % \ return format_html('<p class="filter"><strong>View by:</strong> {0}</p>',
', '.join(['<a href="fields/%s/">%s</a>' % (f.name, force_unicode(capfirst(f.verbose_name))) for f in fields.values()])) 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): def urls(self, plugin_name, easy_instance_field):
if easy_instance_field.field in self.field_dict(easy_instance_field.model.model).values(): if easy_instance_field.field in self.field_dict(easy_instance_field.model.model).values():

View File

@ -1,5 +1,6 @@
from django.conf import settings from django.conf import settings
from django.template.loader import render_to_string from django.template.loader import render_to_string
from django.utils.html import format_html
from django.utils.safestring import mark_safe from django.utils.safestring import mark_safe
from django.contrib.gis.maps.google.overlays import GPolygon, GPolyline, GMarker from django.contrib.gis.maps.google.overlays import GPolygon, GPolyline, GMarker
@ -111,17 +112,18 @@ class GoogleMap(object):
@property @property
def body(self): def body(self):
"Returns HTML body tag for loading and unloading Google Maps javascript." "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 @property
def onload(self): def onload(self):
"Returns the `onload` HTML <body> attribute." "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 @property
def api_script(self): def api_script(self):
"Returns the <script> tag for the Google Maps API javascript." "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 @property
def js(self): def js(self):
@ -131,17 +133,17 @@ class GoogleMap(object):
@property @property
def scripts(self): def scripts(self):
"Returns all <script></script> tags required with Google Maps JavaScript." "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 @property
def style(self): def style(self):
"Returns additional CSS styling needed for Google Maps on IE." "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 @property
def xhtml(self): def xhtml(self):
"Returns XHTML information needed for IE VML overlays." "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 @property
def icons(self): def icons(self):

View File

@ -11,7 +11,7 @@ from django.forms.fields import Field, FileField
from django.forms.util import flatatt, ErrorDict, ErrorList from django.forms.util import flatatt, ErrorDict, ErrorList
from django.forms.widgets import Media, media_property, TextInput, Textarea from django.forms.widgets import Media, media_property, TextInput, Textarea
from django.utils.datastructures import SortedDict 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.encoding import StrAndUnicode, smart_unicode, force_unicode
from django.utils.safestring import mark_safe from django.utils.safestring import mark_safe
@ -167,7 +167,7 @@ class BaseForm(StrAndUnicode):
# punctuation. # punctuation.
if self.label_suffix: if self.label_suffix:
if label[-1] not in ':?.!': if label[-1] not in ':?.!':
label += self.label_suffix label = format_html('{}{}', label, self.label_suffix)
label = bf.label_tag(label) or '' label = bf.label_tag(label) or ''
else: else:
label = '' label = ''
@ -498,8 +498,8 @@ class BoundField(StrAndUnicode):
def label_tag(self, contents=None, attrs=None): def label_tag(self, contents=None, attrs=None):
""" """
Wraps the given contents in a <label>, if the field has an ID attribute. 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 contents should be 'mark_safe'd to avoid HTML escaping. If contents
field's HTML-escaped label. aren't given, uses the field's HTML-escaped label.
If attrs are given, they're used as HTML attributes on the <label> tag. 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 id_ = widget.attrs.get('id') or self.auto_id
if id_: if id_:
attrs = attrs and flatatt(attrs) or '' 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) return mark_safe(contents)
def css_classes(self, extra_classes=None): def css_classes(self, extra_classes=None):

View File

@ -1,7 +1,7 @@
from __future__ import unicode_literals from __future__ import unicode_literals
from django.conf import settings 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.encoding import StrAndUnicode, force_unicode
from django.utils.safestring import mark_safe from django.utils.safestring import mark_safe
from django.utils import timezone from django.utils import timezone
@ -17,8 +17,10 @@ def flatatt(attrs):
The returned string will contain a leading space followed by key="value", 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. 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. 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): class ErrorDict(dict, StrAndUnicode):
""" """
@ -31,9 +33,11 @@ class ErrorDict(dict, StrAndUnicode):
def as_ul(self): def as_ul(self):
if not self: return '' if not self: return ''
return mark_safe('<ul class="errorlist">%s</ul>' return format_html('<ul class="errorlist">{}</ul>',
% ''.join(['<li>%s%s</li>' % (k, conditional_escape(force_unicode(v))) format_html_join('', '<li>{0}{1}</li>',
for k, v in self.items()])) ((k, force_unicode(v))
for k, v in self.items())
))
def as_text(self): 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()]) 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): def as_ul(self):
if not self: return '' if not self: return ''
return mark_safe('<ul class="errorlist">%s</ul>' return format_html('<ul class="errorlist">{}</ul>',
% ''.join(['<li>%s</li>' % conditional_escape(force_unicode(e)) for e in self])) format_html_join('', '<li>{}</li>',
((force_unicode(e),) for e in self)
)
)
def as_text(self): def as_text(self):
if not self: return '' if not self: return ''

View File

@ -12,7 +12,7 @@ from urlparse import urljoin
from django.conf import settings from django.conf import settings
from django.forms.util import flatatt, to_current_timezone from django.forms.util import flatatt, to_current_timezone
from django.utils.datastructures import MultiValueDict, MergeDict 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.translation import ugettext, ugettext_lazy
from django.utils.encoding import StrAndUnicode, force_unicode from django.utils.encoding import StrAndUnicode, force_unicode
from django.utils.safestring import mark_safe 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]))) return mark_safe('\n'.join(chain(*[getattr(self, 'render_' + name)() for name in MEDIA_TYPES])))
def render_js(self): 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): def render_css(self):
# To keep rendering order consistent, we can't just iterate over items(). # To keep rendering order consistent, we can't just iterate over items().
@ -61,7 +61,7 @@ class Media(StrAndUnicode):
media = self._css.keys() media = self._css.keys()
media.sort() media.sort()
return chain(*[ 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 path in self._css[medium]]
for medium in media]) for medium in media])
@ -254,7 +254,7 @@ class Input(Widget):
if value != '': if value != '':
# Only add the 'value' attribute if a value is non-empty. # Only add the 'value' attribute if a value is non-empty.
final_attrs['value'] = force_unicode(self._format_value(value)) 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): class TextInput(Input):
input_type = 'text' input_type = 'text'
@ -295,7 +295,7 @@ class MultipleHiddenInput(HiddenInput):
# An ID attribute was given. Add a numeric index as a suffix # An ID attribute was given. Add a numeric index as a suffix
# so that the inputs don't all have the same ID attribute. # so that the inputs don't all have the same ID attribute.
input_attrs['id'] = '%s_%s' % (id_, i) 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)) return mark_safe('\n'.join(inputs))
def value_from_datadict(self, data, files, name): def value_from_datadict(self, data, files, name):
@ -355,9 +355,9 @@ class ClearableFileInput(FileInput):
if value and hasattr(value, "url"): if value and hasattr(value, "url"):
template = self.template_with_initial template = self.template_with_initial
substitutions['initial'] = ('<a href="%s">%s</a>' substitutions['initial'] = format_html('<a href="{0}">{1}</a>',
% (escape(value.url), value.url,
escape(force_unicode(value)))) force_unicode(value))
if not self.is_required: if not self.is_required:
checkbox_name = self.clear_checkbox_name(name) checkbox_name = self.clear_checkbox_name(name)
checkbox_id = self.clear_checkbox_id(checkbox_name) checkbox_id = self.clear_checkbox_id(checkbox_name)
@ -392,8 +392,9 @@ class Textarea(Widget):
def render(self, name, value, attrs=None): def render(self, name, value, attrs=None):
if value is None: value = '' if value is None: value = ''
final_attrs = self.build_attrs(attrs, name=name) final_attrs = self.build_attrs(attrs, name=name)
return mark_safe('<textarea%s>%s</textarea>' % (flatatt(final_attrs), return format_html('<textarea{0}>{1}</textarea>',
conditional_escape(force_unicode(value)))) flatatt(final_attrs),
force_unicode(value))
class DateInput(Input): class DateInput(Input):
input_type = 'text' 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 == ''): 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. # Only add the 'value' attribute if a value is non-empty.
final_attrs['value'] = force_unicode(value) 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): def value_from_datadict(self, data, files, name):
if name not in data: if name not in data:
@ -543,7 +544,7 @@ class Select(Widget):
def render(self, name, value, attrs=None, choices=()): def render(self, name, value, attrs=None, choices=()):
if value is None: value = '' if value is None: value = ''
final_attrs = self.build_attrs(attrs, name=name) 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]) options = self.render_options(choices, [value])
if options: if options:
output.append(options) output.append(options)
@ -553,15 +554,16 @@ class Select(Widget):
def render_option(self, selected_choices, option_value, option_label): def render_option(self, selected_choices, option_value, option_label):
option_value = force_unicode(option_value) option_value = force_unicode(option_value)
if option_value in selected_choices: if option_value in selected_choices:
selected_html = ' selected="selected"' selected_html = mark_safe(' selected="selected"')
if not self.allow_multiple_selected: if not self.allow_multiple_selected:
# Only allow for a single selection. # Only allow for a single selection.
selected_choices.remove(option_value) selected_choices.remove(option_value)
else: else:
selected_html = '' selected_html = ''
return '<option value="%s"%s>%s</option>' % ( return format_html('<option value="{0}"{1}>{2}</option>',
escape(option_value), selected_html, option_value,
conditional_escape(force_unicode(option_label))) selected_html,
force_unicode(option_label))
def render_options(self, choices, selected_choices): def render_options(self, choices, selected_choices):
# Normalize to strings. # Normalize to strings.
@ -569,7 +571,7 @@ class Select(Widget):
output = [] output = []
for option_value, option_label in chain(self.choices, choices): for option_value, option_label in chain(self.choices, choices):
if isinstance(option_label, (list, tuple)): 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: for option in option_label:
output.append(self.render_option(selected_choices, *option)) output.append(self.render_option(selected_choices, *option))
output.append('</optgroup>') output.append('</optgroup>')
@ -618,7 +620,7 @@ class SelectMultiple(Select):
def render(self, name, value, attrs=None, choices=()): def render(self, name, value, attrs=None, choices=()):
if value is None: value = [] if value is None: value = []
final_attrs = self.build_attrs(attrs, name=name) 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) options = self.render_options(choices, value)
if options: if options:
output.append(options) output.append(options)
@ -662,11 +664,11 @@ class RadioInput(SubWidget):
value = value or self.value value = value or self.value
attrs = attrs or self.attrs attrs = attrs or self.attrs
if 'id' in 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: else:
label_for = '' label_for = ''
choice_label = conditional_escape(force_unicode(self.choice_label)) choice_label = force_unicode(self.choice_label)
return mark_safe('<label%s>%s %s</label>' % (label_for, self.tag(), choice_label)) return format_html('<label{0}>{1} {2}</label>', label_for, self.tag(), choice_label)
def is_checked(self): def is_checked(self):
return self.value == self.choice_value 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) final_attrs = dict(self.attrs, type='radio', name=self.name, value=self.choice_value)
if self.is_checked(): if self.is_checked():
final_attrs['checked'] = 'checked' final_attrs['checked'] = 'checked'
return mark_safe('<input%s />' % flatatt(final_attrs)) return format_html('<input{} />', flatatt(final_attrs))
class RadioFieldRenderer(StrAndUnicode): class RadioFieldRenderer(StrAndUnicode):
""" """
@ -701,8 +703,10 @@ class RadioFieldRenderer(StrAndUnicode):
def render(self): def render(self):
"""Outputs a <ul> for this set of radio fields.""" """Outputs a <ul> for this set of radio fields."""
return mark_safe('<ul>\n%s\n</ul>' % '\n'.join(['<li>%s</li>' return format_html('<ul>\n{0}\n</ul>',
% force_unicode(w) for w in self])) format_html_join('\n', '<li>{0}</li>',
[(force_unicode(w),) for w in self]
))
class RadioSelect(Select): class RadioSelect(Select):
renderer = RadioFieldRenderer renderer = RadioFieldRenderer
@ -751,15 +755,16 @@ class CheckboxSelectMultiple(SelectMultiple):
# so that the checkboxes don't all have the same ID attribute. # so that the checkboxes don't all have the same ID attribute.
if has_id: if has_id:
final_attrs = dict(final_attrs, id='%s_%s' % (attrs['id'], i)) 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: else:
label_for = '' label_for = ''
cb = CheckboxInput(final_attrs, check_test=lambda value: value in str_values) cb = CheckboxInput(final_attrs, check_test=lambda value: value in str_values)
option_value = force_unicode(option_value) option_value = force_unicode(option_value)
rendered_cb = cb.render(name, option_value) rendered_cb = cb.render(name, option_value)
option_label = conditional_escape(force_unicode(option_label)) option_label = force_unicode(option_label)
output.append('<li><label%s>%s %s</label></li>' % (label_for, rendered_cb, option_label)) output.append(format_html('<li><label{0}>{1} {2}</label></li>',
label_for, rendered_cb, option_label))
output.append('</ul>') output.append('</ul>')
return mark_safe('\n'.join(output)) return mark_safe('\n'.join(output))

View File

@ -16,6 +16,7 @@ from django.template.smartif import IfParser, Literal
from django.template.defaultfilters import date from django.template.defaultfilters import date
from django.utils.encoding import smart_unicode from django.utils.encoding import smart_unicode
from django.utils.safestring import mark_safe from django.utils.safestring import mark_safe
from django.utils.html import format_html
from django.utils import timezone from django.utils import timezone
register = Library() register = Library()
@ -44,9 +45,9 @@ class CsrfTokenNode(Node):
csrf_token = context.get('csrf_token', None) csrf_token = context.get('csrf_token', None)
if csrf_token: if csrf_token:
if csrf_token == 'NOTPROVIDED': if csrf_token == 'NOTPROVIDED':
return mark_safe("") return format_html("")
else: 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: else:
# It's very probable that the token is missing because of # It's very probable that the token is missing because of
# misconfiguration, so we raise a warning # misconfiguration, so we raise a warning