diff --git a/cms/djangoapps/contentstore/views/item.py b/cms/djangoapps/contentstore/views/item.py index 16cff22f32..b79c7355ec 100644 --- a/cms/djangoapps/contentstore/views/item.py +++ b/cms/djangoapps/contentstore/views/item.py @@ -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 diff --git a/cms/static/js/spec/views/pages/course_outline_spec.js b/cms/static/js/spec/views/pages/course_outline_spec.js index b4fd8143b7..332e9849f9 100644 --- a/cms/static/js/spec/views/pages/course_outline_spec.js +++ b/cms/static/js/spec/views/pages/course_outline_spec.js @@ -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({}, [ diff --git a/cms/static/js/views/course_outline.js b/cms/static/js/views/course_outline.js index 5233bb7061..acdd46d5e1 100644 --- a/cms/static/js/views/course_outline.js +++ b/cms/static/js/views/course_outline.js @@ -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 ) diff --git a/cms/static/js/views/modals/course_outline_modals.js b/cms/static/js/views/modals/course_outline_modals.js index 95b1d982f3..9503338d04 100644 --- a/cms/static/js/views/modals/course_outline_modals.js +++ b/cms/static/js/views/modals/course_outline_modals.js @@ -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, diff --git a/cms/static/sass/elements/_modal-window.scss b/cms/static/sass/elements/_modal-window.scss index 6eeb100043..b6508ef49a 100644 --- a/cms/static/sass/elements/_modal-window.scss +++ b/cms/static/sass/elements/_modal-window.scss @@ -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 { diff --git a/cms/templates/course_outline.html b/cms/templates/course_outline.html index 912948c2ed..74a7592021 100644 --- a/cms/templates/course_outline.html +++ b/cms/templates/course_outline.html @@ -29,7 +29,7 @@ from django.urls import reverse <%block name="header_extras"> -% 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']: diff --git a/cms/templates/js/discussion-editor.underscore b/cms/templates/js/discussion-editor.underscore new file mode 100644 index 0000000000..c0fff944a9 --- /dev/null +++ b/cms/templates/js/discussion-editor.underscore @@ -0,0 +1,11 @@ +
diff --git a/cms/templates/js/unit-access-editor.underscore b/cms/templates/js/unit-access-editor.underscore index a35b8bc109..93d3379094 100644 --- a/cms/templates/js/unit-access-editor.underscore +++ b/cms/templates/js/unit-access-editor.underscore @@ -3,7 +3,7 @@ var userPartitionInfo = xblockInfo.get('user_partition_info'); var selectablePartitions = userPartitionInfo['selectable_partitions']; %>