Fix on async messaging to don't escape message string marked as safe.

Fixes bug 1103243.

Change-Id: Ia9541d5cc338914f5f9164e7ee1386270211f4d0
This commit is contained in:
Lin Hua Cheng 2013-02-05 14:45:47 -08:00
parent a366264d62
commit 43e8ea4d4d
7 changed files with 56 additions and 9 deletions

View File

@ -22,6 +22,7 @@ messaging needs (e.g. AJAX communication, etc.).
from django.contrib import messages as _messages
from django.contrib.messages import constants
from django.utils.encoding import force_unicode
from django.utils.safestring import SafeData
def add_message(request, level, message, extra_tags='', fail_silently=False):
@ -30,8 +31,13 @@ def add_message(request, level, message, extra_tags='', fail_silently=False):
"""
if request.is_ajax():
tag = constants.DEFAULT_TAGS[level]
# if message is marked as safe, pass "safe" tag as extra_tags so that
# client can skip HTML escape for the message when rendering
if isinstance(message, SafeData):
extra_tags = extra_tags + ' safe'
request.horizon['async_messages'].append([tag,
force_unicode(message)])
force_unicode(message),
extra_tags])
else:
return _messages.add_message(request, level, message,
extra_tags, fail_silently)

View File

@ -98,8 +98,8 @@ class HorizonMiddleware(object):
# Drop our messages back into the session as per usual so they
# don't disappear during the redirect. Not that we explicitly
# use django's messages methods here.
for tag, message in queued_msgs:
getattr(django_messages, tag)(request, message)
for tag, message, extra_tags in queued_msgs:
getattr(django_messages, tag)(request, message, extra_tags)
redirect_response = http.HttpResponse()
redirect_response['X-Horizon-Location'] = response['location']
return redirect_response

View File

@ -1,9 +1,15 @@
horizon.alert = function (type, message) {
horizon.alert = function (type, message, extra_tags) {
safe = false
// Check if the message is tagged as safe.
if (typeof(extra_tags) !== "undefined" && _.contains(extra_tags.split(' '), 'safe')) {
safe = true
}
var template = horizon.templates.compiled_templates["#alert_message_template"],
params = {
"type": type,
"type_capitalized": horizon.utils.capitalize(type),
"message": message
"message": message,
"safe": safe
};
return $(template.render(params)).hide().prependTo("#main_content .messages").fadeIn(100);
};
@ -26,7 +32,7 @@ horizon.addInitFunction(function () {
$("body").ajaxComplete(function(event, request, settings){
var message_array = $.parseJSON(horizon.ajax.get_messages(request));
$(message_array).each(function (index, item) {
horizon.alert(item[0], item[1]);
horizon.alert(item[0], item[1], item[2]);
});
});

View File

@ -25,4 +25,14 @@ horizon.addInitFunction(function () {
equal($('#main_content .messages .alert-success').length, 0, "Verify our success message was removed.");
equal($('#main_content .messages .alert').length, 0, "Verify no messages remain.");
});
test("Alert With HTML Tag", function () {
safe_string = "A safe message <a>here</a>!"
message = horizon.alert("success", safe_string, "safe");
ok(message, "Create a message with extra tag.");
ok((message.html().indexOf(safe_string ) != -1), 'Verify the message with HTML tag was not escaped.');
equal($('#main_content .messages .alert').length, 1, "Verify our message was added to the DOM.");
horizon.clearAllMessages();
equal($('#main_content .messages .alert').length, 0, "Verify our message was removed.");
});
});

View File

@ -7,7 +7,15 @@
{% jstemplate %}
<div class="alert alert-block fade in alert-[[type]]">
<a class="close" data-dismiss="alert" href="#">&times;</a>
<p><strong>[[type_capitalized]]: </strong>[[message]]</p>
<p>
<strong>[[type_capitalized]]: </strong>
[[#safe]]
[[[message]]]
[[/safe]]
[[^safe]]
[[message]]
[[/safe]]
</p>
</div>
{% endjstemplate %}
{% endblock %}

View File

@ -120,6 +120,7 @@ class JSTemplateNode(template.Node):
def render(self, context, ):
output = self.nodelist.render(context)
output = output.replace('[[[', '{{{').replace(']]]', '}}}')
output = output.replace('[[', '{{').replace(']]', '}}')
output = output.replace('[%', '{%').replace('%]', '%}')
return output
@ -128,7 +129,8 @@ class JSTemplateNode(template.Node):
@register.tag
def jstemplate(parser, token):
"""
Replaces ``[[`` and ``]]`` with ``{{`` and ``}}`` and
Replaces ``[[[`` and ``]]]`` with ``{{{`` and ``}}}``,
``[[`` and ``]]`` with ``{{`` and ``}}`` and
``[%`` and ``%]`` with ``{%`` and ``%}`` to avoid conflicts
with Django's template engine when using any of the Mustache-based
templating libraries.

View File

@ -18,6 +18,7 @@ import json
from django import http
from django.utils.encoding import force_unicode
from django.utils.safestring import mark_safe
from django.utils.translation import ugettext_lazy as _
from horizon import messages
@ -29,7 +30,21 @@ class MessageTests(test.TestCase):
def test_middleware_header(self):
req = self.request
string = _("Giant ants are attacking San Francisco!")
expected = ["error", force_unicode(string)]
expected = ["error", force_unicode(string), ""]
self.assertTrue("async_messages" in req.horizon)
self.assertItemsEqual(req.horizon['async_messages'], [])
req.META['HTTP_X_REQUESTED_WITH'] = 'XMLHttpRequest'
messages.error(req, string)
self.assertItemsEqual(req.horizon['async_messages'], [expected])
res = http.HttpResponse()
res = middleware.HorizonMiddleware().process_response(req, res)
self.assertEqual(res['X-Horizon-Messages'],
json.dumps([expected]))
def test_safe_message(self):
req = self.request
string = mark_safe(_("We are now safe from ants! Go <a>here</a>!"))
expected = ["error", force_unicode(string), " safe"]
self.assertTrue("async_messages" in req.horizon)
self.assertItemsEqual(req.horizon['async_messages'], [])
req.META['HTTP_X_REQUESTED_WITH'] = 'XMLHttpRequest'