feat: Add Studio UI to edit discussions toggle [TNL-7349] (#29720)

Adds a UI in studio to toggle discussions for a units if a course is using a discussion provider that supports it and per-unit discussion control is enabled.

Co-authored-by: Kshitij Sobti <kshitij@opencraft.com

Co-authored-by: Agrendalath <piotr@surowiec.it>
This commit is contained in:
Kshitij Sobti
2022-01-19 06:21:32 +00:00
committed by GitHub
parent d5c9d61a98
commit 8fa962b1c8
8 changed files with 125 additions and 10 deletions

View File

@@ -40,6 +40,7 @@ from common.djangoapps.util.date_utils import get_default_time_display
from common.djangoapps.util.json_request import JsonResponse, expect_json
from common.djangoapps.xblock_django.user_service import DjangoXBlockUserService
from openedx.core.djangoapps.bookmarks import api as bookmarks_api
from openedx.core.djangoapps.discussions.models import DiscussionsConfiguration
from openedx.core.lib.gating import api as gating_api
from openedx.core.lib.xblock_utils import hash_resource, request_token, wrap_xblock, wrap_xblock_aside
from openedx.core.toggles import ENTRANCE_EXAMS
@@ -1221,6 +1222,17 @@ def create_xblock_info(xblock, data=None, metadata=None, include_ancestor_info=F
'category': xblock.category,
'has_children': xblock.has_children
}
if xblock.category == 'course':
discussions_config = DiscussionsConfiguration.get(course.id)
show_unit_level_discussions_toggle = (
discussions_config.enabled and
discussions_config.supports_in_context_discussions() and
discussions_config.enable_in_context and
discussions_config.unit_level_visibility
)
xblock_info["unit_level_discussions"] = show_unit_level_discussions_toggle
if is_concise:
if child_info and child_info.get('children', []):
xblock_info['child_info'] = child_info

View File

@@ -11,7 +11,7 @@ import Course from 'js/models/course';
describe('CourseOutlinePage', function() {
var createCourseOutlinePage, displayNameInput, model, outlinePage, requests, getItemsOfType, getItemHeaders,
verifyItemsExpanded, expandItemsAndVerifyState, collapseItemsAndVerifyState, selectBasicSettings,
selectVisibilitySettings, selectAdvancedSettings, createMockCourseJSON, createMockSectionJSON,
selectVisibilitySettings, selectDiscussionSettings, selectAdvancedSettings, createMockCourseJSON, createMockSectionJSON,
createMockSubsectionJSON, verifyTypePublishable, mockCourseJSON, mockEmptyCourseJSON, setSelfPaced, setSelfPacedCustomPLS,
mockSingleSectionCourseJSON, createMockVerticalJSON, createMockIndexJSON, mockCourseEntranceExamJSON,
selectOnboardingExam, createMockCourseJSONWithReviewRules,mockCourseJSONWithReviewRules,
@@ -37,6 +37,7 @@ describe('CourseOutlinePage', function() {
display_name: 'Section',
children: []
},
unit_level_discussions: false,
user_partitions: [],
user_partition_info: {},
highlights_enabled: true,
@@ -197,6 +198,10 @@ describe('CourseOutlinePage', function() {
$(".modal-section .settings-tab-button[data-tab='advanced']").click();
};
function selectDiscussionSettings() {
$(".modal-section .settings-tab-button[data-tab='discussion']").click();
}
setSelfPaced = function() {
/* global course */
course.set('self_paced', true);
@@ -300,7 +305,7 @@ describe('CourseOutlinePage', function() {
'course-outline', 'xblock-string-field-editor', 'modal-button',
'basic-modal', 'course-outline-modal', 'release-date-editor',
'due-date-editor', 'self-paced-due-date-editor', 'grading-editor', 'publish-editor',
'staff-lock-editor', 'unit-access-editor', 'content-visibility-editor',
'staff-lock-editor', 'unit-access-editor', 'discussion-editor', 'content-visibility-editor',
'settings-modal-tabs', 'timed-examination-preference-editor', 'access-editor',
'show-correctness-editor', 'highlights-editor', 'highlights-enable-editor',
'course-highlights-enable'
@@ -2316,8 +2321,9 @@ describe('CourseOutlinePage', function() {
// Note: most tests for units can be found in Bok Choy
describe('Unit', function() {
var getUnitStatus = function(options) {
mockCourseJSON = createMockCourseJSON({}, [
var getUnitStatus = function(options, courseOptions) {
courseOptions = courseOptions || {};
mockCourseJSON = createMockCourseJSON(courseOptions, [
createMockSectionJSON({}, [
createMockSubsectionJSON({}, [
createMockVerticalJSON(options)
@@ -2409,6 +2415,35 @@ describe('CourseOutlinePage', function() {
expect(messages).toContainText('Contains staff only content');
});
describe('discussion settings', function () {
it('hides discussion settings if unit level discussions are disabled', function() {
getUnitStatus({}, {unit_level_discussions: false});
outlinePage.$('.outline-unit .configure-button').click();
expect($('.modal-section .edit-discussion')).not.toExist();
});
it('shows discussion settings if unit level discussions are enabled', function() {
getUnitStatus({}, {unit_level_discussions: true});
outlinePage.$('.outline-unit .configure-button').click();
expect($('.modal-section .edit-discussion')).toExist();
});
it('marks checkbox as disabled', function() {
getUnitStatus({}, {unit_level_discussions: true});
outlinePage.$('.outline-unit .configure-button').click();
var discussionCheckbox = $('#discussion_enabled');
expect(discussionCheckbox).toExist();
expect(discussionCheckbox.is(':checked')).toBeFalsy();
});
it('marks checkbox as enabled', function() {
getUnitStatus({discussion_enabled: true}, {unit_level_discussions: true});
outlinePage.$('.outline-unit .configure-button').click();
expect($('#discussion_enabled').is(':checked')).toBeTruthy();
});
});
verifyTypePublishable('unit', function(options) {
return createMockCourseJSON({}, [
createMockSectionJSON({}, [

View File

@@ -160,6 +160,7 @@ define(['jquery', 'underscore', 'js/views/xblock_outline', 'common/js/components
var modal;
var enableProctoredExams = false;
var enableTimedExams = false;
var unitLevelDiscussions = false;
if (this.model.get('category') === 'sequential') {
if (this.parentView.parentView.model.has('enable_proctored_exams')) {
enableProctoredExams = this.parentView.parentView.model.get('enable_proctored_exams');
@@ -168,12 +169,15 @@ define(['jquery', 'underscore', 'js/views/xblock_outline', 'common/js/components
enableTimedExams = this.parentView.parentView.model.get('enable_timed_exams');
}
}
if (this.model.get('category') === 'vertical') {
unitLevelDiscussions = this.parentView.parentView.parentView.model.get('unit_level_discussions');
}
modal = CourseOutlineModalsFactory.getModal('edit', this.model, {
onSave: this.refresh.bind(this),
parentInfo: this.parentInfo,
enable_proctored_exams: enableProctoredExams,
enable_timed_exams: enableTimedExams,
unit_level_discussions: unitLevelDiscussions,
xblockType: XBlockViewUtils.getXBlockType(
this.model.get('category'), this.parentView.model, true
)

View File

@@ -17,7 +17,8 @@ define(['jquery', 'backbone', 'underscore', 'gettext', 'js/views/baseview',
AbstractEditor, BaseDateEditor,
ReleaseDateEditor, DueDateEditor, SelfPacedDueDateEditor, GradingEditor, PublishEditor, AbstractVisibilityEditor,
StaffLockEditor, UnitAccessEditor, ContentVisibilityEditor, TimedExaminationPreferenceEditor,
AccessEditor, ShowCorrectnessEditor, HighlightsEditor, HighlightsEnableXBlockModal, HighlightsEnableEditor;
AccessEditor, ShowCorrectnessEditor, HighlightsEditor, HighlightsEnableXBlockModal, HighlightsEnableEditor,
DiscussionEditor;
CourseOutlineXBlockModal = BaseModal.extend({
events: _.extend({}, BaseModal.prototype.events, {
@@ -934,6 +935,50 @@ define(['jquery', 'backbone', 'underscore', 'gettext', 'js/views/baseview',
}
});
DiscussionEditor = AbstractEditor.extend({
templateName: 'discussion-editor',
className: 'edit-discussion',
afterRender: function() {
AbstractEditor.prototype.afterRender.call(this);
this.setStatus(this.currentValue());
},
currentValue: function() {
var discussionEnabled = this.model.get('discussion_enabled');
return discussionEnabled === true || discussionEnabled === 'enabled';
},
setStatus: function(value) {
this.$('#discussion_enabled').prop('checked', value);
},
isEnabled: function() {
return this.$('#discussion_enabled').is(':checked');
},
hasChanges: function() {
return this.currentValue() !== this.isEnabled();
},
getRequestData: function() {
if (this.hasChanges()) {
return {
publish: 'republish',
metadata: {
discussion_enabled: this.isEnabled()
}
};
} else {
return {};
}
},
getContext: function() {
return {
hasDiscussionEnabled: this.currentValue()
};
}
});
ContentVisibilityEditor = AbstractVisibilityEditor.extend({
templateName: 'content-visibility-editor',
className: 'edit-content-visibility',
@@ -1169,7 +1214,7 @@ define(['jquery', 'backbone', 'underscore', 'gettext', 'js/views/baseview',
editors: []
};
if (xblockInfo.isVertical()) {
editors = [StaffLockEditor, UnitAccessEditor];
editors = [StaffLockEditor, UnitAccessEditor, DiscussionEditor];
} else {
tabs = [
{
@@ -1215,6 +1260,10 @@ define(['jquery', 'backbone', 'underscore', 'gettext', 'js/views/baseview',
});
}
if (!options.unit_level_discussions) {
editors = _.without(editors, DiscussionEditor);
}
return new SettingsXBlockModal($.extend({
tabs: tabs,
editors: editors,

View File

@@ -745,6 +745,7 @@
}
}
.edit-discussion,
.edit-staff-lock,
.edit-content-visibility,
.edit-unit-access {
@@ -756,7 +757,8 @@
}
}
// UI: staff lock section
// UI: staff lock and discussion
.edit-discussion,
.edit-staff-lock,
.edit-settings-timed-examination,
.edit-unit-access {
@@ -828,6 +830,7 @@
}
}
.edit-discussion,
.edit-unit-access,
.edit-staff-lock {
.modal-section-content {
@@ -869,6 +872,7 @@
}
}
.edit-discussion,
.edit-unit-access,
.edit-staff-lock {
.modal-section-content {

View File

@@ -29,7 +29,7 @@ from django.urls import reverse
<%block name="header_extras">
<link rel="stylesheet" type="text/css" href="${static.url('js/vendor/timepicker/jquery.timepicker.css')}" />
% for template_name in ['course-outline', 'xblock-string-field-editor', 'basic-modal', 'modal-button', 'course-outline-modal', 'due-date-editor', 'self-paced-due-date-editor', 'release-date-editor', 'grading-editor', 'publish-editor', 'staff-lock-editor', 'unit-access-editor', 'content-visibility-editor', 'verification-access-editor', 'timed-examination-preference-editor', 'access-editor', 'settings-modal-tabs', 'show-correctness-editor', 'highlights-editor', 'highlights-enable-editor', 'course-highlights-enable']:
% for template_name in ['course-outline', 'xblock-string-field-editor', 'basic-modal', 'modal-button', 'course-outline-modal', 'due-date-editor', 'self-paced-due-date-editor', 'release-date-editor', 'grading-editor', 'publish-editor', 'staff-lock-editor', 'unit-access-editor', 'discussion-editor', 'content-visibility-editor', 'verification-access-editor', 'timed-examination-preference-editor', 'access-editor', 'settings-modal-tabs', 'show-correctness-editor', 'highlights-editor', 'highlights-enable-editor', 'course-highlights-enable']:
<script type="text/template" id="${template_name}-tpl">
<%static:include path="js/${template_name}.underscore" />
</script>

View File

@@ -0,0 +1,11 @@
<form>
<h3 class="modal-section-title">
<%- gettext('Discussion') %>
</h3>
<div class="modal-section-content discussion-enabled">
<label class="label">
<input type="checkbox" id="discussion_enabled" name="discussion_enabled" class="input input-checkbox" />
<%- gettext('Enable discussion') %>
</label>
</div>
</form>

View File

@@ -3,7 +3,7 @@ var userPartitionInfo = xblockInfo.get('user_partition_info');
var selectablePartitions = userPartitionInfo['selectable_partitions'];
%>
<form>
<% if (selectablePartitions.length > 0) { %>
<% if (selectablePartitions && selectablePartitions.length > 0) { %>
<h3 class="modal-section-title access-change">
<%- gettext('Unit Access') %>
</h3>