Skip to content

Commit

Permalink
Fixed #2986 -- Made the JavaScript code that drives related model ins…
Browse files Browse the repository at this point in the history
…tance addition in a popup window handle a model representation containing new lines. Also, moved the escapejs functionality yoo django.utils.html so it can be used from Python code. Thanks andrewwatts for the patch.

git-svn-id: http://code.djangoproject.com/svn/django/trunk@15131 bcc190cf-cafb-0310-a4f2-bffc1f526a37
  • Loading branch information
ramiro committed Jan 2, 2011
1 parent 544ab30 commit 0f783b7
Show file tree
Hide file tree
Showing 5 changed files with 57 additions and 24 deletions.
4 changes: 2 additions & 2 deletions django/contrib/admin/options.py
Expand Up @@ -19,7 +19,7 @@
from django.utils.decorators import method_decorator from django.utils.decorators import method_decorator
from django.utils.datastructures import SortedDict from django.utils.datastructures import SortedDict
from django.utils.functional import update_wrapper from django.utils.functional import update_wrapper
from django.utils.html import escape from django.utils.html import escape, escapejs
from django.utils.safestring import mark_safe from django.utils.safestring import mark_safe
from django.utils.functional import curry from django.utils.functional import curry
from django.utils.text import capfirst, get_text_list from django.utils.text import capfirst, get_text_list
Expand Down Expand Up @@ -717,7 +717,7 @@ def response_add(self, request, obj, post_url_continue='../%s/'):
if "_popup" in request.POST: if "_popup" in request.POST:
return HttpResponse('<script type="text/javascript">opener.dismissAddAnotherPopup(window, "%s", "%s");</script>' % \ return HttpResponse('<script type="text/javascript">opener.dismissAddAnotherPopup(window, "%s", "%s");</script>' % \
# escape() calls force_unicode. # escape() calls force_unicode.
(escape(pk_value), escape(obj))) (escape(pk_value), escapejs(obj)))
elif "_addanother" in request.POST: elif "_addanother" in request.POST:
self.message_user(request, msg + ' ' + (_("You may add another %s below.") % force_unicode(opts.verbose_name))) self.message_user(request, msg + ' ' + (_("You may add another %s below.") % force_unicode(opts.verbose_name)))
return HttpResponseRedirect(request.path) return HttpResponseRedirect(request.path)
Expand Down
24 changes: 2 additions & 22 deletions django/template/defaultfilters.py
Expand Up @@ -64,29 +64,10 @@ def capfirst(value):
capfirst.is_safe=True capfirst.is_safe=True
capfirst = stringfilter(capfirst) capfirst = stringfilter(capfirst)


_base_js_escapes = (
('\\', r'\u005C'),
('\'', r'\u0027'),
('"', r'\u0022'),
('>', r'\u003E'),
('<', r'\u003C'),
('&', r'\u0026'),
('=', r'\u003D'),
('-', r'\u002D'),
(';', r'\u003B'),
(u'\u2028', r'\u2028'),
(u'\u2029', r'\u2029')
)

# Escape every ASCII character with a value less than 32.
_js_escapes = (_base_js_escapes +
tuple([('%c' % z, '\\u%04X' % z) for z in range(32)]))

def escapejs(value): def escapejs(value):
"""Hex encodes characters for use in JavaScript strings.""" """Hex encodes characters for use in JavaScript strings."""
for bad, good in _js_escapes: from django.utils.html import escapejs
value = value.replace(bad, good) return escapejs(value)
return value
escapejs = stringfilter(escapejs) escapejs = stringfilter(escapejs)


def fix_ampersands(value): def fix_ampersands(value):
Expand Down Expand Up @@ -745,7 +726,6 @@ def timesince(value, arg=None):
def timeuntil(value, arg=None): def timeuntil(value, arg=None):
"""Formats a date as the time until that date (i.e. "4 days, 6 hours").""" """Formats a date as the time until that date (i.e. "4 days, 6 hours")."""
from django.utils.timesince import timeuntil from django.utils.timesince import timeuntil
from datetime import datetime
if not value: if not value:
return u'' return u''
try: try:
Expand Down
25 changes: 25 additions & 0 deletions django/utils/html.py
Expand Up @@ -34,6 +34,31 @@ def escape(html):
return mark_safe(force_unicode(html).replace('&', '&amp;').replace('<', '&lt;').replace('>', '&gt;').replace('"', '&quot;').replace("'", '&#39;')) return mark_safe(force_unicode(html).replace('&', '&amp;').replace('<', '&lt;').replace('>', '&gt;').replace('"', '&quot;').replace("'", '&#39;'))
escape = allow_lazy(escape, unicode) escape = allow_lazy(escape, unicode)


_base_js_escapes = (
('\\', r'\u005C'),
('\'', r'\u0027'),
('"', r'\u0022'),
('>', r'\u003E'),
('<', r'\u003C'),
('&', r'\u0026'),
('=', r'\u003D'),
('-', r'\u002D'),
(';', r'\u003B'),
(u'\u2028', r'\u2028'),
(u'\u2029', r'\u2029')
)

# Escape every ASCII character with a value less than 32.
_js_escapes = (_base_js_escapes +
tuple([('%c' % z, '\\u%04X' % z) for z in range(32)]))

def escapejs(value):
"""Hex encodes characters for use in JavaScript strings."""
for bad, good in _js_escapes:
value = mark_safe(force_unicode(value).replace(bad, good))
return value
escapejs = allow_lazy(escapejs, unicode)

def conditional_escape(html): def conditional_escape(html):
""" """
Similar to escape(), except that it doesn't operate on pre-escaped strings. Similar to escape(), except that it doesn't operate on pre-escaped strings.
Expand Down
16 changes: 16 additions & 0 deletions tests/regressiontests/admin_views/tests.py
Expand Up @@ -107,6 +107,22 @@ def testBasicAddPost(self):
response = self.client.post('/test_admin/%s/admin_views/section/add/' % self.urlbit, post_data) response = self.client.post('/test_admin/%s/admin_views/section/add/' % self.urlbit, post_data)
self.assertEqual(response.status_code, 302) # redirect somewhere self.assertEqual(response.status_code, 302) # redirect somewhere


def testPopupAddPost(self):
"""
Ensure http response from a popup is properly escaped.
"""
post_data = {
'_popup': u'1',
'title': u'title with a new\nline',
'content': u'some content',
'date_0': u'2010-09-10',
'date_1': u'14:55:39',
}
response = self.client.post('/test_admin/%s/admin_views/article/add/' % self.urlbit, post_data)
self.failUnlessEqual(response.status_code, 200)
self.assertContains(response, 'dismissAddAnotherPopup')
self.assertContains(response, 'title with a new\u000Aline')

# Post data for edit inline # Post data for edit inline
inline_post_data = { inline_post_data = {
"name": u"Test section", "name": u"Test section",
Expand Down
12 changes: 12 additions & 0 deletions tests/regressiontests/utils/html.py
Expand Up @@ -109,3 +109,15 @@ def test_fix_ampersands(self):
) )
for value, output in items: for value, output in items:
self.check_output(f, value, output) self.check_output(f, value, output)

def test_escapejs(self):
f = html.escapejs
items = (
(u'"double quotes" and \'single quotes\'', u'\\u0022double quotes\\u0022 and \\u0027single quotes\\u0027'),
(ur'\ : backslashes, too', u'\\u005C : backslashes, too'),
(u'and lots of whitespace: \r\n\t\v\f\b', u'and lots of whitespace: \\u000D\\u000A\\u0009\\u000B\\u000C\\u0008'),
(ur'<script>and this</script>', u'\\u003Cscript\\u003Eand this\\u003C/script\\u003E'),
(u'paragraph separator:\u2029and line separator:\u2028', u'paragraph separator:\\u2029and line separator:\\u2028'),
)
for value, output in items:
self.check_output(f, value, output)

0 comments on commit 0f783b7

Please sign in to comment.