Skip to content

Commit

Permalink
[1.5.x] Fixed #19577 - Added HTML escaping to admin examples.
Browse files Browse the repository at this point in the history
Thanks foo@ for the report and Florian Apolloner for the review.

Backport of eafc036 from master
  • Loading branch information
timgraham committed Jan 25, 2013
1 parent d571894 commit 42fcfca
Show file tree
Hide file tree
Showing 3 changed files with 42 additions and 8 deletions.
4 changes: 2 additions & 2 deletions django/utils/html.py
Original file line number Diff line number Diff line change
Expand Up @@ -87,8 +87,8 @@ def format_html(format_string, *args, **kwargs):

def format_html_join(sep, format_string, args_generator):
"""
A wrapper format_html, for the common case of a group of arguments that need
to be formatted using the same format string, and then joined using
A wrapper of format_html, for the common case of a group of arguments that
need to be formatted using the same format string, and then joined using
'sep'. 'sep' is also passed through conditional_escape.
'args_generator' should be an iterator that returns the sequence of 'args'
Expand Down
33 changes: 27 additions & 6 deletions docs/ref/contrib/admin/index.txt
Original file line number Diff line number Diff line change
Expand Up @@ -451,17 +451,25 @@ subclass::
* If the string given is a method of the model, ``ModelAdmin`` or a
callable, Django will HTML-escape the output by default. If you'd
rather not escape the output of the method, give the method an
``allow_tags`` attribute whose value is ``True``.
``allow_tags`` attribute whose value is ``True``. However, to avoid an
XSS vulnerability, you should use :func:`~django.utils.html.format_html`
to escape user-provided inputs.

Here's a full example model::

from django.utils.html import format_html

class Person(models.Model):
first_name = models.CharField(max_length=50)
last_name = models.CharField(max_length=50)
color_code = models.CharField(max_length=6)

def colored_name(self):
return '<span style="color: #%s;">%s %s</span>' % (self.color_code, self.first_name, self.last_name)
return format_html('<span style="color: #{0};">{1} {2}</span>',
self.color_code,
self.first_name,
self.last_name)

colored_name.allow_tags = True

class PersonAdmin(admin.ModelAdmin):
Expand Down Expand Up @@ -502,12 +510,17 @@ subclass::

For example::

from django.utils.html import format_html

class Person(models.Model):
first_name = models.CharField(max_length=50)
color_code = models.CharField(max_length=6)

def colored_first_name(self):
return '<span style="color: #%s;">%s</span>' % (self.color_code, self.first_name)
return format_html('<span style="color: #{0};">{1}</span>',
self.color_code,
self.first_name)

colored_first_name.allow_tags = True
colored_first_name.admin_order_field = 'first_name'

Expand Down Expand Up @@ -833,19 +846,27 @@ subclass::
the admin interface to provide feedback on the status of the objects being
edited, for example::

from django.utils.html import format_html_join
from django.utils.safestring import mark_safe

class PersonAdmin(ModelAdmin):
readonly_fields = ('address_report',)

def address_report(self, instance):
return ", ".join(instance.get_full_address()) or \
"<span class='errors'>I can't determine this address.</span>"
# assuming get_full_address() returns a list of strings
# for each line of the address and you want to separate each
# line by a linebreak
return format_html_join(
mark_safe('<br/>'),
'{0}',
((line,) for line in instance.get_full_address()),
) or "<span class='errors'>I can't determine this address.</span>"

# short_description functions like a model field's verbose_name
address_report.short_description = "Address"
# in this example, we have used HTML tags in the output
address_report.allow_tags = True


.. attribute:: ModelAdmin.save_as

Set ``save_as`` to enable a "save as" feature on admin change forms.
Expand Down
13 changes: 13 additions & 0 deletions docs/ref/utils.txt
Original file line number Diff line number Diff line change
Expand Up @@ -543,6 +543,19 @@ escaping HTML.
through :func:`conditional_escape` which (ultimately) calls
:func:`~django.utils.encoding.force_text` on the values.

.. function:: format_html_join(sep, format_string, args_generator)

A wrapper of :func:`format_html`, for the common case of a group of
arguments that need to be formatted using the same format string, and then
joined using ``sep``. ``sep`` is also passed through
:func:`conditional_escape`.

``args_generator`` should be an iterator that returns the sequence of
``args`` that will be passed to :func:`format_html`. For example::

format_html_join('\n', "<li>{0} {1}</li>", ((u.first_name, u.last_name)
for u in users))

.. function:: strip_tags(value)

Removes anything that looks like an html tag from the string, that is
Expand Down

0 comments on commit 42fcfca

Please sign in to comment.