Commit aee05410 by Jonathan Piacenti

Better handling of JSON.

parent 7f5affef
......@@ -12,6 +12,22 @@ from xblockutils.resources import ResourceLoader
from .utils import process_markdown
# When changing these constants, check the templates as well for places
# where the user is informed about them.
MAX_PARAGRAPH_LEN = 5000
MAX_URL_LEN = 1000
MAX_ANSWER_LEN = 250
# These two don't have mentions in the templates, but will cause error
# messages.
MAX_ANSWERS = 25
MAX_KEY_LEN = 100
class PollBlock(XBlock):
"""
......@@ -195,53 +211,51 @@ class PollBlock(XBlock):
@XBlock.json_handler
def studio_submit(self, data, suffix=''):
# I wonder if there's something for live validation feedback already.
result = {'success': True, 'errors': []}
if 'question' not in data or not data['question']:
question = data.get('question', '').strip()[:MAX_PARAGRAPH_LEN]
feedback = data.get('feedback', '').strip()[:MAX_PARAGRAPH_LEN]
if not question:
result['errors'].append("You must specify a question.")
result['success'] = False
answers = []
if 'answers' not in data or not isinstance(data['answers'], list):
source_answers = []
result['success'] = False
result['errors'].append(
"'answers' is not present, or not a JSON array.")
else:
question = data['question'][:4096]
if 'feedback' not in data or not data['feedback'].strip():
feedback = ''
else:
feedback = data['feedback'][:4096]
# Need this meta information, otherwise the questions will be
# shuffled by Python's dictionary data type.
poll_order = [
key.strip().replace('answer-', '')
for key in data.get('poll_order', [])
]
# Aggressively clean/sanity check answers list.
answers = {}
for key, value in data.items():
img = False
text = False
if key.startswith('answer-'):
text = 'label'
if key.startswith('img-answer-'):
img = 'img'
if not (text or img):
continue
key = key.replace('answer-', '').replace('img-', '')
if not key or key.isspace():
continue
value = value.strip()[:250]
if not value or value.isspace():
continue
update_dict = {img or text: value}
if key in answers:
answers[key].update(update_dict)
continue
if key in poll_order:
answers[key] = update_dict
source_answers = data['answers']
for value in answers.values():
if 'label' not in value:
value['label'] = None
if 'img' not in value:
value['img'] = None
# Set a reasonable limit to the number of answers in a poll.
if len(source_answers) > MAX_ANSWERS:
result['success'] = False
result['errors'].append("")
# Make sure all components are present and clean them.
for answer in source_answers:
if not isinstance(answer, dict):
result['success'] = False
result['errors'].append(
"Answer {0} not a javascript object!".format(answer))
continue
key = answer.get('key', '').strip()
if not key:
result['success'] = False
result['errors'].append(
"Answer {0} contains no key.".format(answer))
if len(key) > MAX_KEY_LEN:
result['success'] = False
result['errors'].append("Key '{0}' too long.".format(key))
img = answer.get('img', '').strip()[:MAX_URL_LEN]
label = answer.get('label', '').strip()[:MAX_ANSWER_LEN]
if not (img or label):
result['success'] = False
result['errors'].append(
"Answer {0} has no text or img. One is needed.")
answers.append((key, {'label': label, 'img': img}))
if not len(answers) > 1:
result['errors'].append(
......@@ -251,10 +265,6 @@ class PollBlock(XBlock):
if not result['success']:
return result
# Need to sort the answers.
answers = list(answers.items())
answers.sort(key=lambda x: poll_order.index(x[0]))
self.answers = answers
self.question = question
self.feedback = feedback
......
......@@ -11,8 +11,11 @@
<div class="poll-move-down">&#9660;</div>
</div>
</div>
<span class="tip setting-help">Enter an answer for the user to select. An answer must have an image URL or text, and can have both.</span>
<a href="#" class="button action-button poll-delete-answer">Delete</a>
<span class="tip setting-help">
Enter an answer for the user to select. An answer must have an image URL or text, and can have both.
(Text truncated at 250 characters, Image URL at 1000)
</span>
<a href="#" class="button action-button poll-delete-answer" onclick="return false;">Delete</a>
</li>
{{/each}}
</script>
\ No newline at end of file
......@@ -8,7 +8,7 @@
<div id="poll-question-editor-container">
<textarea class="input setting-input" name="question" id="poll-question-editor">{{question}}</textarea>
</div>
<span class="tip setting-help">Enter the prompt for the user.</span>
<span class="tip setting-help">Enter the prompt for the user. (Truncated after 5000 characters)</span>
</li>
<li class="field comp-setting-entry is-set">
<h2><label for="poll-feedback-editor">Feedback</label></h2>
......@@ -18,7 +18,7 @@
</div>
<span class="tip setting-help">
This text will be displayed for the user as some extra feedback after they have
submitted their response to the poll.
submitted their response to the poll. (Truncated after 5000 characters)
</span>
</li>
<li class="field comp-setting-entry is-set">
......@@ -34,10 +34,10 @@
<div class="xblock-actions">
<ul>
<li class="action-item" id="poll-add-answer">
<a href="#" class="button action-button" class="poll-add-answer-link">Add Answer</a>
<a href="#" class="button action-button" class="poll-add-answer-link" onclick="return false;">Add Answer</a>
</li>
<li class="action-item">
<input type="submit" class="button action-primary save-button" value="Save" />
<input id="poll-submit-options" type="submit" class="button action-primary save-button" value="Save" onclick="return false;" />
</li>
<li class="action-item">
<a href="#" class="button cancel-button">Cancel</a>
......
......@@ -52,32 +52,35 @@ function PollEditBlock(runtime, element) {
new_answer.fadeOut(250).fadeIn(250);
});
var to_disable = ['#poll-add-answer-link', 'input[type=submit', '.poll-delete-answer'];
for (var selector in to_disable) {
$(selector, element).click(function(event) {
event.preventDefault();
}
)
}
$(element).find('.cancel-button', element).bind('click', function() {
runtime.notify('cancel', {});
});
$(element).find('.save-button', element).bind('click', function() {
var handlerUrl = runtime.handlerUrl(element, 'studio_submit');
var data = {};
var poll_order = [];
var data = {'answers': []};
var tracker = [];
$('#poll-form input', element).each(function(i) {
data[this.name] = this.value;
if (this.name.indexOf('answer-') == 0){
poll_order.push(this.name);
var key = 'label';
if (this.name.indexOf('answer-') >= 0){
var name = this.name.replace('answer-', '');
if (this.name.indexOf('img-') == 0){
name = name.replace('img-', '');
key = 'img'
}
if (tracker.indexOf(name) == -1){
tracker.push(name);
data['answers'].push({'key': name})
}
var index = tracker.indexOf(name);
data['answers'][index][key] = this.value;
return
}
data[this.name] = this.value
});
data['title'] = $('#poll-title', element).val();
data['question'] = $('#poll-question-editor', element).val();
data['feedback'] = $('#poll-feedback-editor', element).val();
data['poll_order'] = poll_order;
function check_return(data) {
if (data['success']) {
window.location.reload(false);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment