Commit efe6f231 by Jesse Shapiro

Refactor MentoringBlock to use NestedXBlock mixin

parent 88450ee0
...@@ -34,7 +34,7 @@ from xblock.fields import Boolean, Scope, String, Integer, Float, List ...@@ -34,7 +34,7 @@ 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 xblock.validation import ValidationMessage
from .message import MentoringMessageBlock from .message import MentoringMessageBlock, get_message_label
from .mixins import ( from .mixins import (
_normalize_id, QuestionMixin, MessageParentMixin, StepParentMixin, XBlockWithTranslationServiceMixin _normalize_id, QuestionMixin, MessageParentMixin, StepParentMixin, XBlockWithTranslationServiceMixin
) )
...@@ -44,9 +44,16 @@ from xblockutils.helpers import child_isinstance ...@@ -44,9 +44,16 @@ from xblockutils.helpers import child_isinstance
from xblockutils.resources import ResourceLoader from xblockutils.resources import ResourceLoader
from xblockutils.settings import XBlockWithSettingsMixin, ThemableXBlockMixin from xblockutils.settings import XBlockWithSettingsMixin, ThemableXBlockMixin
from xblockutils.studio_editable import ( from xblockutils.studio_editable import (
NestedXBlockSpec, StudioEditableXBlockMixin, StudioContainerXBlockMixin, StudioContainerWithNestedXBlocksMixin, NestedXBlockSpec, StudioEditableXBlockMixin, StudioContainerWithNestedXBlocksMixin,
) )
from problem_builder.answer import AnswerBlock, AnswerRecapBlock
from problem_builder.mcq import MCQBlock, RatingBlock
from problem_builder.mrq import MRQBlock
from problem_builder.plot import PlotBlock
from problem_builder.slider import SliderBlock
from problem_builder.table import MentoringTableBlock
try: try:
# Used to detect if we're in the workbench so we can add Font Awesome # Used to detect if we're in the workbench so we can add Font Awesome
...@@ -220,7 +227,7 @@ class BaseMentoringBlock( ...@@ -220,7 +227,7 @@ class BaseMentoringBlock(
return 1.0 return 1.0
class MentoringBlock(BaseMentoringBlock, StudioContainerXBlockMixin, StepParentMixin): class MentoringBlock(BaseMentoringBlock, StudioContainerWithNestedXBlocksMixin, StepParentMixin):
""" """
An XBlock providing mentoring capabilities An XBlock providing mentoring capabilities
...@@ -322,6 +329,64 @@ class MentoringBlock(BaseMentoringBlock, StudioContainerXBlockMixin, StepParentM ...@@ -322,6 +329,64 @@ class MentoringBlock(BaseMentoringBlock, StudioContainerXBlockMixin, StepParentM
) )
@property @property
def allowed_nested_blocks(self):
"""
Returns a list of allowed nested XBlocks. Each item can be either
* An XBlock class
* A NestedXBlockSpec
If XBlock class is used it is assumed that this XBlock is enabled and allows multiple instances.
NestedXBlockSpec allows explicitly setting disabled/enabled state, disabled reason (if any) and single/multiple
instances
"""
additional_blocks = []
try:
from xmodule.video_module.video_module import VideoDescriptor
additional_blocks.append(NestedXBlockSpec(
VideoDescriptor, category='video', label=_(u"Video")
))
except ImportError:
pass
try:
from imagemodal import ImageModal
additional_blocks.append(NestedXBlockSpec(
ImageModal, category='imagemodal', label=_(u"Image Modal")
))
except ImportError:
pass
message_block_shims = [
NestedXBlockSpec(
MentoringMessageBlock,
category='pb-message',
boilerplate=message_type,
label=get_message_label(message_type),
)
for message_type in (
'completed',
'incomplete',
'max_attempts_reached',
)
]
if self.is_assessment:
message_block_shims.append(
NestedXBlockSpec(
MentoringMessageBlock,
category='pb-message',
boilerplate='on-assessment-review',
label=get_message_label('on-assessment-review'),
)
)
return [
NestedXBlockSpec(AnswerBlock, boilerplate='studio_default'),
MCQBlock, RatingBlock, MRQBlock,
NestedXBlockSpec(None, category="html", label=self._("HTML")),
AnswerRecapBlock, MentoringTableBlock, PlotBlock, SliderBlock
] + additional_blocks + message_block_shims
@property
def is_assessment(self): def is_assessment(self):
""" Checks if mentoring XBlock is in assessment mode """ """ Checks if mentoring XBlock is in assessment mode """
return self.mode == 'assessment' return self.mode == 'assessment'
...@@ -817,23 +882,19 @@ class MentoringBlock(BaseMentoringBlock, StudioContainerXBlockMixin, StepParentM ...@@ -817,23 +882,19 @@ class MentoringBlock(BaseMentoringBlock, StudioContainerXBlockMixin, StepParentM
""" """
Add some HTML to the author view that allows authors to add child blocks. Add some HTML to the author view that allows authors to add child blocks.
""" """
fragment = Fragment(u'<div class="mentoring">') # This DIV is needed for CSS to apply to the previews local_context = context.copy()
self.render_children(context, fragment, can_reorder=True, can_add=False) local_context['author_edit_view'] = True
fragment.add_content(u'</div>') fragment = super(MentoringBlock, self).author_edit_view(local_context)
# Show buttons to add review-related child blocks only in assessment mode.
fragment.add_content(loader.render_template('templates/html/mentoring_add_buttons.html', {
"show_review": self.is_assessment,
}))
fragment.add_content(loader.render_template('templates/html/mentoring_url_name.html', { fragment.add_content(loader.render_template('templates/html/mentoring_url_name.html', {
"url_name": self.url_name 'url_name': self.url_name
})) }))
fragment.add_css_url(self.runtime.local_resource_url(self, 'public/css/problem-builder.css')) fragment.add_css_url(self.runtime.local_resource_url(self, 'public/css/problem-builder.css'))
fragment.add_css_url(self.runtime.local_resource_url(self, 'public/css/problem-builder-edit.css')) fragment.add_css_url(self.runtime.local_resource_url(self, 'public/css/problem-builder-edit.css'))
fragment.add_css_url(self.runtime.local_resource_url(self, 'public/css/problem-builder-tinymce-content.css')) fragment.add_css_url(self.runtime.local_resource_url(self, 'public/css/problem-builder-tinymce-content.css'))
fragment.add_javascript_url(self.runtime.local_resource_url(self, 'public/js/util.js')) fragment.add_javascript_url(self.runtime.local_resource_url(self, 'public/js/util.js'))
fragment.add_javascript_url(self.runtime.local_resource_url(self, 'public/js/mentoring_edit.js')) fragment.add_javascript_url(self.runtime.local_resource_url(self, 'public/js/container_edit.js'))
fragment.initialize_js('MentoringEditComponents') fragment.initialize_js('ProblemBuilderContainerEdit')
return fragment return fragment
@staticmethod @staticmethod
......
...@@ -45,6 +45,7 @@ class MentoringMessageBlock(XBlock, StudioEditableXBlockMixin, XBlockWithTransla ...@@ -45,6 +45,7 @@ class MentoringMessageBlock(XBlock, StudioEditableXBlockMixin, XBlockWithTransla
MESSAGE_TYPES = { MESSAGE_TYPES = {
"completed": { "completed": {
"display_name": _(u"Completed"), "display_name": _(u"Completed"),
"studio_label": _(u'Message (Complete)'),
"long_display_name": _(u"Message shown when complete"), "long_display_name": _(u"Message shown when complete"),
"default": _(u"Great job!"), "default": _(u"Great job!"),
"description": _( "description": _(
...@@ -54,6 +55,7 @@ class MentoringMessageBlock(XBlock, StudioEditableXBlockMixin, XBlockWithTransla ...@@ -54,6 +55,7 @@ class MentoringMessageBlock(XBlock, StudioEditableXBlockMixin, XBlockWithTransla
}, },
"incomplete": { "incomplete": {
"display_name": _(u"Incomplete"), "display_name": _(u"Incomplete"),
"studio_label": _(u'Message (Incomplete)'),
"long_display_name": _(u"Message shown when incomplete"), "long_display_name": _(u"Message shown when incomplete"),
"default": _(u"Not quite! You can try again, though."), "default": _(u"Not quite! You can try again, though."),
"description": _( "description": _(
...@@ -64,6 +66,7 @@ class MentoringMessageBlock(XBlock, StudioEditableXBlockMixin, XBlockWithTransla ...@@ -64,6 +66,7 @@ class MentoringMessageBlock(XBlock, StudioEditableXBlockMixin, XBlockWithTransla
}, },
"max_attempts_reached": { "max_attempts_reached": {
"display_name": _(u"Reached max. # of attempts"), "display_name": _(u"Reached max. # of attempts"),
"studio_label": _(u'Message (Max # Attempts)'),
"long_display_name": _(u"Message shown when student reaches max. # of attempts"), "long_display_name": _(u"Message shown when student reaches max. # of attempts"),
"default": _(u"Sorry, you have used up all of your allowed submissions."), "default": _(u"Sorry, you have used up all of your allowed submissions."),
"description": _( "description": _(
...@@ -74,6 +77,7 @@ class MentoringMessageBlock(XBlock, StudioEditableXBlockMixin, XBlockWithTransla ...@@ -74,6 +77,7 @@ class MentoringMessageBlock(XBlock, StudioEditableXBlockMixin, XBlockWithTransla
}, },
"on-assessment-review": { "on-assessment-review": {
"display_name": _(u"Review with attempts left"), "display_name": _(u"Review with attempts left"),
"studio_label": _(u'Message (Assessment Review)'),
"long_display_name": _(u"Message shown during review when attempts remain"), "long_display_name": _(u"Message shown during review when attempts remain"),
"default": _( "default": _(
u"Note: if you retake this assessment, only your final score counts. " u"Note: if you retake this assessment, only your final score counts. "
...@@ -100,6 +104,8 @@ class MentoringMessageBlock(XBlock, StudioEditableXBlockMixin, XBlockWithTransla ...@@ -100,6 +104,8 @@ class MentoringMessageBlock(XBlock, StudioEditableXBlockMixin, XBlockWithTransla
}, },
} }
has_author_view = True
content = String( content = String(
display_name=_("Message"), display_name=_("Message"),
help=_("Message to display upon completion"), help=_("Message to display upon completion"),
...@@ -189,3 +195,7 @@ class CompletedMentoringMessageShim(object): ...@@ -189,3 +195,7 @@ class CompletedMentoringMessageShim(object):
class IncompleteMentoringMessageShim(object): class IncompleteMentoringMessageShim(object):
CATEGORY = 'pb-message' CATEGORY = 'pb-message'
STUDIO_LABEL = _("Message (Incomplete)") STUDIO_LABEL = _("Message (Incomplete)")
def get_message_label(type):
return MentoringMessageBlock.MESSAGE_TYPES[type]['studio_label']
...@@ -32,7 +32,6 @@ from xblockutils.resources import ResourceLoader ...@@ -32,7 +32,6 @@ from xblockutils.resources import ResourceLoader
from xblockutils.studio_editable import StudioEditableXBlockMixin, StudioContainerXBlockMixin, XBlockWithPreviewMixin from xblockutils.studio_editable import StudioEditableXBlockMixin, StudioContainerXBlockMixin, XBlockWithPreviewMixin
from .choice import ChoiceBlock from .choice import ChoiceBlock
from .mentoring import MentoringBlock
from .message import MentoringMessageBlock from .message import MentoringMessageBlock
from .mixins import QuestionMixin, XBlockWithTranslationServiceMixin from .mixins import QuestionMixin, XBlockWithTranslationServiceMixin
from .tip import TipBlock from .tip import TipBlock
...@@ -94,8 +93,10 @@ class QuestionnaireAbstractBlock( ...@@ -94,8 +93,10 @@ class QuestionnaireAbstractBlock(
fragment = Fragment(loader.render_template(template_path, context)) fragment = Fragment(loader.render_template(template_path, context))
# If we use local_resource_url(self, ...) the runtime may insert many identical copies # If we use local_resource_url(self, ...) the runtime may insert many identical copies
# of questionnaire.[css/js] into the DOM. So we use the mentoring block here if possible # of questionnaire.[css/js] into the DOM. So we use the mentoring block here if possible.
block_with_resources = self.get_parent() block_with_resources = self.get_parent()
from .mentoring import MentoringBlock
# We use an inline import here to avoid a circular dependency with the .mentoring module.
if not isinstance(block_with_resources, MentoringBlock): if not isinstance(block_with_resources, MentoringBlock):
block_with_resources = self block_with_resources = self
fragment.add_css_url(self.runtime.local_resource_url(block_with_resources, 'public/css/questionnaire.css')) fragment.add_css_url(self.runtime.local_resource_url(block_with_resources, 'public/css/questionnaire.css'))
......
{% load i18n %}
<div class="add-xblock-component new-component-item adding">
<div class="new-component">
<h5>{% trans "Add New Component" %}</h5>
<ul class="new-component-type">
<li><a href="#" class="single-template add-xblock-component-button" data-category="pb-answer" data-boilerplate="studio_default">{% trans "Long Answer" %}</a></li>
<li><a href="#" class="single-template add-xblock-component-button" data-category="pb-mcq">{% trans "Multiple Choice Question" %}</a></li>
<li><a href="#" class="single-template add-xblock-component-button" data-category="pb-rating">{% trans "Rating Question" %}</a></li>
<li><a href="#" class="single-template add-xblock-component-button" data-category="pb-mrq">{% trans "Multiple Response Question" %}</a></li>
<li><a href="#" class="single-template add-xblock-component-button" data-category="pb-slider">{% trans "Ranged Value Slider" %}</a></li>
<li><a href="#" class="single-template add-xblock-component-button" data-category="html">{% trans "HTML" %}</a></li>
<li><a href="#" class="single-template add-xblock-component-button" data-category="pb-answer-recap">{% trans "Long Answer Recap" %}</a></li>
<li><a href="#" class="single-template add-xblock-component-button" data-category="pb-table">{% trans "Answer Recap Table" %}</a></li>
<li><a href="#" class="single-template add-xblock-component-button" data-category="pb-message" data-boilerplate="completed">{% trans "Message (Complete)" %}</a></li>
<li><a href="#" class="single-template add-xblock-component-button" data-category="pb-message" data-boilerplate="incomplete">{% trans "Message (Incomplete)" %}</a></li>
<li><a href="#" class="single-template add-xblock-component-button" data-category="pb-message" data-boilerplate="max_attempts_reached">{% trans "Message (Max # Attempts)" %}</a></li>
{% if show_review %}
<li><a href="#" class="single-template add-xblock-component-button" data-category="pb-message" data-boilerplate="on-assessment-review">{% trans "Message (Assessment Review)" %}</a></li>
{% endif %}
</ul>
</div>
</div>
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