From efe6f2314f79784484ec9e714070294bfc5dda21 Mon Sep 17 00:00:00 2001 From: Jesse Shapiro Date: Fri, 16 Sep 2016 12:56:58 -0400 Subject: [PATCH] Refactor MentoringBlock to use NestedXBlock mixin --- problem_builder/mentoring.py | 89 ++++++++++++++++--- problem_builder/message.py | 10 +++ problem_builder/questionnaire.py | 5 +- .../templates/html/mentoring_add_buttons.html | 23 ----- 4 files changed, 88 insertions(+), 39 deletions(-) delete mode 100644 problem_builder/templates/html/mentoring_add_buttons.html diff --git a/problem_builder/mentoring.py b/problem_builder/mentoring.py index 3cd5b9c7..ae7f7cf3 100644 --- a/problem_builder/mentoring.py +++ b/problem_builder/mentoring.py @@ -34,7 +34,7 @@ from xblock.fragment import Fragment from xblock.validation import ValidationMessage -from .message import MentoringMessageBlock +from .message import MentoringMessageBlock, get_message_label from .mixins import ( _normalize_id, QuestionMixin, MessageParentMixin, StepParentMixin, XBlockWithTranslationServiceMixin ) @@ -44,9 +44,16 @@ from xblockutils.resources import ResourceLoader from xblockutils.settings import XBlockWithSettingsMixin, ThemableXBlockMixin 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: # Used to detect if we're in the workbench so we can add Font Awesome @@ -220,7 +227,7 @@ def max_score(self): return 1.0 -class MentoringBlock(BaseMentoringBlock, StudioContainerXBlockMixin, StepParentMixin): +class MentoringBlock(BaseMentoringBlock, StudioContainerWithNestedXBlocksMixin, StepParentMixin): """ An XBlock providing mentoring capabilities @@ -321,6 +328,64 @@ class MentoringBlock(BaseMentoringBlock, StudioContainerXBlockMixin, StepParentM 'display_submit', 'feedback_label', 'weight', 'extended_feedback' ) + @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): """ Checks if mentoring XBlock is in assessment mode """ @@ -817,23 +882,19 @@ def author_edit_view(self, context): """ Add some HTML to the author view that allows authors to add child blocks. """ - fragment = Fragment(u'
') # This DIV is needed for CSS to apply to the previews - self.render_children(context, fragment, can_reorder=True, can_add=False) - fragment.add_content(u'
') - - # 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, - })) + local_context = context.copy() + local_context['author_edit_view'] = True + fragment = super(MentoringBlock, self).author_edit_view(local_context) 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-edit.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/mentoring_edit.js')) - fragment.initialize_js('MentoringEditComponents') + fragment.add_javascript_url(self.runtime.local_resource_url(self, 'public/js/container_edit.js')) + fragment.initialize_js('ProblemBuilderContainerEdit') + return fragment @staticmethod diff --git a/problem_builder/message.py b/problem_builder/message.py index 7315c98d..a1bf3942 100644 --- a/problem_builder/message.py +++ b/problem_builder/message.py @@ -45,6 +45,7 @@ class MentoringMessageBlock(XBlock, StudioEditableXBlockMixin, XBlockWithTransla MESSAGE_TYPES = { "completed": { "display_name": _(u"Completed"), + "studio_label": _(u'Message (Complete)'), "long_display_name": _(u"Message shown when complete"), "default": _(u"Great job!"), "description": _( @@ -54,6 +55,7 @@ class MentoringMessageBlock(XBlock, StudioEditableXBlockMixin, XBlockWithTransla }, "incomplete": { "display_name": _(u"Incomplete"), + "studio_label": _(u'Message (Incomplete)'), "long_display_name": _(u"Message shown when incomplete"), "default": _(u"Not quite! You can try again, though."), "description": _( @@ -64,6 +66,7 @@ class MentoringMessageBlock(XBlock, StudioEditableXBlockMixin, XBlockWithTransla }, "max_attempts_reached": { "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"), "default": _(u"Sorry, you have used up all of your allowed submissions."), "description": _( @@ -74,6 +77,7 @@ class MentoringMessageBlock(XBlock, StudioEditableXBlockMixin, XBlockWithTransla }, "on-assessment-review": { "display_name": _(u"Review with attempts left"), + "studio_label": _(u'Message (Assessment Review)'), "long_display_name": _(u"Message shown during review when attempts remain"), "default": _( u"Note: if you retake this assessment, only your final score counts. " @@ -100,6 +104,8 @@ class MentoringMessageBlock(XBlock, StudioEditableXBlockMixin, XBlockWithTransla }, } + has_author_view = True + content = String( display_name=_("Message"), help=_("Message to display upon completion"), @@ -189,3 +195,7 @@ class CompletedMentoringMessageShim(object): class IncompleteMentoringMessageShim(object): CATEGORY = 'pb-message' STUDIO_LABEL = _("Message (Incomplete)") + + +def get_message_label(type): + return MentoringMessageBlock.MESSAGE_TYPES[type]['studio_label'] diff --git a/problem_builder/questionnaire.py b/problem_builder/questionnaire.py index 5d338e90..595bb25f 100644 --- a/problem_builder/questionnaire.py +++ b/problem_builder/questionnaire.py @@ -32,7 +32,6 @@ from xblockutils.studio_editable import StudioEditableXBlockMixin, StudioContainerXBlockMixin, XBlockWithPreviewMixin from .choice import ChoiceBlock -from .mentoring import MentoringBlock from .message import MentoringMessageBlock from .mixins import QuestionMixin, XBlockWithTranslationServiceMixin from .tip import TipBlock @@ -94,8 +93,10 @@ def student_view(self, context=None): fragment = Fragment(loader.render_template(template_path, context)) # 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() + 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): block_with_resources = self fragment.add_css_url(self.runtime.local_resource_url(block_with_resources, 'public/css/questionnaire.css')) diff --git a/problem_builder/templates/html/mentoring_add_buttons.html b/problem_builder/templates/html/mentoring_add_buttons.html deleted file mode 100644 index e9ac09a0..00000000 --- a/problem_builder/templates/html/mentoring_add_buttons.html +++ /dev/null @@ -1,23 +0,0 @@ -{% load i18n %} - -
- -