implement RichTextBlock, and ensure that script tags embedded in html_declarations bodies are properly escaped

This commit is contained in:
Matt Westcott 2015-02-05 16:23:31 +00:00
parent 959feec13a
commit f3a66d34af
4 changed files with 31 additions and 12 deletions

View file

@ -19,6 +19,8 @@ from django.forms.utils import ErrorList
import six
from wagtail.wagtailcore.utils import escape_script
# helpers for Javascript expression formatting
def indent(string, depth=1):
@ -292,6 +294,11 @@ class CharBlock(FieldBlock):
# TODO: some kwargs, such as max_length, and *possibly* things like help_text, should be passed to
# the CharField constructor. Figure out a system for doing this
class RichTextBlock(FieldBlock):
def __init__(self, **kwargs):
from wagtail.wagtailcore.fields import RichTextArea
super(RichTextBlock, self).__init__(CharField(widget=RichTextArea), **kwargs)
# =======
# Chooser
# =======
@ -517,7 +524,7 @@ class ListBlock(Block):
return format_html(
'<script type="text/template" id="{0}-newmember">{1}</script>',
self.definition_prefix, list_member_html
self.definition_prefix, mark_safe(escape_script(list_member_html))
)
def js_initializer(self):
@ -641,7 +648,7 @@ class BaseStreamBlock(Block):
(
self.definition_prefix,
name,
self.render_list_member(name, child_block.default, '__PREFIX__', '')
mark_safe(escape_script(self.render_list_member(name, child_block.default, '__PREFIX__', '')))
)
for name, child_block in self.child_blocks.items()
]

View file

@ -69,11 +69,17 @@ For example, they don't assume the presence of a 'delete' button - it's up to th
newMember._markAdded();
}
function elementFromTemplate(template, newPrefix) {
/* generate a jquery object ready to be inserted into the list, based on the passed HTML template string.
'__PREFIX__' will be substituted with newPrefix, and script tags escaped as <-/script> will be un-escaped */
return $(template.replace(/__PREFIX__/g, newPrefix).replace(/<-(-*)\/script>/g, '<$1/script>'));
}
self.insertMemberBefore = function(otherMember, template) {
newMemberPrefix = getNewMemberPrefix();
/* Create the new list member element with the real prefix substituted in */
var elem = $(template.replace(/__PREFIX__/g, newMemberPrefix));
var elem = elementFromTemplate(template, newMemberPrefix);
otherMember.container.before(elem);
var newMember = SequenceMember(self, newMemberPrefix);
var index = otherMember.getIndex();
@ -94,7 +100,7 @@ For example, they don't assume the presence of a 'delete' button - it's up to th
newMemberPrefix = getNewMemberPrefix();
/* Create the new list member element with the real prefix substituted in */
var elem = $(template.replace(/__PREFIX__/g, newMemberPrefix));
var elem = elementFromTemplate(template, newMemberPrefix);
otherMember.container.after(elem);
var newMember = SequenceMember(self, newMemberPrefix);
var index = otherMember.getIndex() + 1;
@ -119,7 +125,7 @@ For example, they don't assume the presence of a 'delete' button - it's up to th
newMemberPrefix = getNewMemberPrefix();
/* Create the new list member element with the real prefix substituted in */
var elem = $(template.replace(/__PREFIX__/g, newMemberPrefix));
var elem = elementFromTemplate(template, newMemberPrefix);
list.prepend(elem);
var newMember = SequenceMember(self, newMemberPrefix);
@ -139,7 +145,7 @@ For example, they don't assume the presence of a 'delete' button - it's up to th
newMemberPrefix = getNewMemberPrefix();
/* Create the new list member element with the real prefix substituted in */
var elem = $(template.replace(/__PREFIX__/g, newMemberPrefix));
var elem = elementFromTemplate(template, newMemberPrefix);
list.append(elem);
var newMember = SequenceMember(self, newMemberPrefix);

View file

@ -1,14 +1,12 @@
from __future__ import unicode_literals
import re
from django.conf import settings
from django import template
from django.contrib.humanize.templatetags.humanize import intcomma
from wagtail.wagtailcore import hooks
from wagtail.wagtailcore.models import get_navigation_menu_items, UserPagePermissionsProxy, PageViewRestriction
from wagtail.wagtailcore.utils import camelcase_to_underscore
from wagtail.wagtailcore.utils import camelcase_to_underscore, escape_script
from wagtail.wagtailadmin.menu import admin_menu
@ -136,7 +134,6 @@ def usage_count_enabled():
class EscapeScriptNode(template.Node):
TAG_NAME = 'escapescript'
SCRIPT_RE = re.compile(r'<(-*)/script>')
def __init__(self, nodelist):
super(EscapeScriptNode, self).__init__()
@ -144,8 +141,7 @@ class EscapeScriptNode(template.Node):
def render(self, context):
out = self.nodelist.render(context)
escaped_out = self.SCRIPT_RE.sub(r'<-\1/script>', out)
return escaped_out
return escape_script(out)
@classmethod
def handle(cls, parser, token):

View file

@ -36,3 +36,13 @@ def resolve_model_string(model_string, default_app=None):
else:
raise LookupError("Can not resolve {0!r} into a model".format(model_string), model_string)
SCRIPT_RE = re.compile(r'<(-*)/script>')
def escape_script(text):
"""
Escape `</script>` tags in 'text' so that it can be placed within a `<script>` block without
accidentally closing it. A '-' character will be inserted for each time it is escaped:
`<-/script>`, `<--/script>` etc.
"""
return SCRIPT_RE.sub(r'<-\1/script>', text)