Fixed #2986 -- Made the JavaScript code that drives related model instance 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
This commit is contained in:
parent
544ab30ed7
commit
0f783b7f4e
|
@ -19,7 +19,7 @@ from django.shortcuts import get_object_or_404, render_to_response
|
||||||
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
|
||||||
|
@ -717,7 +717,7 @@ class ModelAdmin(BaseModelAdmin):
|
||||||
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)
|
||||||
|
|
|
@ -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):
|
||||||
|
@ -745,7 +726,6 @@ timesince.is_safe = False
|
||||||
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:
|
||||||
|
|
|
@ -34,6 +34,31 @@ def escape(html):
|
||||||
return mark_safe(force_unicode(html).replace('&', '&').replace('<', '<').replace('>', '>').replace('"', '"').replace("'", '''))
|
return mark_safe(force_unicode(html).replace('&', '&').replace('<', '<').replace('>', '>').replace('"', '"').replace("'", '''))
|
||||||
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.
|
||||||
|
|
|
@ -107,6 +107,22 @@ class AdminViewBasicTest(TestCase):
|
||||||
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",
|
||||||
|
|
|
@ -109,3 +109,15 @@ class TestUtilsHtml(unittest.TestCase):
|
||||||
)
|
)
|
||||||
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)
|
||||||
|
|
Loading…
Reference in New Issue