Code

Ticket #19577: 19577.2.diff

File 19577.2.diff, 5.2 KB (added by timo, 18 months ago)
Line 
1diff --git a/django/utils/html.py b/django/utils/html.py
2index 25605be..ec7b28d 100644
3--- a/django/utils/html.py
4+++ b/django/utils/html.py
5@@ -87,8 +87,8 @@ def format_html(format_string, *args, **kwargs):
6 
7 def format_html_join(sep, format_string, args_generator):
8     """
9-    A wrapper format_html, for the common case of a group of arguments that need
10-    to be formatted using the same format string, and then joined using
11+    A wrapper of format_html, for the common case of a group of arguments that
12+    need to be formatted using the same format string, and then joined using
13     'sep'. 'sep' is also passed through conditional_escape.
14 
15     'args_generator' should be an iterator that returns the sequence of 'args'
16diff --git a/docs/ref/contrib/admin/index.txt b/docs/ref/contrib/admin/index.txt
17index ee04d77..a862d55 100644
18--- a/docs/ref/contrib/admin/index.txt
19+++ b/docs/ref/contrib/admin/index.txt
20@@ -449,17 +449,25 @@ subclass::
21     * If the string given is a method of the model, ``ModelAdmin`` or a
22       callable, Django will HTML-escape the output by default. If you'd
23       rather not escape the output of the method, give the method an
24-      ``allow_tags`` attribute whose value is ``True``.
25+      ``allow_tags`` attribute whose value is ``True``. However, to avoid an
26+      XSS vulnerability, you should use :func:`~django.utils.html.format_html`
27+      to escape user-provided inputs.
28 
29       Here's a full example model::
30 
31+          from django.utils.html import format_html
32+
33           class Person(models.Model):
34               first_name = models.CharField(max_length=50)
35               last_name = models.CharField(max_length=50)
36               color_code = models.CharField(max_length=6)
37 
38               def colored_name(self):
39-                  return '<span style="color: #%s;">%s %s</span>' % (self.color_code, self.first_name, self.last_name)
40+                  return format_html('<span style="color: #{0};">{1} {2}</span>',
41+                                     self.color_code,
42+                                     self.first_name,
43+                                     self.last_name)
44+
45               colored_name.allow_tags = True
46 
47           class PersonAdmin(admin.ModelAdmin):
48@@ -500,12 +508,17 @@ subclass::
49 
50       For example::
51 
52+        from django.utils.html import format_html
53+
54         class Person(models.Model):
55             first_name = models.CharField(max_length=50)
56             color_code = models.CharField(max_length=6)
57 
58             def colored_first_name(self):
59-                return '<span style="color: #%s;">%s</span>' % (self.color_code, self.first_name)
60+                return format_html('<span style="color: #{0};">{1}</span>',
61+                                   self.color_code,
62+                                   self.first_name)
63+
64             colored_first_name.allow_tags = True
65             colored_first_name.admin_order_field = 'first_name'
66 
67@@ -817,19 +830,27 @@ subclass::
68     the admin interface to provide feedback on the status of the objects being
69     edited, for example::
70 
71+        from django.utils.html import format_html_join
72+        from django.utils.safestring import mark_safe
73+
74         class PersonAdmin(ModelAdmin):
75             readonly_fields = ('address_report',)
76 
77             def address_report(self, instance):
78-                return ", ".join(instance.get_full_address()) or \
79-                   "<span class='errors'>I can't determine this address.</span>"
80+                # assuming get_full_address() returns a list of strings
81+                # for each line of the address and you want to separate each
82+                # line by a linebreak
83+                return format_html_join(
84+                    mark_safe('<br/>'),
85+                    '{0}',
86+                    ((line,) for line in instance.get_full_address()),
87+                ) or "<span class='errors'>I can't determine this address.</span>"
88 
89             # short_description functions like a model field's verbose_name
90             address_report.short_description = "Address"
91             # in this example, we have used HTML tags in the output
92             address_report.allow_tags = True
93 
94-
95 .. attribute:: ModelAdmin.save_as
96 
97     Set ``save_as`` to enable a "save as" feature on admin change forms.
98diff --git a/docs/ref/utils.txt b/docs/ref/utils.txt
99index 20192ed..2dc007f 100644
100--- a/docs/ref/utils.txt
101+++ b/docs/ref/utils.txt
102@@ -541,6 +541,20 @@ escaping HTML.
103     through :func:`conditional_escape` which (ultimately) calls
104     :func:`~django.utils.encoding.force_text` on the values.
105 
106+.. function:: format_html_join(sep, format_string, args_generator)
107+
108+    A wrapper of :func:`format_html`, for the common case of a group of
109+    arguments that need to be formatted using the same format string, and then
110+    joined using ``sep``. ``sep`` is also passed through
111+    :func:`conditional_escape`.
112+
113+    ``args_generator`` should be an iterator that returns the sequence of
114+    ``args`` that will be passed to :func:`format_html`. For example::
115+
116+        format_html_join('\n', "<li>{0} {1}</li>", ((u.first_name, u.last_name)
117+                                                    for u in users))
118+
119+
120 .. function:: strip_tags(value)
121 
122     Removes anything that looks like an html tag from the string, that is