Commit db084466 by Jonathan Piacenti

Several CSS fixes. Dropped title field. Fixed markdown handling. Added Feedback.

parent 13de8d1b
"""TO-DO: Write a description of what this XBlock is.""" """TO-DO: Write a description of what this XBlock is."""
from collections import OrderedDict from collections import OrderedDict
import bleach
from django.template import Template, Context from django.template import Template, Context
from bleach import clean
from markdown import markdown from markdown import markdown
import pkg_resources import pkg_resources
...@@ -12,20 +13,31 @@ from xblock.fields import Scope, List, String, Dict ...@@ -12,20 +13,31 @@ from xblock.fields import Scope, List, String, Dict
from xblock.fragment import Fragment from xblock.fragment import Fragment
ALLOWED_TAGS = {
'h1': [], 'h2': [], 'h3': [], 'h4': [], 'h5': [], 'h6': [],
'a': ['target', 'href', 'class'], 'strong': [], 'em': [], 'blockquote': [],
'pre': [], 'li': [], 'ul': [], 'ol': [], 'code': ['class'], 'p': [],
}
def process_markdown(raw_text):
return bleach.clean(markdown(raw_text), tags=ALLOWED_TAGS, strip_comments=False)
class PollBlock(XBlock): class PollBlock(XBlock):
""" """
Poll XBlock. Allows a teacher to poll users, and presents the results so Poll XBlock. Allows a teacher to poll users, and presents the results so
far of the poll to the user when finished. far of the poll to the user when finished.
""" """
title = String(default='Poll')
question = String(default='What is your favorite color?') question = String(default='What is your favorite color?')
answers = List( answers = List(
default=(('Red', 'Red'), ('Blue', 'Blue'), ('Green', 'Green'), default=(('Red', 'Red'), ('Blue', 'Blue'), ('Green', 'Green'),
('Other', 'Other')), ('Other', 'Other')),
scope=Scope.settings, help="The questions on this poll." scope=Scope.settings, help="The question on this poll."
) )
feedback = String(help="Text to display after the user votes.")
tally = Dict(default={'Red': 0, 'Blue': 0, 'Green': 0, 'Other': 0}, tally = Dict(default={'Red': 0, 'Blue': 0, 'Green': 0, 'Other': 0},
scope=Scope.content, scope=Scope.user_state_summary,
help="Total tally of answers from students.") help="Total tally of answers from students.")
choice = String(scope=Scope.user_state, help="The student's answer") choice = String(scope=Scope.user_state, help="The student's answer")
...@@ -36,7 +48,12 @@ class PollBlock(XBlock): ...@@ -36,7 +48,12 @@ class PollBlock(XBlock):
@XBlock.json_handler @XBlock.json_handler
def get_results(self, data, suffix=''): def get_results(self, data, suffix=''):
return {'question': markdown(clean(self.question)), 'tally': self.tally_detail()} detail, total = self.tally_detail()
print process_markdown(self.feedback)
return {
'question': process_markdown(self.question), 'tally': detail,
'total': total, 'feedback': process_markdown(self.feedback),
}
def tally_detail(self): def tally_detail(self):
""" """
...@@ -52,7 +69,8 @@ class PollBlock(XBlock): ...@@ -52,7 +69,8 @@ class PollBlock(XBlock):
'answer': value, 'answer': value,
'key': key, 'key': key,
'top': False, 'top': False,
'choice': False 'choice': False,
'last': False,
}) })
total += tally[-1]['count'] total += tally[-1]['count']
...@@ -71,7 +89,8 @@ class PollBlock(XBlock): ...@@ -71,7 +89,8 @@ class PollBlock(XBlock):
if tally: if tally:
# Mark the top item to make things easier for Handlebars. # Mark the top item to make things easier for Handlebars.
tally[0]['top'] = True tally[0]['top'] = True
return tally tally[-1]['last'] = True
return tally, total
def get_choice(self): def get_choice(self):
""" """
...@@ -98,17 +117,20 @@ class PollBlock(XBlock): ...@@ -98,17 +117,20 @@ class PollBlock(XBlock):
choice = self.get_choice() choice = self.get_choice()
print bleach.ALLOWED_ATTRIBUTES
context.update({ context.update({
'choice': choice, 'choice': choice,
# Offset so choices will always be True. # Offset so choices will always be True.
'answers': self.answers, 'answers': self.answers,
'question': markdown(clean(self.question)), 'question': process_markdown(self.question),
'title': self.title, 'feedback': process_markdown(self.feedback),
'js_template': js_template, 'js_template': js_template,
}) })
if self.choice: if self.choice:
context.update({'tally': self.tally_detail()}) detail, total = self.tally_detail()
context.update({'tally': detail, 'total': total})
context = Context(context) context = Context(context)
html = self.resource_string("public/html/poll.html") html = self.resource_string("public/html/poll.html")
...@@ -134,8 +156,8 @@ class PollBlock(XBlock): ...@@ -134,8 +156,8 @@ class PollBlock(XBlock):
js_template = self.resource_string('/public/handlebars/studio.handlebars') js_template = self.resource_string('/public/handlebars/studio.handlebars')
context.update({ context.update({
'question': clean(self.question), 'question': self.question,
'title': self.title, 'feedback': self.feedback,
'js_template': js_template 'js_template': js_template
}) })
context = Context(context) context = Context(context)
...@@ -155,14 +177,15 @@ class PollBlock(XBlock): ...@@ -155,14 +177,15 @@ class PollBlock(XBlock):
def studio_submit(self, data, suffix=''): def studio_submit(self, data, suffix=''):
# I wonder if there's something for live validation feedback already. # I wonder if there's something for live validation feedback already.
result = {'success': True, 'errors': []} result = {'success': True, 'errors': []}
if 'title' not in data or not data['title']:
# This can be blank, if it needs to be.
title = ''
else:
title = data['title'][:200]
if 'question' not in data or not data['question']: if 'question' not in data or not data['question']:
result['errors'].append("You must specify a question.") result['errors'].append("You must specify a question.")
result['success'] = False result['success'] = False
else:
question = data['question'][:4096]
if 'feedback' not in data or not data['feedback']:
feedback = ''
else:
feedback = data['feedback'][:4096]
# Need this meta information, otherwise the questions will be # Need this meta information, otherwise the questions will be
# shuffled by Python's dictionary data type. # shuffled by Python's dictionary data type.
...@@ -182,7 +205,6 @@ class PollBlock(XBlock): ...@@ -182,7 +205,6 @@ class PollBlock(XBlock):
continue continue
if key in poll_order: if key in poll_order:
answers.append((key, value)) answers.append((key, value))
self.title = title
if not len(answers) > 1: if not len(answers) > 1:
result['errors'].append( result['errors'].append(
...@@ -196,9 +218,8 @@ class PollBlock(XBlock): ...@@ -196,9 +218,8 @@ class PollBlock(XBlock):
answers.sort(key=lambda x: poll_order.index(x[0]), reverse=True) answers.sort(key=lambda x: poll_order.index(x[0]), reverse=True)
self.answers = answers self.answers = answers
self.question = data['question'] self.question = question
self.feedback = feedback
print answers
tally = self.tally tally = self.tally
......
...@@ -18,7 +18,7 @@ ...@@ -18,7 +18,7 @@
display: table-cell; display: table-cell;
width: 100%; width: 100%;
vertical-align: middle; vertical-align: middle;
padding-bottom: .2em; background-color: #fafbfc;
} }
ul.poll-answers, ul.poll-answers-results { ul.poll-answers, ul.poll-answers-results {
...@@ -28,6 +28,10 @@ ul.poll-answers, ul.poll-answers-results { ...@@ -28,6 +28,10 @@ ul.poll-answers, ul.poll-answers-results {
li.poll-answer { li.poll-answer {
display: block; display: block;
border-bottom-width: .5em;
}
li.poll-spacer {
height: .25em;
} }
ul.poll-answers-results { ul.poll-answers-results {
...@@ -47,16 +51,20 @@ li.poll-result { ...@@ -47,16 +51,20 @@ li.poll-result {
.poll-percent-container { .poll-percent-container {
display: table-cell; display: table-cell;
font-weight: bold;
text-align: left; text-align: left;
padding-left: .2em; padding-left: .2em;
vertical-align: middle; vertical-align: middle;
} }
.poll-result-answer-container { .poll-percent-display {
font-weight: bold;
} }
.poll-top-choice { .poll-top-choice {
color: #e37222; color: #e37222;
} }
.poll-footnote {
margin-top: 1em;
margin-bottom: 1em;
}
\ No newline at end of file
...@@ -3,11 +3,11 @@ ...@@ -3,11 +3,11 @@
.poll-delete-answer { .poll-delete-answer {
float: right; float: right;
} }
#question-editor-container{ #poll-question-editor-container, #poll-feedback-editor-container{
width: 100%; width: 100%;
text-align: center; text-align: center;
} }
#question-editor{ #poll-question-editor, #poll-feedback-editor{
width: 98%; width: 98%;
height: 7em; height: 7em;
text-align: left; text-align: left;
...@@ -20,7 +20,7 @@ ...@@ -20,7 +20,7 @@
padding: 10px; padding: 10px;
} }
h2 label { label.poll-label {
font-weight: bold; font-weight: bold;
font-size: 16pt; font-size: 16pt;
} }
\ No newline at end of file
...@@ -4,7 +4,7 @@ ...@@ -4,7 +4,7 @@
{{#each tally}} {{#each tally}}
<li class="poll-result"> <li class="poll-result">
<div class="poll-result-input-container"> <div class="poll-result-input-container">
<input id="answer-{{key}}" type="radio" disabled {{#if choice}}checked="True"{{/if}} /> <input id="answer-{{key}}" type="radio" disabled {{#choice}}checked="True"{{/choice}} />
</div> </div>
<div class="percentage-gauge-container"> <div class="percentage-gauge-container">
<div class="percentage-gauge" style="width:{{percent}}%;"> <div class="percentage-gauge" style="width:{{percent}}%;">
...@@ -12,9 +12,21 @@ ...@@ -12,9 +12,21 @@
</div> </div>
</div> </div>
<div class="poll-percent-container"> <div class="poll-percent-container">
<span class="poll-percent-display{{#if top}} poll-top-choice{{/if}}">{{percent}}%</span> <span class="poll-percent-display{{#top}} poll-top-choice{{/top}}">{{percent}}%</span>
</div> </div>
</li> </li>
{{^last}}
<li class="poll-spacer">
</li>
{{/last}}
{{/each}} {{/each}}
</ul> </ul>
<input type="submit" name="poll-submit" onclick="return false;" value="Submit" disabled>
<div class="poll-footnote"><small>Results gathered from {{total}} respondent(s).</small></div>
{{#feedback}}
<hr />
<div class="poll-feedback">
{{{this}}}
</div>
{{/feedback}}
</script> </script>
\ No newline at end of file
...@@ -3,7 +3,6 @@ ...@@ -3,7 +3,6 @@
{# If no form is present, the Javascript will load the results instead. #} {# If no form is present, the Javascript will load the results instead. #}
{% if not choice %} {% if not choice %}
<form> <form>
<h2>{{ title }}</h2>
{{question|safe}} {{question|safe}}
<ul class="poll-answers"> <ul class="poll-answers">
{% for key, answer in answers %} {% for key, answer in answers %}
......
...@@ -3,18 +3,23 @@ ...@@ -3,18 +3,23 @@
<form id="poll-form"> <form id="poll-form">
<ul class="list-input settings-list" id="poll-line-items"> <ul class="list-input settings-list" id="poll-line-items">
<li class="field comp-setting-entry is-set"> <li class="field comp-setting-entry is-set">
<div class="wrapper-comp-setting"> <h2><label for="poll-question-editor">Question/Prompt</label></h2>
<label class="label setting-label" for="poll-title">Title</label> <a href="//daringfireball.net/projects/markdown/syntax" target="_blank">Markdown Syntax</a> is supported.
<input class="input setting-input" name="title" id="poll-title" value="{{title}}" type="text" /> <div id="poll-question-editor-container">
<textarea class="input setting-input" name="question" id="poll-question-editor">{{question}}</textarea>
</div> </div>
<span class="tip setting-help">Enter a title to act as the header for this Poll.</span> <span class="tip setting-help">Enter the prompt for the user.</span>
</li> </li>
<li class="field comp-setting-entry is-set"> <li class="field comp-setting-entry is-set">
<h2><label for="question-editor">Question/Prompt</label></h2> <h2><label for="poll-feedback-editor">Feedback</label></h2>
<a href="//daringfireball.net/projects/markdown/syntax" target="_blank">Markdown Syntax</a> is supported. <a href="//daringfireball.net/projects/markdown/syntax" target="_blank">Markdown Syntax</a> is supported.
<div id="question-editor-container"> <div id="poll-feedback-editor-container">
<textarea class="input setting-input" name="question" id="question-editor">{{question|safe}}</textarea> <textarea class="input setting-input" name="feedback" id="poll-feedback-editor">{{feedback}}</textarea>
</div> </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.
</span>
</li> </li>
<li class="field comp-setting-entry is-set"> <li class="field comp-setting-entry is-set">
<p> <p>
......
...@@ -47,7 +47,8 @@ function PollEditBlock(runtime, element) { ...@@ -47,7 +47,8 @@ function PollEditBlock(runtime, element) {
} }
}); });
data['title'] = $('#poll-title', element).val(); data['title'] = $('#poll-title', element).val();
data['question'] = $('#question-editor', element).val(); data['question'] = $('#poll-question-editor', element).val();
data['feedback'] = $('#poll-feedback-editor', element).val();
data['poll_order'] = poll_order; data['poll_order'] = poll_order;
function check_return(data) { function check_return(data) {
if (data['success']) { if (data['success']) {
......
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