diff --git a/django/contrib/admin/options.py b/django/contrib/admin/options.py
index 952d4b8298..f84b096158 100644
--- a/django/contrib/admin/options.py
+++ b/django/contrib/admin/options.py
@@ -101,7 +101,7 @@ class Fieldline(object):
yield AdminField(self.form, field, is_first=(i == 0))
def errors(self):
- return u'\n'.join([self.form[f].errors.as_ul() for f in self.fields])
+ return mark_safe(u'\n'.join([self.form[f].errors.as_ul() for f in self.fields]))
class AdminField(object):
def __init__(self, form, field, is_first):
diff --git a/django/contrib/admin/templatetags/admin_list.py b/django/contrib/admin/templatetags/admin_list.py
index c3b993f518..2737a27972 100644
--- a/django/contrib/admin/templatetags/admin_list.py
+++ b/django/contrib/admin/templatetags/admin_list.py
@@ -114,7 +114,7 @@ def result_headers(cl):
yield {"text": header,
"sortable": True,
"url": cl.get_query_string({ORDER_VAR: i, ORDER_TYPE_VAR: new_order_type}),
- "class_attrib": (th_classes and ' class="%s"' % ' '.join(th_classes) or '')}
+ "class_attrib": mark_safe((th_classes and ' class="%s"' % ' '.join(th_classes) or ''))}
def _boolean_icon(field_val):
BOOLEAN_MAPPING = {True: 'yes', False: 'no', None: 'unknown'}
@@ -148,8 +148,6 @@ def items_for_result(cl, result):
# function has an "allow_tags" attribute set to True.
if not allow_tags:
result_repr = escape(result_repr)
- else:
- result_repr = mark_safe(result_repr)
else:
field_val = getattr(result, f.attname)
@@ -187,7 +185,7 @@ def items_for_result(cl, result):
else:
result_repr = escape(field_val)
if force_unicode(result_repr) == '':
- result_repr = mark_safe(' ')
+ result_repr = ' '
# If list_display_links not defined, add the link tag to the first field
if (first and not cl.list_display_links) or field_name in cl.list_display_links:
table_tag = {True:'th', False:'td'}[first]
diff --git a/django/contrib/admin/tests/__init__.py b/django/contrib/admin/tests/__init__.py
new file mode 100644
index 0000000000..ca064cbbf9
--- /dev/null
+++ b/django/contrib/admin/tests/__init__.py
@@ -0,0 +1,5 @@
+from django.contrib.admin.tests import widgets
+
+__test__ = {
+ 'WIDGET_TESTS': widgets,
+}
diff --git a/django/contrib/admin/tests/widgets.py b/django/contrib/admin/tests/widgets.py
new file mode 100644
index 0000000000..e2c8c68916
--- /dev/null
+++ b/django/contrib/admin/tests/widgets.py
@@ -0,0 +1,35 @@
+"""
+>>> from datetime import datetime
+>>> from django.utils.html import escape, conditional_escape
+>>> from django.contrib.admin.widgets import FilteredSelectMultiple, AdminSplitDateTime
+>>> from django.contrib.admin.widgets import AdminFileWidget, ForeignKeyRawIdWidget
+>>> from django.contrib.admin.widgets import RelatedFieldWidgetWrapper
+>>> from django.contrib.admin.models import LogEntry
+
+Calling conditional_escape on the output of widget.render will simulate what
+happens in the template. This is easier than setting up a template and context
+for each test.
+
+Make sure that the Admin widgets render properly, that is, without their extra
+HTML escaped.
+
+>>> w = FilteredSelectMultiple('test', False)
+>>> print conditional_escape(w.render('test', 'test'))
+
+
+
+>>> w = AdminSplitDateTime()
+>>> print conditional_escape(w.render('test', datetime(2007, 12, 1, 9, 30)))
+
Date: Time:
+
+>>> w = AdminFileWidget()
+>>> print conditional_escape(w.render('test', 'test'))
+Currently: test Change:
+
+>>> rel = LogEntry._meta.get_field('user').rel
+>>> w = ForeignKeyRawIdWidget(rel)
+>>> print conditional_escape(w.render('test', 'test', attrs={}))
+
+
+"""
diff --git a/django/contrib/admin/views/main.py b/django/contrib/admin/views/main.py
index 44012bc0cd..2a78b7ed75 100644
--- a/django/contrib/admin/views/main.py
+++ b/django/contrib/admin/views/main.py
@@ -10,6 +10,7 @@ from django.db.models.query import handle_legacy_orderlist, QuerySet
from django.http import Http404
from django.utils.encoding import force_unicode, smart_str
from django.utils.translation import ugettext
+from django.utils.safestring import mark_safe
import operator
try:
diff --git a/django/contrib/admin/widgets.py b/django/contrib/admin/widgets.py
index cbef69ce01..26aacf7a03 100644
--- a/django/contrib/admin/widgets.py
+++ b/django/contrib/admin/widgets.py
@@ -6,6 +6,7 @@ from django import newforms as forms
from django.utils.datastructures import MultiValueDict
from django.utils.text import capfirst
from django.utils.translation import ugettext as _
+from django.utils.safestring import mark_safe
from django.conf import settings
class FilteredSelectMultiple(forms.SelectMultiple):
@@ -28,7 +29,7 @@ class FilteredSelectMultiple(forms.SelectMultiple):
# API to determine the ID dynamically.
output.append(u'SelectFilter.init("id_%s", "%s", %s, "%s"); });\n' % \
(name, self.verbose_name.replace('"', '\\"'), int(self.is_stacked), settings.ADMIN_MEDIA_PREFIX))
- return u''.join(output)
+ return mark_safe(u''.join(output))
class AdminDateWidget(forms.TextInput):
class Media:
@@ -57,8 +58,8 @@ class AdminSplitDateTime(forms.SplitDateTimeWidget):
forms.MultiWidget.__init__(self, widgets, attrs)
def format_output(self, rendered_widgets):
- return u'