feat: make hide from TOC a visibility section setting (#33952)
Exposes the hide_from_toc xblock attribute so course authors can configure it as a section visibility option in Studio. Before this change, the Hide from TOC functionality was mainly used by OLX components. Hence, it wasn't available for configuration through the Studio UI. Still, its implementation existed in the platform and could be used by setting the attribute: hide_from_toc=true as part of the OLX definition. Ref: https://openedx.atlassian.net/wiki/spaces/OEPM/pages/3853975595/Feature+Enhancement+Proposal+Hide+Sections+from+course+outline
This commit is contained in:
@@ -77,3 +77,4 @@ class XblockSerializer(StrictSerializer):
|
||||
target_index = serializers.IntegerField(required=False, allow_null=True)
|
||||
boilerplate = serializers.JSONField(required=False, allow_null=True)
|
||||
staged_content = serializers.CharField(required=False, allow_null=True)
|
||||
hide_from_toc = serializers.BooleanField(required=False, allow_null=True)
|
||||
|
||||
@@ -1445,6 +1445,23 @@ class ContentStoreTest(ContentStoreTestCase):
|
||||
).replace('REPLACE', r'([0-9]|[a-f]){3,}')
|
||||
self.assertRegex(data['locator'], retarget)
|
||||
|
||||
@ddt.data(True, False)
|
||||
def test_hide_xblock_from_toc_via_handler(self, hide_from_toc):
|
||||
"""Test that the hide_from_toc field can be set via the xblock_handler."""
|
||||
course = CourseFactory.create()
|
||||
sequential = BlockFactory.create(parent_location=course.location)
|
||||
data = {
|
||||
"metadata": {
|
||||
"hide_from_toc": hide_from_toc
|
||||
}
|
||||
}
|
||||
|
||||
response = self.client.ajax_post(get_url("xblock_handler", sequential.location), data)
|
||||
sequential = self.store.get_item(sequential.location)
|
||||
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertEqual(hide_from_toc, sequential.hide_from_toc)
|
||||
|
||||
def test_capa_block(self):
|
||||
"""Test that a problem treats markdown specially."""
|
||||
course = CourseFactory.create()
|
||||
|
||||
@@ -1089,6 +1089,8 @@ def create_xblock_info( # lint-amnesty, pylint: disable=too-many-statements
|
||||
"group_access": xblock.group_access,
|
||||
"user_partitions": user_partitions,
|
||||
"show_correctness": xblock.show_correctness,
|
||||
"hide_from_toc": xblock.hide_from_toc,
|
||||
"enable_hide_from_toc_ui": settings.FEATURES.get("ENABLE_HIDE_FROM_TOC_UI", False),
|
||||
}
|
||||
)
|
||||
|
||||
@@ -1217,6 +1219,15 @@ def create_xblock_info( # lint-amnesty, pylint: disable=too-many-statements
|
||||
else:
|
||||
xblock_info["staff_only_message"] = False
|
||||
|
||||
if xblock_info["hide_from_toc"]:
|
||||
xblock_info["hide_from_toc_message"] = True
|
||||
elif child_info and child_info["children"]:
|
||||
xblock_info["hide_from_toc_message"] = all(
|
||||
child["hide_from_toc_message"] for child in child_info["children"]
|
||||
)
|
||||
else:
|
||||
xblock_info["hide_from_toc_message"] = False
|
||||
|
||||
# If the ENABLE_TAGGING_TAXONOMY_LIST_PAGE feature flag is enabled, we show the "Manage Tags" options
|
||||
if use_tagging_taxonomy_list_page():
|
||||
xblock_info["use_tagging_taxonomy_list_page"] = True
|
||||
@@ -1345,6 +1356,7 @@ class VisibilityState:
|
||||
needs_attention = "needs_attention"
|
||||
staff_only = "staff_only"
|
||||
gated = "gated"
|
||||
hide_from_toc = "hide_from_toc"
|
||||
|
||||
|
||||
def _compute_visibility_state(
|
||||
@@ -1355,6 +1367,8 @@ def _compute_visibility_state(
|
||||
"""
|
||||
if xblock.visible_to_staff_only:
|
||||
return VisibilityState.staff_only
|
||||
elif xblock.hide_from_toc:
|
||||
return VisibilityState.hide_from_toc
|
||||
elif is_unit_with_changes:
|
||||
# Note that a unit that has never been published will fall into this category,
|
||||
# as well as previously published units with draft content.
|
||||
@@ -1362,22 +1376,27 @@ def _compute_visibility_state(
|
||||
|
||||
is_unscheduled = xblock.start == DEFAULT_START_DATE
|
||||
is_live = is_course_self_paced or datetime.now(UTC) > xblock.start
|
||||
if child_info and child_info.get("children", []):
|
||||
if child_info and child_info.get("children", []): # pylint: disable=too-many-nested-blocks
|
||||
all_staff_only = True
|
||||
all_unscheduled = True
|
||||
all_live = True
|
||||
all_hide_from_toc = True
|
||||
for child in child_info["children"]:
|
||||
child_state = child["visibility_state"]
|
||||
if child_state == VisibilityState.needs_attention:
|
||||
return child_state
|
||||
elif not child_state == VisibilityState.staff_only:
|
||||
all_staff_only = False
|
||||
if not child_state == VisibilityState.unscheduled:
|
||||
all_unscheduled = False
|
||||
if not child_state == VisibilityState.live:
|
||||
all_live = False
|
||||
if not child_state == VisibilityState.hide_from_toc:
|
||||
all_hide_from_toc = False
|
||||
if not child_state == VisibilityState.unscheduled:
|
||||
all_unscheduled = False
|
||||
if not child_state == VisibilityState.live:
|
||||
all_live = False
|
||||
if all_staff_only:
|
||||
return VisibilityState.staff_only
|
||||
elif all_hide_from_toc:
|
||||
return VisibilityState.hide_from_toc
|
||||
elif all_unscheduled:
|
||||
return (
|
||||
VisibilityState.unscheduled
|
||||
|
||||
@@ -177,6 +177,14 @@ define(
|
||||
* List of tags of the unit. This list is managed by the content_tagging module.
|
||||
*/
|
||||
tags: null,
|
||||
/**
|
||||
* True if the xblock is not visible to students only via links.
|
||||
*/
|
||||
hide_from_toc: null,
|
||||
/**
|
||||
* True iff this xblock should display a "Contains staff only content" message.
|
||||
*/
|
||||
hide_from_toc_message: null,
|
||||
},
|
||||
|
||||
initialize: function() {
|
||||
|
||||
@@ -42,6 +42,8 @@ describe('CourseOutlinePage', function() {
|
||||
user_partition_info: {},
|
||||
highlights_enabled: true,
|
||||
highlights_enabled_for_messaging: false,
|
||||
hide_from_toc: false,
|
||||
enable_hide_from_toc_ui: true
|
||||
}, options, {child_info: {children: children}});
|
||||
};
|
||||
|
||||
@@ -68,7 +70,9 @@ describe('CourseOutlinePage', function() {
|
||||
show_review_rules: true,
|
||||
user_partition_info: {},
|
||||
highlights_enabled: true,
|
||||
highlights_enabled_for_messaging: false
|
||||
highlights_enabled_for_messaging: false,
|
||||
hide_from_toc: false,
|
||||
enable_hide_from_toc_ui: true
|
||||
}, options, {child_info: {children: children}});
|
||||
};
|
||||
|
||||
@@ -93,7 +97,9 @@ describe('CourseOutlinePage', function() {
|
||||
group_access: {},
|
||||
user_partition_info: {},
|
||||
highlights: [],
|
||||
highlights_enabled: true
|
||||
highlights_enabled: true,
|
||||
hide_from_toc: false,
|
||||
enable_hide_from_toc_ui: true
|
||||
}, options, {child_info: {children: children}});
|
||||
};
|
||||
|
||||
@@ -123,7 +129,9 @@ describe('CourseOutlinePage', function() {
|
||||
},
|
||||
user_partitions: [],
|
||||
group_access: {},
|
||||
user_partition_info: {}
|
||||
user_partition_info: {},
|
||||
hide_from_toc: false,
|
||||
enable_hide_from_toc_ui: true
|
||||
}, options, {child_info: {children: children}});
|
||||
};
|
||||
|
||||
@@ -141,7 +149,9 @@ describe('CourseOutlinePage', function() {
|
||||
edited_by: 'MockUser',
|
||||
user_partitions: [],
|
||||
group_access: {},
|
||||
user_partition_info: {}
|
||||
user_partition_info: {},
|
||||
hide_from_toc: false,
|
||||
enable_hide_from_toc_ui: true
|
||||
}, options);
|
||||
};
|
||||
|
||||
@@ -1214,7 +1224,9 @@ describe('CourseOutlinePage', function() {
|
||||
is_practice_exam: false,
|
||||
is_proctored_exam: false,
|
||||
default_time_limit_minutes: 150,
|
||||
hide_after_due: true
|
||||
hide_after_due: true,
|
||||
hide_from_toc: false,
|
||||
enable_hide_from_toc_ui: true,
|
||||
}, [
|
||||
createMockVerticalJSON({
|
||||
has_changes: true,
|
||||
@@ -1397,6 +1409,7 @@ describe('CourseOutlinePage', function() {
|
||||
default_time_limit_minutes: 150,
|
||||
hide_after_due: true,
|
||||
is_onboarding_exam: false,
|
||||
hide_from_toc: null,
|
||||
}
|
||||
});
|
||||
expect(requests[0].requestHeaders['X-HTTP-Method-Override']).toBe('PATCH');
|
||||
@@ -2240,6 +2253,8 @@ describe('CourseOutlinePage', function() {
|
||||
is_practice_exam: false,
|
||||
is_proctored_exam: false,
|
||||
default_time_limit_minutes: null,
|
||||
hide_from_toc: false,
|
||||
enable_hide_from_toc_ui: true,
|
||||
}, [
|
||||
createMockVerticalJSON({
|
||||
has_changes: true,
|
||||
@@ -2521,6 +2536,7 @@ describe('CourseOutlinePage', function() {
|
||||
publish: 'republish',
|
||||
metadata: {
|
||||
visible_to_staff_only: null,
|
||||
hide_from_toc: null
|
||||
}
|
||||
});
|
||||
})
|
||||
|
||||
@@ -314,6 +314,8 @@ define(['jquery', 'backbone', 'underscore', 'gettext', 'js/views/baseview',
|
||||
var isProctoredExam = xblockInfo.get('is_proctored_exam');
|
||||
var isPracticeExam = xblockInfo.get('is_practice_exam');
|
||||
var isOnboardingExam = xblockInfo.get('is_onboarding_exam');
|
||||
var enableHideFromTOCUI = xblockInfo.get('enable_hide_from_toc_ui');
|
||||
var hideFromTOC = xblockInfo.get('hide_from_toc');
|
||||
var html = this.template($.extend({}, {
|
||||
xblockInfo: xblockInfo,
|
||||
xblockType: this.options.xblockType,
|
||||
@@ -323,6 +325,8 @@ define(['jquery', 'backbone', 'underscore', 'gettext', 'js/views/baseview',
|
||||
isProctoredExam: isProctoredExam,
|
||||
isPracticeExam: isPracticeExam,
|
||||
isOnboardingExam: isOnboardingExam,
|
||||
enableHideFromTOCUI: enableHideFromTOCUI,
|
||||
hideFromTOC: hideFromTOC,
|
||||
isTimedExam: isTimeLimited && !(
|
||||
isProctoredExam || isPracticeExam || isOnboardingExam
|
||||
),
|
||||
@@ -798,6 +802,10 @@ define(['jquery', 'backbone', 'underscore', 'gettext', 'js/views/baseview',
|
||||
return this.model.get('ancestor_has_staff_lock');
|
||||
},
|
||||
|
||||
isModelHiddenFromTOC: function() {
|
||||
return this.model.get('hide_from_toc');
|
||||
},
|
||||
|
||||
getContext: function() {
|
||||
return {
|
||||
hasExplicitStaffLock: this.isModelLocked(),
|
||||
@@ -812,6 +820,8 @@ define(['jquery', 'backbone', 'underscore', 'gettext', 'js/views/baseview',
|
||||
afterRender: function() {
|
||||
AbstractVisibilityEditor.prototype.afterRender.call(this);
|
||||
this.setLock(this.isModelLocked());
|
||||
this.setHideFromTOC(this.isModelHiddenFromTOC());
|
||||
this.setVisibleToLearners(this.isVisibleToLearners());
|
||||
},
|
||||
|
||||
setLock: function(value) {
|
||||
@@ -822,8 +832,24 @@ define(['jquery', 'backbone', 'underscore', 'gettext', 'js/views/baseview',
|
||||
return this.$('#staff_lock').is(':checked');
|
||||
},
|
||||
|
||||
setHideFromTOC: function(value) {
|
||||
this.$('#hide_from_toc').prop('checked', value);
|
||||
},
|
||||
|
||||
setVisibleToLearners: function(value) {
|
||||
this.$('#visible_to_learners').prop('checked', value);
|
||||
},
|
||||
|
||||
isVisibleToLearners: function() {
|
||||
return this.$('#staff_lock').is(':not(:checked)') && this.$('#hide_from_toc').is(':not(:checked)');
|
||||
},
|
||||
|
||||
isHiddenFromTOC: function() {
|
||||
return this.$('#hide_from_toc').is(':checked');
|
||||
},
|
||||
|
||||
hasChanges: function() {
|
||||
return this.isModelLocked() !== this.isLocked();
|
||||
return this.isModelLocked() !== this.isLocked() || this.isModelHiddenFromTOC() !== this.isHiddenFromTOC();
|
||||
},
|
||||
|
||||
getRequestData: function() {
|
||||
@@ -831,7 +857,8 @@ define(['jquery', 'backbone', 'underscore', 'gettext', 'js/views/baseview',
|
||||
return {
|
||||
publish: 'republish',
|
||||
metadata: {
|
||||
visible_to_staff_only: this.isLocked() ? true : null
|
||||
visible_to_staff_only: this.isLocked() || null,
|
||||
hide_from_toc: this.isHiddenFromTOC() || null
|
||||
}
|
||||
};
|
||||
} else {
|
||||
@@ -1055,12 +1082,20 @@ define(['jquery', 'backbone', 'underscore', 'gettext', 'js/views/baseview',
|
||||
if (this.currentVisibility() === 'staff_only') {
|
||||
metadata.visible_to_staff_only = true;
|
||||
metadata.hide_after_due = null;
|
||||
metadata.hide_from_toc = null;
|
||||
} else if (this.currentVisibility() === 'hide_after_due') {
|
||||
metadata.visible_to_staff_only = null;
|
||||
metadata.hide_after_due = true;
|
||||
} else {
|
||||
metadata.hide_from_toc = null;
|
||||
} else if (this.currentVisibility() === 'hide_from_toc'){
|
||||
metadata.visible_to_staff_only = null;
|
||||
metadata.hide_after_due = null;
|
||||
metadata.hide_from_toc = true;
|
||||
}
|
||||
else {
|
||||
metadata.visible_to_staff_only = null;
|
||||
metadata.hide_after_due = null;
|
||||
metadata.hide_from_toc = null;
|
||||
}
|
||||
|
||||
return {
|
||||
|
||||
@@ -149,6 +149,7 @@ function($, _, gettext, BaseView, ViewUtils, XBlockViewUtils, MoveXBlockUtils, H
|
||||
releaseDate: this.model.get('release_date'),
|
||||
releaseDateFrom: this.model.get('release_date_from'),
|
||||
hasExplicitStaffLock: this.model.get('has_explicit_staff_lock'),
|
||||
hideFromTOC: this.model.get('hide_from_toc'),
|
||||
staffLockFrom: this.model.get('staff_lock_from'),
|
||||
enableCopyUnit: this.model.get('enable_copy_paste_units'),
|
||||
course: window.course,
|
||||
|
||||
@@ -29,6 +29,8 @@ function($, _, gettext, ViewUtils, ModuleUtils, XBlockInfo, StringUtils) {
|
||||
*
|
||||
* staffOnly - all of the block's content is to be shown to staff only
|
||||
* Note: staff only items do not affect their parent's state.
|
||||
*
|
||||
* hideFromTOC - all of the block's content is to be hidden from the table of contents.
|
||||
*/
|
||||
VisibilityState = {
|
||||
live: 'live',
|
||||
@@ -36,7 +38,8 @@ function($, _, gettext, ViewUtils, ModuleUtils, XBlockInfo, StringUtils) {
|
||||
unscheduled: 'unscheduled',
|
||||
needsAttention: 'needs_attention',
|
||||
staffOnly: 'staff_only',
|
||||
gated: 'gated'
|
||||
gated: 'gated',
|
||||
hideFromTOC: 'hide_from_toc'
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -310,6 +313,9 @@ function($, _, gettext, ViewUtils, ModuleUtils, XBlockInfo, StringUtils) {
|
||||
if (visibilityState === VisibilityState.staffOnly) {
|
||||
return 'is-staff-only';
|
||||
}
|
||||
if (visibilityState === VisibilityState.hideFromTOC) {
|
||||
return 'is-hidden-from-toc';
|
||||
}
|
||||
if (visibilityState === VisibilityState.gated) {
|
||||
return 'is-gated';
|
||||
}
|
||||
|
||||
@@ -112,6 +112,7 @@ function($, _, gettext, BaseView, ViewUtils, XBlockViewUtils, XBlockStringFieldE
|
||||
includesChildren: this.shouldRenderChildren(),
|
||||
hasExplicitStaffLock: this.model.get('has_explicit_staff_lock'),
|
||||
staffOnlyMessage: this.model.get('staff_only_message'),
|
||||
hideFromTOCMessage: this.model.get('hide_from_toc_message'),
|
||||
course: course,
|
||||
enableCopyPasteUnits: this.model.get("enable_copy_paste_units"), // ENABLE_COPY_PASTE_UNITS waffle flag
|
||||
useTaggingTaxonomyListPage: this.model.get("use_tagging_taxonomy_list_page"), // ENABLE_TAGGING_TAXONOMY_LIST_PAGE waffle flag
|
||||
|
||||
@@ -507,6 +507,18 @@ $outline-indent-width: $baseline;
|
||||
}
|
||||
}
|
||||
|
||||
// CASE: is hidden from TOC
|
||||
&.is-hidden-from-toc {
|
||||
// needed to make sure direct children only
|
||||
> .section-status,
|
||||
> .subsection-status,
|
||||
> .unit-status {
|
||||
.status-message .icon {
|
||||
color: $color-hide-from-toc;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// CASE: has gated content
|
||||
&.is-gated {
|
||||
|
||||
@@ -603,6 +615,11 @@ $outline-indent-width: $baseline;
|
||||
border-left-color: $color-staff-only;
|
||||
}
|
||||
|
||||
// CASE: is hidden from TOC
|
||||
&.is-hidden-from-toc {
|
||||
border-left-color: $color-hide-from-toc;
|
||||
}
|
||||
|
||||
// CASE: has gated content
|
||||
&.is-gated {
|
||||
border-left-color: $color-gated;
|
||||
@@ -698,6 +715,11 @@ $outline-indent-width: $baseline;
|
||||
border-left-color: $color-staff-only;
|
||||
}
|
||||
|
||||
// CASE: is hidden from TOC
|
||||
&.is-hidden-from-toc {
|
||||
border-left-color: $color-hide-from-toc;
|
||||
}
|
||||
|
||||
// CASE: is presented for gated
|
||||
&.is-gated {
|
||||
border-left-color: $color-gated;
|
||||
|
||||
@@ -218,6 +218,7 @@ $color-ready: $green !default;
|
||||
$color-warning: $orange-l2 !default;
|
||||
$color-error: $red-l2 !default;
|
||||
$color-staff-only: $black !default;
|
||||
$color-hide-from-toc: $black !default;
|
||||
$color-gated: $black !default;
|
||||
|
||||
$color-heading-base: $gray-d2 !default;
|
||||
|
||||
@@ -155,6 +155,15 @@
|
||||
}
|
||||
}
|
||||
|
||||
// CASE: is hidden from TOC
|
||||
&.is-hidden-from-toc{
|
||||
@extend %bar-module-black;
|
||||
|
||||
&.is-scheduled .wrapper-release .copy {
|
||||
text-decoration: line-through;
|
||||
}
|
||||
}
|
||||
|
||||
// CASE: content is gated
|
||||
&.is-gated {
|
||||
@extend %bar-module-black;
|
||||
|
||||
@@ -22,7 +22,13 @@ var addStatusMessage = function (statusType, message) {
|
||||
statusIconClass = 'fa-warning';
|
||||
} else if (statusType === 'staff-only' || statusType === 'gated') {
|
||||
statusIconClass = 'fa-lock';
|
||||
} else if (statusType === 'partition-groups') {
|
||||
} else if(statusType === 'hide-from-toc-chapter') {
|
||||
statusIconClass = 'fa-eye-slash';
|
||||
}
|
||||
else if(statusType === 'hide-from-toc-sequence') {
|
||||
statusIconClass = 'fa-ban';
|
||||
}
|
||||
else if (statusType === 'partition-groups') {
|
||||
statusIconClass = 'fa-eye';
|
||||
}
|
||||
|
||||
@@ -49,6 +55,15 @@ if (staffOnlyMessage) {
|
||||
messageType = 'staff-only';
|
||||
messageText = gettext('Contains staff only content');
|
||||
addStatusMessage(messageType, messageText);
|
||||
} else if (hideFromTOCMessage && !xblockInfo.isVertical()){
|
||||
if (xblockInfo.isChapter()) {
|
||||
messageText = gettext('Hidden in the course outline, but sections are accessible for your learners through their separate link.');
|
||||
messageType = 'hide-from-toc-chapter';
|
||||
} else {
|
||||
messageText = gettext('Subsections are not navigable between each other, they can only be accessed through their link.');
|
||||
messageType = 'hide-from-toc-sequence';
|
||||
}
|
||||
addStatusMessage(messageType, messageText);
|
||||
} else {
|
||||
if (visibilityState === 'needs_attention' && xblockInfo.isVertical()) {
|
||||
messageType = 'warning';
|
||||
|
||||
@@ -6,11 +6,39 @@
|
||||
<%- gettext('Section Visibility') %>
|
||||
<% } %>
|
||||
</h3>
|
||||
<div class="list-fields list-input content-visibility" role="group" aria-labelledby="content_visibility_label">
|
||||
<% if (enableHideFromTOCUI) { %>
|
||||
<div class="modal-section-content no-visibility-option">
|
||||
<label class="label">
|
||||
<input type="radio" id="visible_to_learners" name="content-visibility" class="input input-radio" />
|
||||
<span> <%- gettext('Visible to learners') %> </span>
|
||||
</label>
|
||||
<p class='field-message' id='visible-to-learners-description'> <%- gettext('It is visible to learners, ensuring that all learners can view its contents.') %> </p>
|
||||
</div>
|
||||
<div class="modal-section-content hide-from-toc">
|
||||
<% if (!xblockInfo.isVertical()) { %>
|
||||
<label class="label">
|
||||
<input type="radio" id="hide_from_toc" name="content-visibility" class="input input-radio" />
|
||||
<span> <%- gettext('Hide in Course Outline, accessible via Link') %> </span>
|
||||
</label>
|
||||
<p class='field-message' id='hide-from-toc-description'> <%- gettext('It is intentionally hidden from standard navigation, ensuring that only individuals with the link can view its contents.') %> </p>
|
||||
<% if (hideFromTOC) { %>
|
||||
<p class="tip tip-warning">
|
||||
<%- gettext('If you make this section visible in the table of content, learners will be able to see its content.') %>
|
||||
</p>
|
||||
<% } %>
|
||||
<% } %>
|
||||
</div>
|
||||
<% } %>
|
||||
<div class="modal-section-content staff-lock">
|
||||
<label class="label">
|
||||
<input type="checkbox" id="staff_lock" name="staff_lock" class="input input-checkbox" />
|
||||
<% var inputType = enableHideFromTOCUI ? 'radio' : 'checkbox'; %>
|
||||
<input type="<%- inputType %>" id="staff_lock" name="content-visibility" class="<%- inputType === 'radio' ? 'input input-radio' : 'input input-checkbox' %>" />
|
||||
<%- gettext('Hide from learners') %>
|
||||
</label>
|
||||
</label>
|
||||
<% if (enableHideFromTOCUI) { %>
|
||||
<p class='field-message' id='staff-lock-description'> <%- gettext('It is intentionally hidden from learners, ensuring that only the course staff can view its contents.') %> </p>
|
||||
<% } %>
|
||||
<% if (hasExplicitStaffLock && !ancestorLocked) { %>
|
||||
<p class="tip tip-warning">
|
||||
<% if (xblockInfo.isVertical()) { %>
|
||||
@@ -20,6 +48,7 @@
|
||||
<%- interpolate(message, { xblockType: xblockType }, true) %>
|
||||
<% } %>
|
||||
</p>
|
||||
<% } %>
|
||||
<% } %>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
@@ -240,6 +240,13 @@ class InheritanceMixin(XBlockMixin):
|
||||
scope=Scope.settings
|
||||
)
|
||||
|
||||
hide_from_toc = Boolean(
|
||||
display_name=_("Hide from Table of Contents"),
|
||||
help=_("Enter true or false. If true, this block will be hidden from the Table of Contents."),
|
||||
default=False,
|
||||
scope=Scope.settings
|
||||
)
|
||||
|
||||
@property
|
||||
def close_date(self):
|
||||
"""
|
||||
|
||||
Reference in New Issue
Block a user