Skip to content

Commit da16903

Browse files
committed
feat: support optional completion for individual XBlocks
1 parent 15f91ac commit da16903

File tree

4 files changed

+26
-2
lines changed

4 files changed

+26
-2
lines changed

common/templates/xblock_wrapper.html

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,11 @@
11
## xss-lint: disable=mako-missing-default
22
<%!
33
from openedx.core.djangolib.js_utils import dump_js_escaped_json
4+
from django.utils.translation import gettext as _
45
%>
6+
% if 'data-is-optional="True"' in data_attributes:
7+
<span class="optional-completion-badge text-uppercase border badge badge-light mb-3">${_("Optional")}</span>
8+
% endif
59
<div class="${' '.join(classes) | n, decode.utf8}" ${data_attributes}>
610
% if js_init_parameters:
711
<script type="json/xblock-args" class="xblock-json-init-args">

openedx/core/lib/xblock_utils/__init__.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
from web_fragments.fragment import Fragment
2424
from xblock.core import XBlock
2525
from xblock.exceptions import InvalidScopeError
26+
from xblock.fields import Scope
2627
from xblock.scorable import ScorableXBlockMixin
2728

2829
from common.djangoapps import static_replace
@@ -141,6 +142,13 @@ def wrap_xblock(
141142
if block.name:
142143
data['name'] = block.name
143144

145+
# We check whether the block is explicitly set as optional because this information is already displayed for units
146+
# in the Learning MFE.
147+
has_children = getattr(block, "children", False)
148+
is_explicitly_optional = block.get_explicitly_set_fields_by_scope(Scope.settings).get('optional_completion', False)
149+
if view != STUDIO_VIEW and not has_children and is_explicitly_optional:
150+
data['is-optional'] = getattr(block, 'optional_completion', False)
151+
144152
template_context = {
145153
'content': block.display_name if display_name_only else frag.content,
146154
'classes': css_classes,

xmodule/tests/test_xml_block.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -236,7 +236,7 @@ def test_display_name_field(self):
236236
editable_fields = self.get_xml_editable_fields(DictFieldData({}))
237237
# Tests that the xblock fields (currently tags and name) get filtered out.
238238
# Also tests that xml_attributes is filtered out of XmlMixin.
239-
assert 1 == len(editable_fields), editable_fields
239+
assert 2 == len(editable_fields), editable_fields
240240
self.assert_field_values(
241241
editable_fields, 'display_name', XModuleMixin.display_name,
242242
explicitly_set=False, value=None, default_value=None
@@ -253,7 +253,7 @@ def test_override_default(self):
253253
def test_integer_field(self):
254254
block = self.get_block(DictFieldData({'max_attempts': '7'}))
255255
editable_fields = block.editable_metadata_fields
256-
assert 8 == len(editable_fields)
256+
assert 9 == len(editable_fields)
257257
self.assert_field_values(
258258
editable_fields, 'max_attempts', TestFields.max_attempts,
259259
explicitly_set=True, value=7, default_value=1000, type='Integer',

xmodule/x_module.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -651,6 +651,18 @@ def editable_metadata_fields(self):
651651

652652
metadata_fields[field.name] = self._create_metadata_editor_info(field)
653653

654+
# The `optional_completion` field can be either explicitly set or inherited from the parent.
655+
# While we could query the parent's field, there is a special case that cannot be supported with the platform's
656+
# completion calculations that count the completed units instead of individual XBlocks.
657+
# If this field is explicitly set to False, then we want to show it in the metadata editor so that the author
658+
# can reset it to the default inherited value.
659+
if "optional_completion" in self.fields:
660+
has_explicitly_set_optional_completion = self.fields['optional_completion'].is_set_on(self)
661+
if not self.optional_completion or has_explicitly_set_optional_completion:
662+
metadata_fields["optional_completion"] = self._create_metadata_editor_info(
663+
self.fields["optional_completion"]
664+
)
665+
654666
return metadata_fields
655667

656668
def _create_metadata_editor_info(self, field):

0 commit comments

Comments
 (0)