diff --git a/cms/djangoapps/contentstore/views/item.py b/cms/djangoapps/contentstore/views/item.py
index eb40ebed51..0004a13111 100644
--- a/cms/djangoapps/contentstore/views/item.py
+++ b/cms/djangoapps/contentstore/views/item.py
@@ -982,6 +982,11 @@ def create_xblock_info(xblock, data=None, metadata=None, include_ancestor_info=F
"user_partitions": get_user_partition_info(xblock, course=course),
}
+ if xblock.category == 'sequential':
+ xblock_info.update({
+ "hide_after_due": xblock.hide_after_due,
+ })
+
# update xblock_info with special exam information if the feature flag is enabled
if settings.FEATURES.get('ENABLE_SPECIAL_EXAMS'):
if xblock.category == 'course':
@@ -997,7 +1002,6 @@ def create_xblock_info(xblock, data=None, metadata=None, include_ancestor_info=F
"is_time_limited": xblock.is_time_limited,
"exam_review_rules": xblock.exam_review_rules,
"default_time_limit_minutes": xblock.default_time_limit_minutes,
- "hide_after_due": xblock.hide_after_due,
})
# Update with gating info
diff --git a/cms/static/js/spec/views/pages/container_subviews_spec.js b/cms/static/js/spec/views/pages/container_subviews_spec.js
index 22af2fb667..66c6997e0a 100644
--- a/cms/static/js/spec/views/pages/container_subviews_spec.js
+++ b/cms/static/js/spec/views/pages/container_subviews_spec.js
@@ -434,7 +434,7 @@ define(["jquery", "underscore", "underscore.string", "edx-ui-toolkit/js/utils/sp
expect(visibilityCopy).toContain('Staff Only');
expect(containerPage.$(bitPublishingCss)).toHaveClass(staffOnlyClass);
} else {
- expect(visibilityCopy).toBe('Staff and Students');
+ expect(visibilityCopy).toBe('Staff and Learners');
expect(containerPage.$(bitPublishingCss)).not.toHaveClass(staffOnlyClass);
verifyExplicitStaffOnly(false);
verifyImplicitStaffOnly(false);
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 efbe55607a..7d59717898 100644
--- a/cms/static/js/spec/views/pages/course_outline_spec.js
+++ b/cms/static/js/spec/views/pages/course_outline_spec.js
@@ -7,10 +7,10 @@ define(["jquery", "edx-ui-toolkit/js/utils/spec-helpers/ajax-helpers", "common/j
describe("CourseOutlinePage", function() {
var createCourseOutlinePage, displayNameInput, model, outlinePage, requests,
getItemsOfType, getItemHeaders, verifyItemsExpanded, expandItemsAndVerifyState,
- collapseItemsAndVerifyState, createMockCourseJSON, createMockSectionJSON, createMockSubsectionJSON,
- verifyTypePublishable, mockCourseJSON, mockEmptyCourseJSON, mockSingleSectionCourseJSON,
- createMockVerticalJSON, createMockIndexJSON, mockCourseEntranceExamJSON,
- mockOutlinePage = readFixtures('mock/mock-course-outline-page.underscore'),
+ collapseItemsAndVerifyState, selectBasicSettings, selectAdvancedSettings, createMockCourseJSON,
+ createMockSectionJSON, createMockSubsectionJSON, verifyTypePublishable, mockCourseJSON,
+ mockEmptyCourseJSON, mockSingleSectionCourseJSON, createMockVerticalJSON, createMockIndexJSON,
+ mockCourseEntranceExamJSON, mockOutlinePage = readFixtures('mock/mock-course-outline-page.underscore'),
mockRerunNotification = readFixtures('mock/mock-course-rerun-notification.underscore');
createMockCourseJSON = function(options, children) {
@@ -137,6 +137,14 @@ define(["jquery", "edx-ui-toolkit/js/utils/spec-helpers/ajax-helpers", "common/j
verifyItemsExpanded(type, false);
};
+ selectBasicSettings = function() {
+ this.$(".modal-section .settings-tab-button[data-tab='basic']").click();
+ };
+
+ selectAdvancedSettings = function() {
+ this.$(".modal-section .settings-tab-button[data-tab='advanced']").click();
+ };
+
createCourseOutlinePage = function(test, courseJSON, createOnly) {
requests = AjaxHelpers.requests(test);
model = new XBlockOutlineInfo(courseJSON, { parse: true });
@@ -230,8 +238,8 @@ define(["jquery", "edx-ui-toolkit/js/utils/spec-helpers/ajax-helpers", "common/j
'course-outline', 'xblock-string-field-editor', 'modal-button',
'basic-modal', 'course-outline-modal', 'release-date-editor',
'due-date-editor', 'grading-editor', 'publish-editor',
- 'staff-lock-editor', 'settings-modal-tabs', 'timed-examination-preference-editor',
- 'access-editor'
+ 'staff-lock-editor','content-visibility-editor', 'settings-modal-tabs',
+ 'timed-examination-preference-editor', 'access-editor'
]);
appendSetFixtures(mockOutlinePage);
mockCourseJSON = createMockCourseJSON({}, [
@@ -535,8 +543,10 @@ define(["jquery", "edx-ui-toolkit/js/utils/spec-helpers/ajax-helpers", "common/j
expect($("due_date")).not.toExist();
expect($("grading_format")).not.toExist();
- // Staff lock controls are always visible
+ // Staff lock controls are always visible on the advanced tab
+ selectAdvancedSettings();
expect($("#staff_lock")).toExist();
+ selectBasicSettings();
$(".wrapper-modal-window .action-save").click();
AjaxHelpers.expectJsonRequest(requests, 'POST', '/xblock/mock-section', {
"metadata":{
@@ -605,43 +615,33 @@ define(["jquery", "edx-ui-toolkit/js/utils/spec-helpers/ajax-helpers", "common/j
});
describe("Subsection", function() {
- var getDisplayNameWrapper, setEditModalValues, mockServerValuesJson,
- selectDisableSpecialExams, selectBasicSettings, selectAdvancedSettings,
- selectAccessSettings, selectTimedExam, selectProctoredExam, selectPracticeExam,
+ var getDisplayNameWrapper, setEditModalValues, setContentVisibility, mockServerValuesJson,
+ selectDisableSpecialExams, selectTimedExam, selectProctoredExam, selectPracticeExam,
selectPrerequisite, selectLastPrerequisiteSubsection, checkOptionFieldVisibility;
getDisplayNameWrapper = function() {
return getItemHeaders('subsection').find('.wrapper-xblock-field');
};
- setEditModalValues = function (start_date, due_date, grading_type, is_locked) {
+ setEditModalValues = function (start_date, due_date, grading_type) {
$("#start_date").val(start_date);
$("#due_date").val(due_date);
$("#grading_type").val(grading_type);
- $("#staff_lock").prop('checked', is_locked);
+ };
+
+ setContentVisibility = function (visibility) {
+ $('input[name=content-visibility][value='+visibility+']').prop('checked', true);
};
selectDisableSpecialExams = function() {
this.$("input.no_special_exam").prop('checked', true).trigger('change');
};
- selectBasicSettings = function() {
- this.$(".modal-section .settings-tab-button[data-tab='basic']").click();
- };
-
- selectAdvancedSettings = function() {
- this.$(".modal-section .settings-tab-button[data-tab='advanced']").click();
- };
-
- selectAccessSettings = function() {
- this.$(".modal-section .settings-tab-button[data-tab='access']").click();
- };
-
- selectTimedExam = function(time_limit, hide_after_due) {
+ selectTimedExam = function(time_limit) {
this.$("input.timed_exam").prop('checked', true).trigger('change');
this.$(".field-time-limit input").val(time_limit);
this.$(".field-time-limit input").trigger('focusout');
- this.$('.field-hide-after-due input').prop('checked', hide_after_due).trigger('change');
+ setContentVisibility("hide_after_due");
};
selectProctoredExam = function(time_limit) {
@@ -666,10 +666,9 @@ define(["jquery", "edx-ui-toolkit/js/utils/spec-helpers/ajax-helpers", "common/j
};
// Helper to validate oft-checked additional option fields' visibility
- checkOptionFieldVisibility = function(time_limit, review_rules, hide_after_due) {
+ checkOptionFieldVisibility = function(time_limit, review_rules) {
expect($('.field-time-limit').is(':visible')).toBe(time_limit);
expect($('.field-exam-review-rules').is(':visible')).toBe(review_rules);
- expect($('.field-hide-after-due').is(':visible')).toBe(hide_after_due);
};
// Contains hard-coded dates because dates are presented in different formats.
@@ -767,7 +766,6 @@ define(["jquery", "edx-ui-toolkit/js/utils/spec-helpers/ajax-helpers", "common/j
selectBasicSettings();
expect($('.modal-section .settings-tab-button[data-tab="basic"]')).toHaveClass('active');
expect($('.modal-section .settings-tab-button[data-tab="advanced"]')).not.toHaveClass('active');
- expect($('.modal-section .settings-tab-button[data-tab="access"]')).not.toHaveClass('active');
});
it('can show advanced settings', function() {
@@ -776,20 +774,11 @@ define(["jquery", "edx-ui-toolkit/js/utils/spec-helpers/ajax-helpers", "common/j
selectAdvancedSettings();
expect($('.modal-section .settings-tab-button[data-tab="basic"]')).not.toHaveClass('active');
expect($('.modal-section .settings-tab-button[data-tab="advanced"]')).toHaveClass('active');
- expect($('.modal-section .settings-tab-button[data-tab="access"]')).not.toHaveClass('active');
- });
-
- it('can show access settings', function() {
- createCourseOutlinePage(this, mockCourseJSON, false);
- outlinePage.$('.outline-subsection .configure-button').click();
- selectAccessSettings();
- expect($('.modal-section .settings-tab-button[data-tab="basic"]')).not.toHaveClass('active');
- expect($('.modal-section .settings-tab-button[data-tab="advanced"]')).not.toHaveClass('active');
- expect($('.modal-section .settings-tab-button[data-tab="access"]')).toHaveClass('active');
});
it('does not show settings tab headers if there is only one tab to show', function() {
- var mockSubsectionJSON = createMockSubsectionJSON({}, []);
+ var mockVerticalJSON = createMockVerticalJSON({}, []);
+ var mockSubsectionJSON = createMockSubsectionJSON({}, [mockVerticalJSON]);
delete mockSubsectionJSON.is_prereq;
delete mockSubsectionJSON.prereqs;
delete mockSubsectionJSON.prereq;
@@ -801,7 +790,7 @@ define(["jquery", "edx-ui-toolkit/js/utils/spec-helpers/ajax-helpers", "common/j
createMockSectionJSON({}, [mockSubsectionJSON])
]);
createCourseOutlinePage(this, mockCourseJSON, false);
- outlinePage.$('.outline-subsection .configure-button').click();
+ outlinePage.$('.outline-unit .configure-button').click();
expect($(".settings-tabs-header").length).toBe(0);
});
@@ -818,7 +807,7 @@ define(["jquery", "edx-ui-toolkit/js/utils/spec-helpers/ajax-helpers", "common/j
expect($(".edit-settings-release").length).toBe(0);
expect($(".grading-due-date").length).toBe(0);
expect($(".edit-settings-grading").length).toBe(1);
- expect($(".edit-staff-lock").length).toBe(1);
+ expect($(".edit-content-visibility").length).toBe(1);
});
it('can select valid time', function() {
@@ -847,16 +836,16 @@ define(["jquery", "edx-ui-toolkit/js/utils/spec-helpers/ajax-helpers", "common/j
it('can be edited', function() {
createCourseOutlinePage(this, mockCourseJSON, false);
outlinePage.$('.outline-subsection .configure-button').click();
- setEditModalValues("7/9/2014", "7/10/2014", "Lab", true);
+ setEditModalValues("7/9/2014", "7/10/2014", "Lab");
selectAdvancedSettings();
- selectTimedExam("02:30", true);
+ selectTimedExam("02:30");
$(".wrapper-modal-window .action-save").click();
AjaxHelpers.expectJsonRequest(requests, 'POST', '/xblock/mock-subsection', {
"graderType":"Lab",
"publish": "republish",
"isPrereq": false,
"metadata":{
- "visible_to_staff_only": true,
+ "visible_to_staff_only": null,
"start":"2014-07-09T00:00:00.000Z",
"due":"2014-07-10T00:00:00.000Z",
"exam_review_rules": "",
@@ -892,21 +881,21 @@ define(["jquery", "edx-ui-toolkit/js/utils/spec-helpers/ajax-helpers", "common/j
expect($("#start_date").val()).toBe('7/9/2014');
expect($("#due_date").val()).toBe('7/10/2014');
expect($("#grading_type").val()).toBe('Lab');
- expect($("#staff_lock").is(":checked")).toBe(true);
+ expect($("input[name=content-visibility][value=staff_only]").is(":checked")).toBe(true);
expect($("input.timed_exam").is(":checked")).toBe(true);
expect($("input.proctored_exam").is(":checked")).toBe(false);
expect($("input.no_special_exam").is(":checked")).toBe(false);
expect($("input.practice_exam").is(":checked")).toBe(false);
expect($(".field-time-limit input").val()).toBe("02:30");
- expect($(".field-hide-after-due input").is(":checked")).toBe(true);
});
it('can hide time limit and hide after due fields when the None radio box is selected', function() {
createCourseOutlinePage(this, mockCourseJSON, false);
outlinePage.$('.outline-subsection .configure-button').click();
- setEditModalValues("7/9/2014", "7/10/2014", "Lab", true);
+ setEditModalValues("7/9/2014", "7/10/2014", "Lab");
selectAdvancedSettings();
selectDisableSpecialExams();
+ setContentVisibility("staff_only");
// all additional options should be hidden
expect($('.exam-options').is(':hidden')).toBe(true);
@@ -915,12 +904,13 @@ define(["jquery", "edx-ui-toolkit/js/utils/spec-helpers/ajax-helpers", "common/j
it('can select the practice exam', function() {
createCourseOutlinePage(this, mockCourseJSON, false);
outlinePage.$('.outline-subsection .configure-button').click();
- setEditModalValues("7/9/2014", "7/10/2014", "Lab", true);
+ setEditModalValues("7/9/2014", "7/10/2014", "Lab");
selectAdvancedSettings();
selectPracticeExam("00:30");
+ setContentVisibility("staff_only");
- // time limit should be visible, review rules and hide after due should be hidden
- checkOptionFieldVisibility(true, false, false);
+ // time limit should be visible, review rules should be hidden
+ checkOptionFieldVisibility(true, false);
$(".wrapper-modal-window .action-save").click();
});
@@ -928,12 +918,12 @@ define(["jquery", "edx-ui-toolkit/js/utils/spec-helpers/ajax-helpers", "common/j
it('can select the timed exam', function() {
createCourseOutlinePage(this, mockCourseJSON, false);
outlinePage.$('.outline-subsection .configure-button').click();
- setEditModalValues("7/9/2014", "7/10/2014", "Lab", true);
+ setEditModalValues("7/9/2014", "7/10/2014", "Lab");
selectAdvancedSettings();
selectTimedExam("00:30");
- // time limit and hide after due should be visible, review rules should be hidden
- checkOptionFieldVisibility(true, false, true);
+ // time limit should be visible, review rules should be hidden
+ checkOptionFieldVisibility(true, false);
$(".wrapper-modal-window .action-save").click();
});
@@ -941,12 +931,13 @@ define(["jquery", "edx-ui-toolkit/js/utils/spec-helpers/ajax-helpers", "common/j
it('can select the Proctored exam option', function() {
createCourseOutlinePage(this, mockCourseJSON, false);
outlinePage.$('.outline-subsection .configure-button').click();
- setEditModalValues("7/9/2014", "7/10/2014", "Lab", true);
+ setEditModalValues("7/9/2014", "7/10/2014", "Lab");
selectAdvancedSettings();
selectProctoredExam("00:30");
+ setContentVisibility("staff_only");
- // time limit and review rules should be visible, hide after due should be hidden
- checkOptionFieldVisibility(true, true, false);
+ // time limit and review rules should be visible
+ checkOptionFieldVisibility(true, true);
$(".wrapper-modal-window .action-save").click();
@@ -955,9 +946,10 @@ define(["jquery", "edx-ui-toolkit/js/utils/spec-helpers/ajax-helpers", "common/j
it('entering invalid time format uses default value of 30 minutes.', function() {
createCourseOutlinePage(this, mockCourseJSON, false);
outlinePage.$('.outline-subsection .configure-button').click();
- setEditModalValues("7/9/2014", "7/10/2014", "Lab", true);
+ setEditModalValues("7/9/2014", "7/10/2014", "Lab");
selectAdvancedSettings();
selectProctoredExam("abcd");
+ setContentVisibility("staff_only");
// time limit field should be visible and have the correct value
expect($('.field-time-limit').is(':visible')).toBe(true);
@@ -992,7 +984,6 @@ define(["jquery", "edx-ui-toolkit/js/utils/spec-helpers/ajax-helpers", "common/j
expect($("input.no_special_exam").is(":checked")).toBe(true);
expect($("input.practice_exam").is(":checked")).toBe(false);
expect($(".field-time-limit input").val()).toBe("02:30");
- expect($('.field-hide-after-due').is(':hidden')).toBe(true);
});
it('can show a saved timed exam correctly when hide_after_due is true', function() {
@@ -1022,7 +1013,6 @@ define(["jquery", "edx-ui-toolkit/js/utils/spec-helpers/ajax-helpers", "common/j
expect($("input.no_special_exam").is(":checked")).toBe(false);
expect($("input.practice_exam").is(":checked")).toBe(false);
expect($(".field-time-limit input").val()).toBe("00:10");
- expect($('.field-hide-after-due input').is(":checked")).toBe(true);
});
it('can show a saved timed exam correctly when hide_after_due is true', function() {
@@ -1081,7 +1071,6 @@ define(["jquery", "edx-ui-toolkit/js/utils/spec-helpers/ajax-helpers", "common/j
expect($("input.no_special_exam").is(":checked")).toBe(false);
expect($("input.practice_exam").is(":checked")).toBe(true);
expect($(".field-time-limit input").val()).toBe("02:30");
- expect($('.field-hide-after-due').is(':hidden')).toBe(true);
});
it('can show a saved proctored exam correctly', function() {
@@ -1110,7 +1099,6 @@ define(["jquery", "edx-ui-toolkit/js/utils/spec-helpers/ajax-helpers", "common/j
expect($("input.no_special_exam").is(":checked")).toBe(false);
expect($("input.practice_exam").is(":checked")).toBe(false);
expect($(".field-time-limit input").val()).toBe("02:30");
- expect($('.field-hide-after-due').is(':hidden')).toBe(true);
});
it('does not show proctored settings if proctored exams not enabled', function() {
@@ -1138,7 +1126,6 @@ define(["jquery", "edx-ui-toolkit/js/utils/spec-helpers/ajax-helpers", "common/j
expect($("input.timed_exam").is(":checked")).toBe(true);
expect($("input.no_special_exam").is(":checked")).toBe(false);
expect($(".field-time-limit input").val()).toBe("02:30");
- expect($('.field-hide-after-due input').is(":checked")).toBe(true);
});
it('can select prerequisite', function() {
@@ -1300,7 +1287,7 @@ define(["jquery", "edx-ui-toolkit/js/utils/spec-helpers/ajax-helpers", "common/j
]);
createCourseOutlinePage(this, mockCourseWithPreqsJSON, false);
outlinePage.$('.outline-subsection .configure-button').click();
- selectAccessSettings();
+ selectAdvancedSettings();
selectLastPrerequisiteSubsection('');
expect($('#prereq_min_score_error').css('display')).toBe('none');
selectLastPrerequisiteSubsection('80');
@@ -1314,7 +1301,8 @@ define(["jquery", "edx-ui-toolkit/js/utils/spec-helpers/ajax-helpers", "common/j
it('release date, due date, grading type, and staff lock can be cleared.', function() {
createCourseOutlinePage(this, mockCourseJSON, false);
outlinePage.$('.outline-item .outline-subsection .configure-button').click();
- setEditModalValues("7/9/2014", "7/10/2014", "Lab", true);
+ setEditModalValues("7/9/2014", "7/10/2014", "Lab");
+ setContentVisibility("staff_only");
$(".wrapper-modal-window .action-save").click();
// This is the response for the change operation.
@@ -1339,7 +1327,7 @@ define(["jquery", "edx-ui-toolkit/js/utils/spec-helpers/ajax-helpers", "common/j
expect($("#start_date").val()).toBe('7/9/2014');
expect($("#due_date").val()).toBe('7/10/2014');
expect($("#grading_type").val()).toBe('Lab');
- expect($("#staff_lock").is(":checked")).toBe(true);
+ expect($("input[name=content-visibility][value=staff_only]").is(":checked")).toBe(true);
$(".wrapper-modal-window .scheduled-date-input .action-clear").click();
$(".wrapper-modal-window .due-date-input .action-clear").click();
@@ -1347,7 +1335,7 @@ define(["jquery", "edx-ui-toolkit/js/utils/spec-helpers/ajax-helpers", "common/j
expect($("#due_date").val()).toBe('');
$("#grading_type").val('notgraded');
- $("#staff_lock").prop('checked', false);
+ setContentVisibility("visible");
$(".wrapper-modal-window .action-save").click();
diff --git a/cms/static/js/utils/date_utils.js b/cms/static/js/utils/date_utils.js
index db2b9ff726..ee96162063 100644
--- a/cms/static/js/utils/date_utils.js
+++ b/cms/static/js/utils/date_utils.js
@@ -52,7 +52,10 @@ function($, date, TriggerChangeEventOnEnter) {
// given a pair of inputs (datepicker and timepicker), return a JS Date
// object that corresponds to the datetime.js that they represent. Assume
// UTC timezone, NOT the timezone of the user's browser.
- var date = $(datepickerInput).datepicker("getDate"), time = null;
+ var date = null, time = null;
+ if (datepickerInput.length > 0) {
+ date = $(datepickerInput).datepicker("getDate");
+ }
if (timepickerInput.length > 0) {
time = $(timepickerInput).timepicker("getTime");
}
diff --git a/cms/static/js/views/modals/course_outline_modals.js b/cms/static/js/views/modals/course_outline_modals.js
index ee4a202d88..340f52e605 100644
--- a/cms/static/js/views/modals/course_outline_modals.js
+++ b/cms/static/js/views/modals/course_outline_modals.js
@@ -7,14 +7,15 @@
*/
define(['jquery', 'backbone', 'underscore', 'gettext', 'js/views/baseview',
'js/views/modals/base_modal', 'date', 'js/views/utils/xblock_utils',
- 'js/utils/date_utils'
+ 'js/utils/date_utils', 'edx-ui-toolkit/js/utils/html-utils',
+ 'edx-ui-toolkit/js/utils/string-utils'
], function(
- $, Backbone, _, gettext, BaseView, BaseModal, date, XBlockViewUtils, DateUtils
+ $, Backbone, _, gettext, BaseView, BaseModal, date, XBlockViewUtils, DateUtils, HtmlUtils, StringUtils
) {
'use strict';
var CourseOutlineXBlockModal, SettingsXBlockModal, PublishXBlockModal, AbstractEditor, BaseDateEditor,
- ReleaseDateEditor, DueDateEditor, GradingEditor, PublishEditor, StaffLockEditor,
- VerificationAccessEditor, TimedExaminationPreferenceEditor, AccessEditor;
+ ReleaseDateEditor, DueDateEditor, GradingEditor, PublishEditor, AbstractVisibilityEditor, StaffLockEditor,
+ ContentVisibilityEditor, VerificationAccessEditor, TimedExaminationPreferenceEditor, AccessEditor;
CourseOutlineXBlockModal = BaseModal.extend({
events : _.extend({}, BaseModal.prototype.events, {
@@ -105,9 +106,9 @@ define(['jquery', 'backbone', 'underscore', 'gettext', 'js/views/baseview',
SettingsXBlockModal = CourseOutlineXBlockModal.extend({
getTitle: function () {
- return interpolate(
- gettext('%(display_name)s Settings'),
- { display_name: this.model.get('display_name') }, true
+ return StringUtils.interpolate(
+ gettext('{display_name} Settings'),
+ { display_name: this.model.get('display_name') }
);
},
@@ -115,9 +116,10 @@ define(['jquery', 'backbone', 'underscore', 'gettext', 'js/views/baseview',
var message = '';
var tabs = this.options.tabs;
if (!tabs || tabs.length < 2) {
- message = interpolate(
- gettext('Change the settings for %(display_name)s'),
- { display_name: this.model.get('display_name') }, true);
+ message = StringUtils.interpolate(
+ gettext('Change the settings for {display_name}'),
+ { display_name: this.model.get('display_name') }
+ );
}
return message;
},
@@ -127,7 +129,7 @@ define(['jquery', 'backbone', 'underscore', 'gettext', 'js/views/baseview',
if (tabs && tabs.length > 0) {
if (tabs.length > 1) {
var tabsTemplate = this.loadTemplate('settings-modal-tabs');
- this.$('.modal-section').html(tabsTemplate({tabs: tabs}));
+ HtmlUtils.setHtml(this.$('.modal-section'), HtmlUtils.HTML(tabsTemplate({tabs: tabs})));
_.each(this.options.tabs, function(tab) {
this.options.editors.push.apply(
this.options.editors,
@@ -196,16 +198,16 @@ define(['jquery', 'backbone', 'underscore', 'gettext', 'js/views/baseview',
},
getTitle: function () {
- return interpolate(
- gettext('Publish %(display_name)s'),
- { display_name: this.model.get('display_name') }, true
+ return StringUtils.interpolate(
+ gettext('Publish {display_name}'),
+ { display_name: this.model.get('display_name') }
);
},
getIntroductionMessage: function () {
- return interpolate(
- gettext('Publish all unpublished changes for this %(item)s?'),
- { item: this.options.xblockType }, true
+ return StringUtils.interpolate(
+ gettext('Publish all unpublished changes for this {item}?'),
+ { item: this.options.xblockType }
);
},
@@ -233,7 +235,7 @@ define(['jquery', 'backbone', 'underscore', 'gettext', 'js/views/baseview',
enable_timed_exam: this.options.enable_timed_exams
}, this.getContext()));
- this.$el.html(html);
+ HtmlUtils.setHtml(this.$el, HtmlUtils.HTML(html));
this.parentElement.append(this.$el);
},
@@ -343,7 +345,7 @@ define(['jquery', 'backbone', 'underscore', 'gettext', 'js/views/baseview',
this.$('.exam-options').hide();
this.$('.field-time-limit input').val('00:00');
},
- selectSpecialExam: function (showRulesField, showHideAfterDueField) {
+ selectSpecialExam: function (showRulesField) {
this.$('.exam-options').show();
this.$('.field-time-limit').show();
if (!this.isValidTimeLimit(this.$('.field-time-limit input').val())) {
@@ -355,24 +357,18 @@ define(['jquery', 'backbone', 'underscore', 'gettext', 'js/views/baseview',
else {
this.$('.field-exam-review-rules').hide();
}
- if (showHideAfterDueField) {
- this.$('.field-hide-after-due').show();
- }
- else {
- this.$('.field-hide-after-due').hide();
- }
},
setTimedExam: function (event) {
event.preventDefault();
- this.selectSpecialExam(false, true);
+ this.selectSpecialExam(false);
},
setPracticeExam: function (event) {
event.preventDefault();
- this.selectSpecialExam(false, false);
+ this.selectSpecialExam(false);
},
setProctoredExam: function (event) {
event.preventDefault();
- this.selectSpecialExam(true, false);
+ this.selectSpecialExam(true);
},
timeLimitFocusout: function(event) {
event.preventDefault();
@@ -395,12 +391,10 @@ define(['jquery', 'backbone', 'underscore', 'gettext', 'js/views/baseview',
this.setExamTime(this.model.get('default_time_limit_minutes'));
this.setReviewRules(this.model.get('exam_review_rules'));
- this.setHideAfterDue(this.model.get('hide_after_due'));
},
setExamType: function(is_time_limited, is_proctored_exam, is_practice_exam) {
this.$('.field-time-limit').hide();
this.$('.field-exam-review-rules').hide();
- this.$('.field-hide-after-due').hide();
if (!is_time_limited) {
this.$('input.no_special_exam').prop('checked', true);
@@ -421,7 +415,6 @@ define(['jquery', 'backbone', 'underscore', 'gettext', 'js/views/baseview',
// if the subsection is not time limited, then
// here we rightfully assume that it just a timed exam
this.$('input.timed_exam').prop('checked', true);
- this.$('.field-hide-after-due').show();
}
},
setExamTime: function(value) {
@@ -431,9 +424,6 @@ define(['jquery', 'backbone', 'underscore', 'gettext', 'js/views/baseview',
setReviewRules: function (value) {
this.$('.field-exam-review-rules textarea').val(value);
},
- setHideAfterDue: function(value) {
- this.$('.field-hide-after-due input').prop('checked', value);
- },
isValidTimeLimit: function(time_limit) {
var pattern = new RegExp('^\\d{1,2}:[0-5][0-9]$');
return pattern.test(time_limit) && time_limit !== "00:00";
@@ -459,7 +449,6 @@ define(['jquery', 'backbone', 'underscore', 'gettext', 'js/views/baseview',
var is_proctored_exam;
var time_limit = this.getExamTimeLimit();
var exam_review_rules = this.$('.field-exam-review-rules textarea').val();
- var hide_after_due = this.$('.field-hide-after-due input').is(':checked');
if (this.$('input.no_special_exam').is(':checked')){
is_time_limited = false;
@@ -484,7 +473,6 @@ define(['jquery', 'backbone', 'underscore', 'gettext', 'js/views/baseview',
'is_practice_exam': is_practice_exam,
'is_time_limited': is_time_limited,
'exam_review_rules': exam_review_rules,
- 'hide_after_due': hide_after_due,
// We have to use the legacy field name
// as the Ajax handler directly populates
// the xBlocks fields. We will have to
@@ -590,9 +578,11 @@ define(['jquery', 'backbone', 'underscore', 'gettext', 'js/views/baseview',
}
});
- StaffLockEditor = AbstractEditor.extend({
- templateName: 'staff-lock-editor',
- className: 'edit-staff-lock',
+ AbstractVisibilityEditor = AbstractEditor.extend({
+ afterRender: function () {
+ AbstractEditor.prototype.afterRender.call(this);
+ },
+
isModelLocked: function() {
return this.model.get('has_explicit_staff_lock');
},
@@ -601,8 +591,19 @@ define(['jquery', 'backbone', 'underscore', 'gettext', 'js/views/baseview',
return this.model.get('ancestor_has_staff_lock');
},
+ getContext: function () {
+ return {
+ hasExplicitStaffLock: this.isModelLocked(),
+ ancestorLocked: this.isAncestorLocked()
+ };
+ }
+ });
+
+ StaffLockEditor = AbstractVisibilityEditor.extend({
+ templateName: 'staff-lock-editor',
+ className: 'edit-staff-lock',
afterRender: function () {
- AbstractEditor.prototype.afterRender.call(this);
+ AbstractVisibilityEditor.prototype.afterRender.call(this);
this.setLock(this.isModelLocked());
},
@@ -619,19 +620,100 @@ define(['jquery', 'backbone', 'underscore', 'gettext', 'js/views/baseview',
},
getRequestData: function() {
- return this.hasChanges() ? {
- publish: 'republish',
- metadata: {
- visible_to_staff_only: this.isLocked() ? true : null
+ if (this.hasChanges()) {
+ return {
+ publish: 'republish',
+ metadata: {
+ visible_to_staff_only: this.isLocked() ? true : null
}
- } : {};
+ };
+ } else {
+ return {};
+ }
+ },
+ });
+
+ ContentVisibilityEditor = AbstractVisibilityEditor.extend({
+ templateName: 'content-visibility-editor',
+ className: 'edit-content-visibility',
+ events: {
+ 'change input[name=content-visibility]': 'toggleUnlockWarning'
+ },
+
+ modelVisibility: function() {
+ if (this.model.get('has_explicit_staff_lock')) {
+ return 'staff_only';
+ } else if (this.model.get('hide_after_due')) {
+ return 'hide_after_due';
+ } else {
+ return 'visible';
+ }
+ },
+
+ afterRender: function () {
+ AbstractVisibilityEditor.prototype.afterRender.call(this);
+ this.setVisibility(this.modelVisibility());
+ this.$('input[name=content-visibility]:checked').change();
+ },
+
+ setVisibility: function(value) {
+ this.$('input[name=content-visibility][value='+value+']').prop('checked', true);
+ },
+
+ currentVisibility: function() {
+ return this.$('input[name=content-visibility]:checked').val();
+ },
+
+ hasChanges: function() {
+ return this.modelVisibility() !== this.currentVisibility();
+ },
+
+ toggleUnlockWarning: function() {
+ var warning = this.$('.staff-lock .tip-warning');
+ if (warning) {
+ var display;
+ if (this.currentVisibility() !== 'staff_only') {
+ display = 'block';
+ } else {
+ display = 'none';
+ }
+ $.each(warning, function(_, element) {
+ element.style.display = display;
+ });
+ }
+ },
+
+ getRequestData: function() {
+ if (this.hasChanges()) {
+ var metadata = {};
+ if (this.currentVisibility() === 'staff_only') {
+ metadata.visible_to_staff_only = true;
+ metadata.hide_after_due = null;
+ }
+ else if (this.currentVisibility() === 'hide_after_due') {
+ metadata.visible_to_staff_only = null;
+ metadata.hide_after_due = true;
+ } else {
+ metadata.visible_to_staff_only = null;
+ metadata.hide_after_due = null;
+ }
+
+ return {
+ publish: 'republish',
+ metadata: metadata
+ };
+ }
+ else {
+ return {};
+ }
},
getContext: function () {
- return {
- hasExplicitStaffLock: this.isModelLocked(),
- ancestorLocked: this.isAncestorLocked()
- };
+ return $.extend(
+ {},
+ AbstractVisibilityEditor.prototype.getContext.call(this),
+ { hide_after_due: this.modelVisibility() === 'hide_after_due'}
+ );
}
});
@@ -748,41 +830,45 @@ define(['jquery', 'backbone', 'underscore', 'gettext', 'js/views/baseview',
},
getEditModal: function (xblockInfo, options) {
- var editors = [];
+
var tabs = [];
-
- if (xblockInfo.isChapter()) {
- editors = [ReleaseDateEditor, StaffLockEditor];
- } else if (xblockInfo.isSequential()) {
- tabs.push({
- name: 'basic',
- displayName: gettext('Basic'),
- editors: [ReleaseDateEditor, GradingEditor, DueDateEditor, StaffLockEditor]
- });
-
- if (options.enable_proctored_exams || options.enable_timed_exams) {
- tabs.push({
- name: 'advanced',
- displayName: gettext('Advanced'),
- editors: [TimedExaminationPreferenceEditor]
- });
- }
-
- if (typeof(xblockInfo.get('is_prereq')) !== 'undefined') {
- tabs.push({
- name: 'access',
- // Translators: This label refers to access to course content.
- displayName: gettext('Access'),
- editors: [AccessEditor]
- });
- }
- } else if (xblockInfo.isVertical()) {
+ var editors = [];
+ if (xblockInfo.isVertical()) {
editors = [StaffLockEditor];
if (xblockInfo.hasVerifiedCheckpoints()) {
editors.push(VerificationAccessEditor);
}
+ } else {
+ tabs = [
+ {
+ name: 'basic',
+ displayName: gettext('Basic'),
+ editors: []
+ },
+ {
+ name: 'advanced',
+ displayName: gettext('Advanced'),
+ editors: []
+ }
+ ];
+ if (xblockInfo.isChapter()) {
+ tabs[0].editors = [ReleaseDateEditor];
+ tabs[1].editors = [StaffLockEditor];
+ } else if (xblockInfo.isSequential()) {
+ tabs[0].editors = [ReleaseDateEditor, GradingEditor, DueDateEditor];
+ tabs[1].editors = [ContentVisibilityEditor];
+
+ if (options.enable_proctored_exams || options.enable_timed_exams) {
+ tabs[1].editors.push(TimedExaminationPreferenceEditor);
+ }
+
+ if (typeof(xblockInfo.get('is_prereq')) !== 'undefined') {
+ tabs[1].editors.push(AccessEditor);
+ }
+ }
}
+
/* globals course */
if (course.get('self_paced')) {
editors = _.without(editors, ReleaseDateEditor, DueDateEditor);
@@ -790,6 +876,7 @@ define(['jquery', 'backbone', 'underscore', 'gettext', 'js/views/baseview',
tab.editors = _.without(tab.editors, ReleaseDateEditor, DueDateEditor);
});
}
+
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 12636be084..4a5ec294c9 100644
--- a/cms/static/sass/elements/_modal-window.scss
+++ b/cms/static/sass/elements/_modal-window.scss
@@ -669,8 +669,13 @@
}
}
- .edit-staff-lock {
+ .edit-staff-lock, .edit-content-visibility {
margin-bottom: $baseline;
+
+ .tip {
+ font-weight: bold;
+ font-size: 12px;
+ }
}
// UI: staff lock section
diff --git a/cms/templates/course_outline.html b/cms/templates/course_outline.html
index 85539600f5..c3753c31d6 100644
--- a/cms/templates/course_outline.html
+++ b/cms/templates/course_outline.html
@@ -1,6 +1,6 @@
<%page expression_filter="h"/>
<%inherit file="base.html" />
-<%def name="online_help_token()"><% return "outline" %>%def>
+<%def name="online_help_token()"><% return "develop_course" %>%def>
<%!
import logging
from util.date_utils import get_default_time_display
@@ -26,7 +26,7 @@ from openedx.core.djangolib.markup import HTML, Text
<%block name="header_extras">
-% for template_name in ['course-outline', 'xblock-string-field-editor', 'basic-modal', 'modal-button', 'course-outline-modal', 'due-date-editor', 'release-date-editor', 'grading-editor', 'publish-editor', 'staff-lock-editor', 'verification-access-editor', 'timed-examination-preference-editor', 'access-editor', 'settings-modal-tabs']:
+% for template_name in ['course-outline', 'xblock-string-field-editor', 'basic-modal', 'modal-button', 'course-outline-modal', 'due-date-editor', 'release-date-editor', 'grading-editor', 'publish-editor', 'staff-lock-editor', 'content-visibility-editor', 'verification-access-editor', 'timed-examination-preference-editor', 'access-editor', 'settings-modal-tabs']:
@@ -42,7 +42,7 @@ from openedx.core.djangolib.markup import HTML, Text
${_("This course was created as a re-run. Some manual configuration is needed.")}
-
${_("No course content is currently visible, and no students are enrolled. Be sure to review and reset all dates, including the Course Start Date; set up the course team; review course updates and other assets for dated material; and seed the discussions and wiki.")}
+
${_("No course content is currently visible, and no learners are enrolled. Be sure to review and reset all dates, including the Course Start Date; set up the course team; review course updates and other assets for dated material; and seed the discussions and wiki.")}
@@ -193,16 +193,29 @@ from openedx.core.djangolib.markup import HTML, Text
${_("Creating your course organization")}
${_("You add sections, subsections, and units directly in the outline.")}
${_("Create a section, then add subsections and units. Open a unit to add course components.")}
+
+
${_("Reorganizing your course")}
${_("Drag sections, subsections, and units to new locations in the outline.")}
+
+
+
${_("Setting release dates and grading policies")}
${_("Select the Configure icon for a section or subsection to set its release date. When you configure a subsection, you can also set the grading policy and due date.")}
-
${_("Changing the content students see")}
-
${_("To publish draft content, select the Publish icon for a section, subsection, or unit.")}
-
${Text(_("To hide content from students, select the Configure icon for a section, subsection, or unit, then select {em_start}Hide from students{em_end}.")).format(em_start=HTML(""), em_end=HTML(" "))}
+
-
-
${_("Learn more about the course outline")}
+
+
${_("Changing the content learners see")}
+
${_("To publish draft content, select the Publish icon for a section, subsection, or unit.")}
+
${Text(_("To make a section, subsection, or unit unavailable to learners, select the Configure icon for that level, then select the appropriate {em_start}Hide{em_end} option. Grades for hidden sections, subsections, and units are not included in grade calculations.")).format(em_start=HTML(""), em_end=HTML(" "))}
+
${Text(_("To hide the content of a subsection from learners after the subsection due date has passed, select the Configure icon for a subsection, then select {em_start}Hide content after due date{em_end}. Grades for the subsection remain included in grade calculations.")).format(em_start=HTML(""), em_end=HTML(" "))}
+
diff --git a/cms/templates/js/content-visibility-editor.underscore b/cms/templates/js/content-visibility-editor.underscore
new file mode 100644
index 0000000000..4c1093461e
--- /dev/null
+++ b/cms/templates/js/content-visibility-editor.underscore
@@ -0,0 +1,34 @@
+
diff --git a/cms/templates/js/course-outline.underscore b/cms/templates/js/course-outline.underscore
index 340f4dd1e4..2b9fbb8d40 100644
--- a/cms/templates/js/course-outline.underscore
+++ b/cms/templates/js/course-outline.underscore
@@ -177,17 +177,6 @@ if (is_proctored_exam) {
<% } %>
-
-
- <% if (!is_proctored_exam && xblockInfo.get('hide_after_due')) { %>
-
- <%- gettext("Exam will remain hidden after due date") %>
- <% } else { %>
-
- <%- gettext("Exam will be visible after due date") %>
- <% } %>
-
-
<% } else if (xblockInfo.get('due_date') || xblockInfo.get('graded')) { %>
@@ -200,6 +189,14 @@ if (is_proctored_exam) {
<% } %>
+
+
+ <% if (xblockInfo.get('hide_after_due')) { %>
+
+ <%- gettext("Subsection is hidden after due date") %>
+ <% } %>
+
+
<% } %>
<% if (statusMessage) { %>
diff --git a/cms/templates/js/publish-xblock.underscore b/cms/templates/js/publish-xblock.underscore
index 738e635985..44b0f8641e 100644
--- a/cms/templates/js/publish-xblock.underscore
+++ b/cms/templates/js/publish-xblock.underscore
@@ -81,7 +81,7 @@ var visibleToStaffOnly = visibilityState === 'staff_only';
<% } %>
<% } else { %>
-
<%- gettext("Staff and Students") %>
+
<%- gettext("Staff and Learners") %>
<% } %>
<% if (hasContentGroupComponents) { %>
@@ -97,10 +97,13 @@ var visibleToStaffOnly = visibilityState === 'staff_only';
<% } else { %>
<% } %>
- <%- gettext('Hide from students') %>
+ <%- gettext('Hide from learners') %>
+
+ <%- gettext("Note: Do not hide graded assignments after they have been released.") %>
+
diff --git a/cms/templates/js/release-date-editor.underscore b/cms/templates/js/release-date-editor.underscore
index 6d558d34b0..cc62f36bf5 100644
--- a/cms/templates/js/release-date-editor.underscore
+++ b/cms/templates/js/release-date-editor.underscore
@@ -1,14 +1,14 @@
-
<%= gettext('Release Date and Time') %>
+
<%- gettext('Release Date and Time') %>