Commit 14fdfb43 by Braden MacDonald

Make Mentoring block itself studio-editable, remove <title> and <header> blocks

parent 00778d03
...@@ -6,5 +6,3 @@ from .mrq import MRQBlock ...@@ -6,5 +6,3 @@ from .mrq import MRQBlock
from .message import MentoringMessageBlock from .message import MentoringMessageBlock
from .table import MentoringTableBlock, MentoringTableColumnBlock, MentoringTableColumnHeaderBlock from .table import MentoringTableBlock, MentoringTableColumnBlock, MentoringTableColumnHeaderBlock
from .tip import TipBlock from .tip import TipBlock
from .title import TitleBlock
from .header import SharedHeaderBlock
# -*- coding: utf-8 -*-
#
# Copyright (C) 2014 Harvard
#
# Authors:
# Xavier Antoviaque <xavier@antoviaque.org>
# David Gabor Bodor <david.gabor.bodor@gmail.com>
#
# This software's license gives you freedom; you can copy, convey,
# propagate, redistribute and/or modify this program under the terms of
# the GNU Affero General Public License (AGPL) as published by the Free
# Software Foundation (FSF), either version 3 of the License, or (at your
# option) any later version of the AGPL published by the FSF.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero
# General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program in a file in the toplevel directory called
# "AGPLv3". If not, see <http://www.gnu.org/licenses/>.
#
from .html import HTMLBlock
class SharedHeaderBlock(HTMLBlock):
"""
A shared header block shown under the title.
"""
FIXED_CSS_CLASS = "shared-header"
pass
...@@ -22,18 +22,74 @@ ...@@ -22,18 +22,74 @@
# #
# Imports ########################################################### # Imports ###########################################################
from lxml import etree
from .common import BlockWithContent from xblock.core import XBlock
from xblock.fields import Scope, String from xblock.fields import Scope, String
from xblock.fragment import Fragment
from xblockutils.studio_editable import StudioEditableXBlockMixin
# Classes ########################################################### # Classes ###########################################################
class MentoringMessageBlock(BlockWithContent): class MentoringMessageBlock(XBlock, StudioEditableXBlockMixin):
""" """
A message which can be conditionally displayed at the mentoring block level, A message which can be conditionally displayed at the mentoring block level,
for example upon completion of the block for example upon completion of the block
""" """
TEMPLATE = 'templates/html/message.html' content = String(
content = String(help="Message to display upon completion", scope=Scope.content, default="") display_name="Message",
type = String(help="Type of message", scope=Scope.content, default="completed") help="Message to display upon completion",
scope=Scope.content,
default="",
multiline_editor="html",
resettable_editor=False,
)
type = String(
help="Type of message",
scope=Scope.content,
default="completed",
values=(
{"display_name": "Completed", "value": "completed"},
{"display_name": "Incompleted", "value": "incomplete"},
{"display_name": "Reached max. # of attemps", "value": "max_attempts_reached"},
),
)
editable_fields = ("content", )
def fallback_view(self, view_name, context):
html = u'<div class="message {msg_type}">{content}</div>'.format(msg_type=self.type, content=self.content)
return Fragment(html)
@property
def display_name(self):
if self.type == 'max_attempts_reached':
max_attempts = self.get_parent().max_attempts
return u"Message when student reaches max. # of attempts ({current_limit})".format(
current_limit=u"unlimited" if max_attempts == 0 else max_attempts
)
if self.type == 'completed':
return u"Message shown when complete"
if self.type == 'incomplete':
return u"Message shown when incomplete"
return u"INVALID MESSAGE"
@classmethod
def get_template(cls, template_id):
"""
Used to interact with Studio's create_xblock method to instantiate pre-defined templates.
"""
return {'metadata': {'type': template_id, 'content': "Message goes here."}, 'data': {}}
@classmethod
def parse_xml(cls, node, runtime, keys, id_generator):
"""
Construct this XBlock from the given XML node.
"""
block = runtime.construct_xblock_from_class(cls, keys)
block.content = unicode(node.text or u"")
block.type = node.attrib['type']
for child in node:
block.content += etree.tostring(child, encoding='unicode')
return block
...@@ -17,7 +17,20 @@ ...@@ -17,7 +17,20 @@
# along with this program in a file in the toplevel directory called # along with this program in a file in the toplevel directory called
# "AGPLv3". If not, see <http://www.gnu.org/licenses/>. # "AGPLv3". If not, see <http://www.gnu.org/licenses/>.
# #
from .utils import child_isinstance from lazy import lazy
from xblockutils.helpers import child_isinstance
def _normalize_id(key):
"""
Helper method to normalize a key to avoid issues where some keys have version/branch and others don't.
e.g. self.scope_ids.usage_id != self.runtime.get_block(self.scope_ids.usage_id).scope_ids.usage_id
"""
if hasattr(key, "for_branch"):
key = key.for_branch(None)
if hasattr(key, "for_version"):
key = key.for_version(None)
return key
class StepParentMixin(object): class StepParentMixin(object):
...@@ -30,19 +43,31 @@ class StepParentMixin(object): ...@@ -30,19 +43,31 @@ class StepParentMixin(object):
""" """
Get the usage_ids of all of this XBlock's children that are "Steps" Get the usage_ids of all of this XBlock's children that are "Steps"
""" """
return [child_id for child_id in self.children if child_isinstance(self, child_id, StepMixin)] return [_normalize_id(child_id) for child_id in self.children if child_isinstance(self, child_id, StepMixin)]
class StepMixin(object): class StepMixin(object):
""" """
An XBlock mixin for a child block that is a "Step" An XBlock mixin for a child block that is a "Step"
""" """
@property has_author_view = True
@lazy
def step_number(self): def step_number(self):
return list(self.get_parent().steps).index(self.scope_ids.usage_id) + 1 return list(self.get_parent().steps).index(_normalize_id(self.scope_ids.usage_id)) + 1
@property @lazy
def lonely_step(self): def lonely_step(self):
if self.scope_ids.usage_id not in self.get_parent().steps: if _normalize_id(self.scope_ids.usage_id) not in self.get_parent().steps:
raise ValueError("Step's parent should contain Step", self, self.get_parent().steps) raise ValueError("Step's parent should contain Step", self, self.get_parent().steps)
return len(self.get_parent().steps) == 1 return len(self.get_parent().steps) == 1
def author_view(self, context):
context = context or {}
context['hide_header'] = True
return self.mentoring_view(context)
def author_preview_view(self, context):
context = context or {}
context['hide_header'] = True
return self.student_view(context)
# -*- coding: utf-8 -*-
#
# Copyright (C) 2014 Harvard
#
# Authors:
# Xavier Antoviaque <xavier@antoviaque.org>
#
# This software's license gives you freedom; you can copy, convey,
# propagate, redistribute and/or modify this program under the terms of
# the GNU Affero General Public License (AGPL) as published by the Free
# Software Foundation (FSF), either version 3 of the License, or (at your
# option) any later version of the AGPL published by the FSF.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero
# General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program in a file in the toplevel directory called
# "AGPLv3". If not, see <http://www.gnu.org/licenses/>.
#
# Imports ###########################################################
from xblock.core import XBlock
from xblock.fields import Scope, String
# Classes ###########################################################
class TitleBlock(XBlock):
"""
A simple html representation of a title, with the mentoring weight.
"""
content = String(help="Text to display", scope=Scope.content, default="")
"""
Helper methods
Should eventually be moved to xblock-utils.
"""
def child_isinstance(block, child_id, block_class_or_mixin):
"""
Is "block"'s child identified by usage_id "child_id" an instance of
"block_class_or_mixin"?
This is a bit complicated since it avoids the need to actually
instantiate the child block.
"""
def_id = block.runtime.id_reader.get_definition_id(child_id)
type_name = block.runtime.id_reader.get_block_type(def_id)
child_class = block.runtime.load_block_type(type_name)
return issubclass(child_class, block_class_or_mixin)
...@@ -27,31 +27,30 @@ import logging ...@@ -27,31 +27,30 @@ import logging
from collections import namedtuple from collections import namedtuple
from lxml import etree
from StringIO import StringIO
from xblock.core import XBlock from xblock.core import XBlock
from xblock.exceptions import NoSuchViewError
from xblock.fields import Boolean, Scope, String, Integer, Float, List from xblock.fields import Boolean, Scope, String, Integer, Float, List
from xblock.fragment import Fragment from xblock.fragment import Fragment
from xblock.validation import ValidationMessage
from components import TitleBlock, SharedHeaderBlock, MentoringMessageBlock from components import MentoringMessageBlock
from components.step import StepParentMixin, StepMixin from components.step import StepParentMixin, StepMixin
from xblockutils.resources import ResourceLoader from xblockutils.resources import ResourceLoader
from xblockutils.studio_editable import StudioEditableXBlockMixin, StudioContainerXBlockMixin
# Globals ########################################################### # Globals ###########################################################
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
loader = ResourceLoader(__name__) loader = ResourceLoader(__name__)
default_xml_content = loader.render_template('templates/xml/mentoring_default.xml', {})
# Classes ########################################################### # Classes ###########################################################
Score = namedtuple("Score", ["raw", "percentage", "correct", "incorrect", "partially_correct"]) Score = namedtuple("Score", ["raw", "percentage", "correct", "incorrect", "partially_correct"])
class MentoringBlock(XBlock, StepParentMixin): @XBlock.needs("i18n")
class MentoringBlock(XBlock, StepParentMixin, StudioEditableXBlockMixin, StudioContainerXBlockMixin):
""" """
An XBlock providing mentoring capabilities An XBlock providing mentoring capabilities
...@@ -60,14 +59,10 @@ class MentoringBlock(XBlock, StepParentMixin): ...@@ -60,14 +59,10 @@ class MentoringBlock(XBlock, StepParentMixin):
student is a) provided mentoring advices and asked to alter his answer, or b) is given the student is a) provided mentoring advices and asked to alter his answer, or b) is given the
ok to continue. ok to continue.
""" """
@staticmethod
def is_default_xml_content(value):
return value == default_xml_content
# Content # Content
MENTORING_MODES = ('standard', 'assessment') MENTORING_MODES = ('standard', 'assessment')
mode = String( mode = String(
display_name="Mode",
help="Mode of the mentoring. 'standard' or 'assessment'", help="Mode of the mentoring. 'standard' or 'assessment'",
default='standard', default='standard',
scope=Scope.content, scope=Scope.content,
...@@ -104,7 +99,13 @@ class MentoringBlock(XBlock, StepParentMixin): ...@@ -104,7 +99,13 @@ class MentoringBlock(XBlock, StepParentMixin):
scope=Scope.content, scope=Scope.content,
enforce_type=True enforce_type=True
) )
xml_content = String(help="XML content", default=default_xml_content, scope=Scope.content) xml_content = String(
help="Not used for version 2. This field is here only to preserve the data needed to upgrade from v1 to v2.",
display_name="XML content",
default='',
scope=Scope.content,
multiline_editor=True
)
# Settings # Settings
weight = Float( weight = Float(
...@@ -114,8 +115,8 @@ class MentoringBlock(XBlock, StepParentMixin): ...@@ -114,8 +115,8 @@ class MentoringBlock(XBlock, StepParentMixin):
enforce_type=True enforce_type=True
) )
display_name = String( display_name = String(
help="Display name of the component", help="Title to display",
default="Mentoring XBlock", default="Mentoring Questions",
scope=Scope.settings scope=Scope.settings
) )
...@@ -155,11 +156,17 @@ class MentoringBlock(XBlock, StepParentMixin): ...@@ -155,11 +156,17 @@ class MentoringBlock(XBlock, StepParentMixin):
scope=Scope.preferences scope=Scope.preferences
) )
editable_fields = (
'mode', 'followed_by', 'max_attempts', 'enforce_dependency',
'display_submit', 'weight', 'display_name',
)
icon_class = 'problem' icon_class = 'problem'
has_score = True has_score = True
has_children = True has_children = True
FLOATING_BLOCKS = (TitleBlock, MentoringMessageBlock, SharedHeaderBlock) def _(self, text):
""" translate text """
return self.runtime.service(self, "i18n").ugettext(text)
@property @property
def is_assessment(self): def is_assessment(self):
...@@ -171,7 +178,7 @@ class MentoringBlock(XBlock, StepParentMixin): ...@@ -171,7 +178,7 @@ class MentoringBlock(XBlock, StepParentMixin):
weights = (float(self.runtime.get_block(step_id).weight) for step_id in self.steps) weights = (float(self.runtime.get_block(step_id).weight) for step_id in self.steps)
total_child_weight = sum(weights) total_child_weight = sum(weights)
if total_child_weight == 0: if total_child_weight == 0:
return (0, 0, 0, 0) return Score(0, 0, 0, 0, 0)
score = sum(r[1]['score'] * r[1]['weight'] for r in self.student_results) / total_child_weight score = sum(r[1]['score'] * r[1]['weight'] for r in self.student_results) / total_child_weight
correct = sum(1 for r in self.student_results if r[1]['status'] == 'correct') correct = sum(1 for r in self.student_results if r[1]['status'] == 'correct')
incorrect = sum(1 for r in self.student_results if r[1]['status'] == 'incorrect') incorrect = sum(1 for r in self.student_results if r[1]['status'] == 'incorrect')
...@@ -184,17 +191,11 @@ class MentoringBlock(XBlock, StepParentMixin): ...@@ -184,17 +191,11 @@ class MentoringBlock(XBlock, StepParentMixin):
self.migrate_fields() self.migrate_fields()
fragment = Fragment() fragment = Fragment()
title = u""
header = u""
child_content = u"" child_content = u""
for child_id in self.children: for child_id in self.children:
child = self.runtime.get_block(child_id) child = self.runtime.get_block(child_id)
if isinstance(child, TitleBlock): if isinstance(child, MentoringMessageBlock):
title = child.content
elif isinstance(child, SharedHeaderBlock):
header = child.render('mentoring_view', context).content
elif isinstance(child, MentoringMessageBlock):
pass # TODO pass # TODO
else: else:
child_fragment = child.render('mentoring_view', context) child_fragment = child.render('mentoring_view', context)
...@@ -203,8 +204,7 @@ class MentoringBlock(XBlock, StepParentMixin): ...@@ -203,8 +204,7 @@ class MentoringBlock(XBlock, StepParentMixin):
fragment.add_content(loader.render_template('templates/html/mentoring.html', { fragment.add_content(loader.render_template('templates/html/mentoring.html', {
'self': self, 'self': self,
'title': title, 'title': self.display_name,
'header': header,
'child_content': child_content, 'child_content': child_content,
'missing_dependency_url': self.has_missing_dependency and self.next_step_url, 'missing_dependency_url': self.has_missing_dependency and self.next_step_url,
})) }))
...@@ -243,26 +243,6 @@ class MentoringBlock(XBlock, StepParentMixin): ...@@ -243,26 +243,6 @@ class MentoringBlock(XBlock, StepParentMixin):
} }
@property @property
def title(self):
"""
Returns the title child.
"""
for child in self.get_children_objects():
if isinstance(child, TitleBlock):
return child
return None
@property
def header(self):
"""
Return the header child.
"""
for child in self.get_children_objects():
if isinstance(child, SharedHeaderBlock):
return child
return None
@property
def has_missing_dependency(self): def has_missing_dependency(self):
""" """
Returns True if the student needs to complete another step before being able to complete Returns True if the student needs to complete another step before being able to complete
...@@ -375,7 +355,7 @@ class MentoringBlock(XBlock, StepParentMixin): ...@@ -375,7 +355,7 @@ class MentoringBlock(XBlock, StepParentMixin):
completed = False completed = False
current_child = None current_child = None
children = [self.runtime.get_block(child_id) for child_id in self.children] children = [self.runtime.get_block(child_id) for child_id in self.children]
children = [child for child in children if not isinstance(child, self.FLOATING_BLOCKS)] children = [child for child in children if not isinstance(child, MentoringMessageBlock)]
steps = [child for child in children if isinstance(child, StepMixin)] # Faster than the self.steps property steps = [child for child in children if isinstance(child, StepMixin)] # Faster than the self.steps property
for child in children: for child in children:
...@@ -466,72 +446,81 @@ class MentoringBlock(XBlock, StepParentMixin): ...@@ -466,72 +446,81 @@ class MentoringBlock(XBlock, StepParentMixin):
html += child.render('mentoring_view', {}).content # TODO: frament_text_rewriting ? html += child.render('mentoring_view', {}).content # TODO: frament_text_rewriting ?
return html return html
def studio_view(self, context): def clean_studio_edits(self, data):
""" """
Editing view in Studio Given POST data dictionary 'data', clean the data before validating it.
e.g. fix capitalization, remove trailing spaces, etc.
""" """
fragment = Fragment() if data.get('mode') == 'assessment' and 'max_attempts' not in data:
fragment.add_content(loader.render_template('templates/html/mentoring_edit.html', { # assessment has a default of 2 max_attempts
'self': self, data['max_attempts'] = 2
'xml_content': self.xml_content,
}))
fragment.add_javascript_url(
self.runtime.local_resource_url(self, 'public/js/mentoring_edit.js'))
fragment.add_css_url(
self.runtime.local_resource_url(self, 'public/css/mentoring_edit.css'))
fragment.initialize_js('MentoringEditBlock')
def validate(self):
"""
Validates the state of this XBlock except for individual field values.
"""
validation = super(MentoringBlock, self).validate()
a_child_has_issues = False
message_types_present = set()
for child_id in self.children:
child = self.runtime.get_block(child_id)
# Check if the child has any errors:
if not child.validate().empty:
a_child_has_issues = True
# Ensure there is only one "message" block of each type:
if isinstance(child, MentoringMessageBlock):
msg_type = child.type
if msg_type in message_types_present:
validation.add(ValidationMessage(
ValidationMessage.ERROR,
self._(u"There should only be one '{}' message component.".format(msg_type))
))
message_types_present.add(msg_type)
if a_child_has_issues:
validation.add(ValidationMessage(
ValidationMessage.ERROR,
self._(u"A component inside this mentoring block has issues.")
))
return validation
def author_preview_view(self, context):
"""
Child blocks can override this to add a custom preview shown to authors in Studio when
not editing this block's children.
"""
fragment = self.student_view(context)
fragment.add_content(loader.render_template('templates/html/mentoring_url_name.html', {
"url_name": self.url_name
}))
fragment.add_css_url(self.runtime.local_resource_url(self, 'public/css/mentoring_edit.css'))
return fragment return fragment
@XBlock.json_handler def author_edit_view(self, context):
def studio_submit(self, submissions, suffix=''): """
log.info(u'Received studio submissions: {}'.format(submissions)) Add some HTML to the author view that allows authors to add child blocks.
"""
success = True fragment = super(MentoringBlock, self).author_edit_view(context)
xml_content = submissions['xml_content'] fragment.add_content(loader.render_template('templates/html/mentoring_add_buttons.html', {}))
try: fragment.add_css_url(self.runtime.local_resource_url(self, 'public/css/mentoring_edit.css'))
content = etree.parse(StringIO(xml_content)) fragment.add_javascript_url(self.runtime.local_resource_url(self, 'public/js/mentoring_edit.js'))
except etree.XMLSyntaxError as e: fragment.initialize_js('MentoringEditComponents')
response = { return fragment
'result': 'error',
'message': e.message
}
success = False
else:
root = content.getroot()
if 'mode' in root.attrib:
if root.attrib['mode'] not in self.MENTORING_MODES:
response = {
'result': 'error',
'message': "Invalid mentoring mode: should be 'standard' or 'assessment'"
}
success = False
elif root.attrib['mode'] == 'assessment' and 'max_attempts' not in root.attrib:
# assessment has a default of 2 max_attempts
root.attrib['max_attempts'] = '2'
if success:
response = {
'result': 'success',
}
self.xml_content = etree.tostring(content, pretty_print=True)
log.debug(u'Response from Studio: {}'.format(response))
return response
@property @classmethod
def url_name_with_default(self): def parse_xml(cls, node, runtime, keys, id_generator):
""" """
Ensure the `url_name` is set to a unique, non-empty value. To avoid collisions with e.g. the existing <html> XBlock in edx-platform,
In future once hte pinned version of XBlock is updated, we prefix all of the mentoring block tags with "mentoring-". However,
we can remove this and change the field to use the using that prefix in the XML is optional. This method adds that prefix
xblock.fields.UNIQUE_ID flag instead. in when parsing XML in a mentoring context.
""" """
if self.url_name == 'mentoring-default': PREFIX_TAGS = ("answer", "answer-recap", "quizz", "mcq", "mrq", "rating", "message", "tip", "choice", "column")
return self.scope_ids.usage_id for element in node.iter():
else: # We have prefixed all our XBlock entry points with "mentoring-". But using the "mentoring-"
return self.url_name # prefix in the XML is optional:
if element.tag in PREFIX_TAGS:
element.tag = "mentoring-{}".format(element.tag)
return super(MentoringBlock, cls).parse_xml(node, runtime, keys, id_generator)
@staticmethod @staticmethod
def workbench_scenarios(): def workbench_scenarios():
......
.mentoring-edit .module-actions .error-message { /* Custom appearance for our "Add" buttons */
color: red; .xblock[data-block-type=mentoring] .add-xblock-component .new-component .new-component-type .add-xblock-component-button {
width: 200px;
height: 30px;
line-height: 30px;
}
.xblock[data-block-type=mentoring] .add-xblock-component .new-component .new-component-type .add-xblock-component-button.disabled,
.xblock[data-block-type=mentoring] .add-xblock-component .new-component .new-component-type .add-xblock-component-button.disabled:hover {
background-color: #ccc;
border-color: #888;
cursor: default;
} }
function MentoringEditBlock(runtime, element) { function MentoringEditComponents(runtime, element) {
var xmlEditorTextarea = $('.block-xml-editor', element), "use strict";
xmlEditor = CodeMirror.fromTextArea(xmlEditorTextarea[0], { mode: 'xml' }); // Disable "add" buttons when a message of that type already exists:
var $buttons = $('.add-xblock-component-button[data-category=mentoring-message]', element);
$('.save-button', element).bind('click', function() { var updateButtons = function() {
var handlerUrl = runtime.handlerUrl(element, 'studio_submit'), $buttons.each(function() {
data = { var msg_type = $(this).data('boilerplate');
'xml_content': xmlEditor.getValue(), $(this).toggleClass('disabled', $('.xblock .message.'+msg_type).length > 0);
};
$('.error-message', element).html();
$.post(handlerUrl, JSON.stringify(data)).done(function(response) {
if (response.result === 'success') {
window.location.reload(false);
} else {
$('.error-message', element).html('Error: '+response.message);
}
}); });
};
updateButtons();
$buttons.click(function(ev) {
if ($(this).is('.disabled')) {
ev.preventDefault();
ev.stopPropagation();
} else {
$(this).addClass('disabled');
}
}); });
runtime.listenTo('deleted-child', updateButtons);
$('.cancel-button', element).bind('click', function() {
runtime.notify('cancel', {});
});
} }
...@@ -4,10 +4,9 @@ ...@@ -4,10 +4,9 @@
attempting this step. attempting this step.
</div> </div>
{% if title or header %} {% if title %}
<div class="title"> <div class="title">
{% if title %} <h2>{{ title }}</h2> {% endif %} {% if title %} <h2>{{ title }}</h2> {% endif %}
{% if header %} {{ header|safe }} {% endif %}
</div> </div>
{% endif %} {% endif %}
......
{% load i18n %}
<div class="add-xblock-component new-component-item adding">
<div class="new-component">
<h5>Add New Component</h5>
<ul class="new-component-type">
<li><a href="#" class="single-template add-xblock-component-button" data-category="mentoring-answer">Long Answer</a></li>
<li><a href="#" class="single-template add-xblock-component-button" data-category="mentoring-mcq">Multiple Choice Question</a></li>
<li><a href="#" class="single-template add-xblock-component-button" data-category="mentoring-rating">Rating Question</a></li>
<li><a href="#" class="single-template add-xblock-component-button" data-category="mentoring-mrq">Multiple Response Question</a></li>
<li><a href="#" class="single-template add-xblock-component-button" data-category="html">HTML</a></li>
<li><a href="#" class="single-template add-xblock-component-button" data-category="mentoring-answer-recap">Long Answer Recap</a></li>
<li><a href="#" class="single-template add-xblock-component-button" data-category="mentoring-table">Answer Recap Table</a></li>
<li><a href="#" class="single-template add-xblock-component-button" data-category="mentoring-message" data-boilerplate="completed">Message (Complete)</a></li>
<li><a href="#" class="single-template add-xblock-component-button" data-category="mentoring-message" data-boilerplate="incomplete">Message (Incomplete)</a></li>
<li><a href="#" class="single-template add-xblock-component-button" data-category="mentoring-message" data-boilerplate="max_attempts_reached">Message (Max # Attempts)</a></li>
</ul>
</div>
</div>
{% load i18n %}
<!-- TODO: Replace by default edit view once available in Studio -->
<div class="mentoring-edit editor-with-buttons">
<div class="wrapper-comp-settings is-active" id="settings-tab">
<textarea class="block-xml-editor">{{ xml_content }}</textarea>
</div>
<div class="xblock-actions">
<span class="error-message"></span>
<ul>
<li class="action-item">
<a href="#" class="button action-primary save-button">{% trans "Save" %}</a>
</li>
<li class="action-item">
<a href="#" class="button cancel-button">{% trans "Cancel" %}</a>
</li>
</ul>
</div>
</div>
<mentoring display_name="Nav tooltip title" weight="1" mode="assessment" max_attempts="10"> <mentoring display_name="Mentoring Assessment Example" weight="1" mode="assessment" max_attempts="10">
<title>Default Title</title> <html_demo>
<shared-header>
<p>This paragraph is shared between <strong>all</strong> questions.</p> <p>This paragraph is shared between <strong>all</strong> questions.</p>
</shared-header>
<html>
<p>Please answer the questions below.</p> <p>Please answer the questions below.</p>
</html> </html_demo>
<answer name="goal"> <answer name="goal">
<question>What is your goal?</question> <question>What is your goal?</question>
......
...@@ -4,20 +4,18 @@ ...@@ -4,20 +4,18 @@
should be able to export their data as CSV.</p> should be able to export their data as CSV.</p>
</html> </html>
<mentoring display_name="Mentoring Block 1" mode="standard"> <mentoring display_name="Mentoring Block 1" mode="standard">
<title>Mentoring Block 1</title> <html_demo>
<html>
<p>Please answer the question below.</p> <p>Please answer the question below.</p>
</html> </html_demo>
<answer name="goal"> <answer name="goal">
<question>What is your goal?</question> <question>What is your goal?</question>
</answer> </answer>
</mentoring> </mentoring>
<mentoring display_name="Mentoring Block 1" mode="assessment"> <mentoring display_name="Mentoring Block 2 (Assessment)" mode="assessment">
<title>Mentoring Block 2 (Assessment)</title> <html_demo>
<html>
<p>Please answer the question below.</p> <p>Please answer the question below.</p>
</html> </html_demo>
<answer name="inspired"> <answer name="inspired">
<question>Who has inspired you the most?</question> <question>Who has inspired you the most?</question>
......
<mentoring display_name="Nav tooltip title" weight="1" mode="standard"> <mentoring display_name="Default Title" weight="1" mode="standard">
<title>Default Title</title> <html_demo>
<html>
<p>Please answer the questions below.</p> <p>Please answer the questions below.</p>
</html> </html_demo>
<answer name="goal"> <answer name="goal">
<question>What is your goal?</question> <question>What is your goal?</question>
......
<mentoring display_name="Nav tooltip title" weight="1" mode="standard"> <mentoring display_name="Default Title" weight="1" mode="standard">
<title>Default Title</title> <html_demo>
<html>
<p>Please answer the questions below.</p> <p>Please answer the questions below.</p>
</html> </html_demo>
<mrq name="mrq_1_1" type="choices"> <mrq name="mrq_1_1" type="choices">
<question>What do you like in this MRQ?</question> <question>What do you like in this MRQ?</question>
......
<mentoring url_name="mentoring-assessment-1" display_name="Nav tooltip title" weight="1" mode="assessment" max_attempts="2"> <mentoring url_name="mentoring-assessment-1" display_name="A Simple Assessment" weight="1" mode="assessment" max_attempts="2">
<title>A Simple Assessment</title> <html_demo>
<shared-header>
<p>This paragraph is shared between <strong>all</strong> questions.</p> <p>This paragraph is shared between <strong>all</strong> questions.</p>
</shared-header>
<html>
<p>Please answer the questions below.</p> <p>Please answer the questions below.</p>
</html> </html_demo>
<answer name="goal"> <answer name="goal">
<question>What is your goal?</question> <question>What is your goal?</question>
......
<mentoring url_name="mentoring-assessment-2" display_name="Nav tooltip title" weight="1" mode="assessment" max_attempts="2"> <mentoring url_name="mentoring-assessment-2" display_name="A Simple Assessment" weight="1" mode="assessment" max_attempts="2">
<title>A Simple Assessment</title> <html_demo>
<shared-header>
<p>This paragraph is shared between <strong>all</strong> questions.</p> <p>This paragraph is shared between <strong>all</strong> questions.</p>
</shared-header>
<html>
<p>Please answer the questions below.</p> <p>Please answer the questions below.</p>
</html> </html_demo>
<mcq name="mcq_1_1" type="choices"> <mcq name="mcq_1_1" type="choices">
<question>Do you like this MCQ?</question> <question>Do you like this MCQ?</question>
......
...@@ -13,11 +13,10 @@ ...@@ -13,11 +13,10 @@
<question>What is your goal?</question> <question>What is your goal?</question>
</answer> </answer>
</mentoring> </mentoring>
<mentoring display_name="Mentoring Block 1" mode="assessment"> <mentoring display_name="Mentoring Block 2 (Assessment)" mode="assessment">
<title>Mentoring Block 2 (Assessment)</title> <html_demo>
<html>
<p>Please answer the question below.</p> <p>Please answer the question below.</p>
</html> </html_demo>
<answer name="inspired"> <answer name="inspired">
<question>Who has inspired you the most?</question> <question>Who has inspired you the most?</question>
......
<vertical_demo> <vertical_demo>
<mentoring url_name="mcq_with_comments" display_name="MRQ Exercise 7" weight="1" enforce_dependency="false"> <mentoring url_name="mcq_with_comments" display_name="MRQ With Resizable popups" weight="1" enforce_dependency="false">
<title>MRQ With Resizable popups</title>
<mrq name="mrq_1_1_7" type="choices"> <mrq name="mrq_1_1_7" type="choices">
<question>What do you like in this MRQ?</question> <question>What do you like in this MRQ?</question>
<choice value="elegance">Its elegance</choice> <choice value="elegance">Its elegance</choice>
......
<vertical_demo> <vertical_demo>
<mentoring url_name="mcq_with_comments" display_name="MCQ Exercise 7" weight="1" enforce_dependency="false"> <mentoring url_name="mcq_with_comments" display_name="MCQ With Resizable popups" weight="1" enforce_dependency="false">
<title>MRQ With Resizable popups</title>
<mcq name="mrq_1_1_7" type="choices"> <mcq name="mrq_1_1_7" type="choices">
<question>What do you like in this MRQ?</question> <question>What do you like in this MRQ?</question>
<choice value="elegance"><html><b>Its elegance</b></html></choice> <choice value="elegance"><html><b>Its elegance</b></html></choice>
......
<vertical_demo> <vertical_demo>
<mentoring url_name="mcq_with_comments" display_name="MRQ Exercise 7" weight="1" enforce_dependency="false"> <mentoring url_name="mcq_with_comments" display_name="MRQ With Resizable popups" weight="1" enforce_dependency="false">
<title>MRQ With Resizable popups</title>
<mrq name="mrq_1_1_7" type="choices"> <mrq name="mrq_1_1_7" type="choices">
<question>What do you like in this MRQ?</question> <question>What do you like in this MRQ?</question>
<choice value="elegance"><html><b>Its elegance</b></html></choice> <choice value="elegance"><html><b>Its elegance</b></html></choice>
......
...@@ -47,18 +47,16 @@ BLOCKS = [ ...@@ -47,18 +47,16 @@ BLOCKS = [
'mentoring-dataexport = mentoring:MentoringDataExportBlock', 'mentoring-dataexport = mentoring:MentoringDataExportBlock',
'mentoring-table = mentoring.components:MentoringTableBlock', 'mentoring-table = mentoring.components:MentoringTableBlock',
'column = mentoring.components:MentoringTableColumnBlock',
'header = mentoring.components:MentoringTableColumnHeaderBlock',
'answer = mentoring.components:AnswerBlock',
'quizz = mentoring.components:MCQBlock',
'mcq = mentoring.components:MCQBlock',
'mrq = mentoring.components:MRQBlock',
'message = mentoring.components:MentoringMessageBlock',
'tip = mentoring.components:TipBlock',
'choice = mentoring.components:ChoiceBlock',
'html = mentoring.components:HTMLBlock', 'html = mentoring.components:HTMLBlock',
'title = mentoring.components:TitleBlock', 'mentoring-column = mentoring.components:MentoringTableColumn',
'shared-header = mentoring.components:SharedHeaderBlock', 'mentoring-answer = mentoring.components:AnswerBlock',
'mentoring-answer-recap = mentoring.components:AnswerRecapBlock',
'mentoring-mcq = mentoring.components:MCQBlock',
'mentoring-rating = mentoring.components:RatingBlock',
'mentoring-mrq = mentoring.components:MRQBlock',
'mentoring-message = mentoring.components:MentoringMessageBlock',
'mentoring-tip = mentoring.components:TipBlock',
'mentoring-choice = mentoring.components:ChoiceBlock',
] ]
setup( setup(
......
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