Commit 3297c09d authored by Ramiro Morales's avatar Ramiro Morales
Browse files

[1.2.X] Fixed #2986 -- Made the JavaScript code that drives related model...

[1.2.X] 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 to django.utils.html so it can be used from Python code. Thanks andrewwatts for the patch.

Backport of [15131] from trunk

git-svn-id: http://code.djangoproject.com/svn/django/branches/releases/1.2.X@15191 bcc190cf-cafb-0310-a4f2-bffc1f526a37
parent 58038a8b
Loading
Loading
Loading
Loading
+2 −2
Original line number Diff line number Diff line
@@ -18,7 +18,7 @@ from django.shortcuts import get_object_or_404, render_to_response
from django.utils.decorators import method_decorator
from django.utils.datastructures import SortedDict
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.functional import curry
from django.utils.text import capfirst, get_text_list
@@ -702,7 +702,7 @@ class ModelAdmin(BaseModelAdmin):
        if request.POST.has_key("_popup"):
            return HttpResponse('<script type="text/javascript">opener.dismissAddAnotherPopup(window, "%s", "%s");</script>' % \
                # escape() calls force_unicode.
                (escape(pk_value), escape(obj)))
                (escape(pk_value), escapejs(obj)))
        elif request.POST.has_key("_addanother"):
            self.message_user(request, msg + ' ' + (_("You may add another %s below.") % force_unicode(opts.verbose_name)))
            return HttpResponseRedirect(request.path)
+2 −22
Original line number Diff line number Diff line
@@ -64,29 +64,10 @@ def capfirst(value):
capfirst.is_safe=True
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):
    """Hex encodes characters for use in JavaScript strings."""
    for bad, good in _js_escapes:
        value = value.replace(bad, good)
    return value
    from django.utils.html import escapejs
    return escapejs(value)
escapejs = stringfilter(escapejs)

def fix_ampersands(value):
@@ -735,7 +716,6 @@ timesince.is_safe = False
def timeuntil(value, arg=None):
    """Formats a date as the time until that date (i.e. "4 days, 6 hours")."""
    from django.utils.timesince import timeuntil
    from datetime import datetime
    if not value:
        return u''
    try:
+25 −0
Original line number Diff line number Diff line
@@ -34,6 +34,31 @@ def escape(html):
    return mark_safe(force_unicode(html).replace('&', '&amp;').replace('<', '&lt;').replace('>', '&gt;').replace('"', '&quot;').replace("'", '&#39;'))
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):
    """
    Similar to escape(), except that it doesn't operate on pre-escaped strings.
+16 −0
Original line number Diff line number Diff line
@@ -101,6 +101,22 @@ class AdminViewBasicTest(TestCase):
        response = self.client.post('/test_admin/%s/admin_views/section/add/' % self.urlbit, post_data)
        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
    inline_post_data = {
        "name": u"Test section",
+12 −0
Original line number Diff line number Diff line
@@ -109,3 +109,15 @@ class TestUtilsHtml(unittest.TestCase):
        )
        for value, output in items:
            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)