Commit 35ae67b5 by Eric Fischer Committed by GitHub

New CMS visibility settings (#12940)

TNL-4906 Subsections now use radio buttons, to allow for "hide after due" as a
visibility option. Also, all tabs have been consolidated to "Basic" and
"Advanced", and visibility options have moved there.

Documentation links are updated to assist course authors with the new
visibility options. Tests have also been updated, and the changes suggested
in TNL-4951 are included.
parent f3a46bfb
...@@ -982,6 +982,11 @@ def create_xblock_info(xblock, data=None, metadata=None, include_ancestor_info=F ...@@ -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), "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 # update xblock_info with special exam information if the feature flag is enabled
if settings.FEATURES.get('ENABLE_SPECIAL_EXAMS'): if settings.FEATURES.get('ENABLE_SPECIAL_EXAMS'):
if xblock.category == 'course': if xblock.category == 'course':
...@@ -997,7 +1002,6 @@ def create_xblock_info(xblock, data=None, metadata=None, include_ancestor_info=F ...@@ -997,7 +1002,6 @@ def create_xblock_info(xblock, data=None, metadata=None, include_ancestor_info=F
"is_time_limited": xblock.is_time_limited, "is_time_limited": xblock.is_time_limited,
"exam_review_rules": xblock.exam_review_rules, "exam_review_rules": xblock.exam_review_rules,
"default_time_limit_minutes": xblock.default_time_limit_minutes, "default_time_limit_minutes": xblock.default_time_limit_minutes,
"hide_after_due": xblock.hide_after_due,
}) })
# Update with gating info # Update with gating info
......
...@@ -434,7 +434,7 @@ define(["jquery", "underscore", "underscore.string", "edx-ui-toolkit/js/utils/sp ...@@ -434,7 +434,7 @@ define(["jquery", "underscore", "underscore.string", "edx-ui-toolkit/js/utils/sp
expect(visibilityCopy).toContain('Staff Only'); expect(visibilityCopy).toContain('Staff Only');
expect(containerPage.$(bitPublishingCss)).toHaveClass(staffOnlyClass); expect(containerPage.$(bitPublishingCss)).toHaveClass(staffOnlyClass);
} else { } else {
expect(visibilityCopy).toBe('Staff and Students'); expect(visibilityCopy).toBe('Staff and Learners');
expect(containerPage.$(bitPublishingCss)).not.toHaveClass(staffOnlyClass); expect(containerPage.$(bitPublishingCss)).not.toHaveClass(staffOnlyClass);
verifyExplicitStaffOnly(false); verifyExplicitStaffOnly(false);
verifyImplicitStaffOnly(false); verifyImplicitStaffOnly(false);
......
...@@ -7,10 +7,10 @@ define(["jquery", "edx-ui-toolkit/js/utils/spec-helpers/ajax-helpers", "common/j ...@@ -7,10 +7,10 @@ define(["jquery", "edx-ui-toolkit/js/utils/spec-helpers/ajax-helpers", "common/j
describe("CourseOutlinePage", function() { describe("CourseOutlinePage", function() {
var createCourseOutlinePage, displayNameInput, model, outlinePage, requests, var createCourseOutlinePage, displayNameInput, model, outlinePage, requests,
getItemsOfType, getItemHeaders, verifyItemsExpanded, expandItemsAndVerifyState, getItemsOfType, getItemHeaders, verifyItemsExpanded, expandItemsAndVerifyState,
collapseItemsAndVerifyState, createMockCourseJSON, createMockSectionJSON, createMockSubsectionJSON, collapseItemsAndVerifyState, selectBasicSettings, selectAdvancedSettings, createMockCourseJSON,
verifyTypePublishable, mockCourseJSON, mockEmptyCourseJSON, mockSingleSectionCourseJSON, createMockSectionJSON, createMockSubsectionJSON, verifyTypePublishable, mockCourseJSON,
createMockVerticalJSON, createMockIndexJSON, mockCourseEntranceExamJSON, mockEmptyCourseJSON, mockSingleSectionCourseJSON, createMockVerticalJSON, createMockIndexJSON,
mockOutlinePage = readFixtures('mock/mock-course-outline-page.underscore'), mockCourseEntranceExamJSON, mockOutlinePage = readFixtures('mock/mock-course-outline-page.underscore'),
mockRerunNotification = readFixtures('mock/mock-course-rerun-notification.underscore'); mockRerunNotification = readFixtures('mock/mock-course-rerun-notification.underscore');
createMockCourseJSON = function(options, children) { createMockCourseJSON = function(options, children) {
...@@ -137,6 +137,14 @@ define(["jquery", "edx-ui-toolkit/js/utils/spec-helpers/ajax-helpers", "common/j ...@@ -137,6 +137,14 @@ define(["jquery", "edx-ui-toolkit/js/utils/spec-helpers/ajax-helpers", "common/j
verifyItemsExpanded(type, false); 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) { createCourseOutlinePage = function(test, courseJSON, createOnly) {
requests = AjaxHelpers.requests(test); requests = AjaxHelpers.requests(test);
model = new XBlockOutlineInfo(courseJSON, { parse: true }); model = new XBlockOutlineInfo(courseJSON, { parse: true });
...@@ -230,8 +238,8 @@ define(["jquery", "edx-ui-toolkit/js/utils/spec-helpers/ajax-helpers", "common/j ...@@ -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', 'course-outline', 'xblock-string-field-editor', 'modal-button',
'basic-modal', 'course-outline-modal', 'release-date-editor', 'basic-modal', 'course-outline-modal', 'release-date-editor',
'due-date-editor', 'grading-editor', 'publish-editor', 'due-date-editor', 'grading-editor', 'publish-editor',
'staff-lock-editor', 'settings-modal-tabs', 'timed-examination-preference-editor', 'staff-lock-editor','content-visibility-editor', 'settings-modal-tabs',
'access-editor' 'timed-examination-preference-editor', 'access-editor'
]); ]);
appendSetFixtures(mockOutlinePage); appendSetFixtures(mockOutlinePage);
mockCourseJSON = createMockCourseJSON({}, [ mockCourseJSON = createMockCourseJSON({}, [
...@@ -535,8 +543,10 @@ define(["jquery", "edx-ui-toolkit/js/utils/spec-helpers/ajax-helpers", "common/j ...@@ -535,8 +543,10 @@ define(["jquery", "edx-ui-toolkit/js/utils/spec-helpers/ajax-helpers", "common/j
expect($("due_date")).not.toExist(); expect($("due_date")).not.toExist();
expect($("grading_format")).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(); expect($("#staff_lock")).toExist();
selectBasicSettings();
$(".wrapper-modal-window .action-save").click(); $(".wrapper-modal-window .action-save").click();
AjaxHelpers.expectJsonRequest(requests, 'POST', '/xblock/mock-section', { AjaxHelpers.expectJsonRequest(requests, 'POST', '/xblock/mock-section', {
"metadata":{ "metadata":{
...@@ -605,43 +615,33 @@ define(["jquery", "edx-ui-toolkit/js/utils/spec-helpers/ajax-helpers", "common/j ...@@ -605,43 +615,33 @@ define(["jquery", "edx-ui-toolkit/js/utils/spec-helpers/ajax-helpers", "common/j
}); });
describe("Subsection", function() { describe("Subsection", function() {
var getDisplayNameWrapper, setEditModalValues, mockServerValuesJson, var getDisplayNameWrapper, setEditModalValues, setContentVisibility, mockServerValuesJson,
selectDisableSpecialExams, selectBasicSettings, selectAdvancedSettings, selectDisableSpecialExams, selectTimedExam, selectProctoredExam, selectPracticeExam,
selectAccessSettings, selectTimedExam, selectProctoredExam, selectPracticeExam,
selectPrerequisite, selectLastPrerequisiteSubsection, checkOptionFieldVisibility; selectPrerequisite, selectLastPrerequisiteSubsection, checkOptionFieldVisibility;
getDisplayNameWrapper = function() { getDisplayNameWrapper = function() {
return getItemHeaders('subsection').find('.wrapper-xblock-field'); 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); $("#start_date").val(start_date);
$("#due_date").val(due_date); $("#due_date").val(due_date);
$("#grading_type").val(grading_type); $("#grading_type").val(grading_type);
$("#staff_lock").prop('checked', is_locked);
};
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() { setContentVisibility = function (visibility) {
this.$(".modal-section .settings-tab-button[data-tab='advanced']").click(); $('input[name=content-visibility][value='+visibility+']').prop('checked', true);
}; };
selectAccessSettings = function() { selectDisableSpecialExams = function() {
this.$(".modal-section .settings-tab-button[data-tab='access']").click(); this.$("input.no_special_exam").prop('checked', true).trigger('change');
}; };
selectTimedExam = function(time_limit, hide_after_due) { selectTimedExam = function(time_limit) {
this.$("input.timed_exam").prop('checked', true).trigger('change'); this.$("input.timed_exam").prop('checked', true).trigger('change');
this.$(".field-time-limit input").val(time_limit); this.$(".field-time-limit input").val(time_limit);
this.$(".field-time-limit input").trigger('focusout'); 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) { selectProctoredExam = function(time_limit) {
...@@ -666,10 +666,9 @@ define(["jquery", "edx-ui-toolkit/js/utils/spec-helpers/ajax-helpers", "common/j ...@@ -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 // 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-time-limit').is(':visible')).toBe(time_limit);
expect($('.field-exam-review-rules').is(':visible')).toBe(review_rules); 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. // 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 ...@@ -767,7 +766,6 @@ define(["jquery", "edx-ui-toolkit/js/utils/spec-helpers/ajax-helpers", "common/j
selectBasicSettings(); selectBasicSettings();
expect($('.modal-section .settings-tab-button[data-tab="basic"]')).toHaveClass('active'); 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="advanced"]')).not.toHaveClass('active');
expect($('.modal-section .settings-tab-button[data-tab="access"]')).not.toHaveClass('active');
}); });
it('can show advanced settings', function() { it('can show advanced settings', function() {
...@@ -776,20 +774,11 @@ define(["jquery", "edx-ui-toolkit/js/utils/spec-helpers/ajax-helpers", "common/j ...@@ -776,20 +774,11 @@ define(["jquery", "edx-ui-toolkit/js/utils/spec-helpers/ajax-helpers", "common/j
selectAdvancedSettings(); selectAdvancedSettings();
expect($('.modal-section .settings-tab-button[data-tab="basic"]')).not.toHaveClass('active'); 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="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() { 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.is_prereq;
delete mockSubsectionJSON.prereqs; delete mockSubsectionJSON.prereqs;
delete mockSubsectionJSON.prereq; delete mockSubsectionJSON.prereq;
...@@ -801,7 +790,7 @@ define(["jquery", "edx-ui-toolkit/js/utils/spec-helpers/ajax-helpers", "common/j ...@@ -801,7 +790,7 @@ define(["jquery", "edx-ui-toolkit/js/utils/spec-helpers/ajax-helpers", "common/j
createMockSectionJSON({}, [mockSubsectionJSON]) createMockSectionJSON({}, [mockSubsectionJSON])
]); ]);
createCourseOutlinePage(this, mockCourseJSON, false); createCourseOutlinePage(this, mockCourseJSON, false);
outlinePage.$('.outline-subsection .configure-button').click(); outlinePage.$('.outline-unit .configure-button').click();
expect($(".settings-tabs-header").length).toBe(0); expect($(".settings-tabs-header").length).toBe(0);
}); });
...@@ -818,7 +807,7 @@ define(["jquery", "edx-ui-toolkit/js/utils/spec-helpers/ajax-helpers", "common/j ...@@ -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($(".edit-settings-release").length).toBe(0);
expect($(".grading-due-date").length).toBe(0); expect($(".grading-due-date").length).toBe(0);
expect($(".edit-settings-grading").length).toBe(1); 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() { it('can select valid time', function() {
...@@ -847,16 +836,16 @@ define(["jquery", "edx-ui-toolkit/js/utils/spec-helpers/ajax-helpers", "common/j ...@@ -847,16 +836,16 @@ define(["jquery", "edx-ui-toolkit/js/utils/spec-helpers/ajax-helpers", "common/j
it('can be edited', function() { it('can be edited', function() {
createCourseOutlinePage(this, mockCourseJSON, false); createCourseOutlinePage(this, mockCourseJSON, false);
outlinePage.$('.outline-subsection .configure-button').click(); outlinePage.$('.outline-subsection .configure-button').click();
setEditModalValues("7/9/2014", "7/10/2014", "Lab", true); setEditModalValues("7/9/2014", "7/10/2014", "Lab");
selectAdvancedSettings(); selectAdvancedSettings();
selectTimedExam("02:30", true); selectTimedExam("02:30");
$(".wrapper-modal-window .action-save").click(); $(".wrapper-modal-window .action-save").click();
AjaxHelpers.expectJsonRequest(requests, 'POST', '/xblock/mock-subsection', { AjaxHelpers.expectJsonRequest(requests, 'POST', '/xblock/mock-subsection', {
"graderType":"Lab", "graderType":"Lab",
"publish": "republish", "publish": "republish",
"isPrereq": false, "isPrereq": false,
"metadata":{ "metadata":{
"visible_to_staff_only": true, "visible_to_staff_only": null,
"start":"2014-07-09T00:00:00.000Z", "start":"2014-07-09T00:00:00.000Z",
"due":"2014-07-10T00:00:00.000Z", "due":"2014-07-10T00:00:00.000Z",
"exam_review_rules": "", "exam_review_rules": "",
...@@ -892,21 +881,21 @@ define(["jquery", "edx-ui-toolkit/js/utils/spec-helpers/ajax-helpers", "common/j ...@@ -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($("#start_date").val()).toBe('7/9/2014');
expect($("#due_date").val()).toBe('7/10/2014'); expect($("#due_date").val()).toBe('7/10/2014');
expect($("#grading_type").val()).toBe('Lab'); 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.timed_exam").is(":checked")).toBe(true);
expect($("input.proctored_exam").is(":checked")).toBe(false); expect($("input.proctored_exam").is(":checked")).toBe(false);
expect($("input.no_special_exam").is(":checked")).toBe(false); expect($("input.no_special_exam").is(":checked")).toBe(false);
expect($("input.practice_exam").is(":checked")).toBe(false); expect($("input.practice_exam").is(":checked")).toBe(false);
expect($(".field-time-limit input").val()).toBe("02:30"); 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() { it('can hide time limit and hide after due fields when the None radio box is selected', function() {
createCourseOutlinePage(this, mockCourseJSON, false); createCourseOutlinePage(this, mockCourseJSON, false);
outlinePage.$('.outline-subsection .configure-button').click(); outlinePage.$('.outline-subsection .configure-button').click();
setEditModalValues("7/9/2014", "7/10/2014", "Lab", true); setEditModalValues("7/9/2014", "7/10/2014", "Lab");
selectAdvancedSettings(); selectAdvancedSettings();
selectDisableSpecialExams(); selectDisableSpecialExams();
setContentVisibility("staff_only");
// all additional options should be hidden // all additional options should be hidden
expect($('.exam-options').is(':hidden')).toBe(true); expect($('.exam-options').is(':hidden')).toBe(true);
...@@ -915,12 +904,13 @@ define(["jquery", "edx-ui-toolkit/js/utils/spec-helpers/ajax-helpers", "common/j ...@@ -915,12 +904,13 @@ define(["jquery", "edx-ui-toolkit/js/utils/spec-helpers/ajax-helpers", "common/j
it('can select the practice exam', function() { it('can select the practice exam', function() {
createCourseOutlinePage(this, mockCourseJSON, false); createCourseOutlinePage(this, mockCourseJSON, false);
outlinePage.$('.outline-subsection .configure-button').click(); outlinePage.$('.outline-subsection .configure-button').click();
setEditModalValues("7/9/2014", "7/10/2014", "Lab", true); setEditModalValues("7/9/2014", "7/10/2014", "Lab");
selectAdvancedSettings(); selectAdvancedSettings();
selectPracticeExam("00:30"); selectPracticeExam("00:30");
setContentVisibility("staff_only");
// time limit should be visible, review rules and hide after due should be hidden // time limit should be visible, review rules should be hidden
checkOptionFieldVisibility(true, false, false); checkOptionFieldVisibility(true, false);
$(".wrapper-modal-window .action-save").click(); $(".wrapper-modal-window .action-save").click();
}); });
...@@ -928,12 +918,12 @@ define(["jquery", "edx-ui-toolkit/js/utils/spec-helpers/ajax-helpers", "common/j ...@@ -928,12 +918,12 @@ define(["jquery", "edx-ui-toolkit/js/utils/spec-helpers/ajax-helpers", "common/j
it('can select the timed exam', function() { it('can select the timed exam', function() {
createCourseOutlinePage(this, mockCourseJSON, false); createCourseOutlinePage(this, mockCourseJSON, false);
outlinePage.$('.outline-subsection .configure-button').click(); outlinePage.$('.outline-subsection .configure-button').click();
setEditModalValues("7/9/2014", "7/10/2014", "Lab", true); setEditModalValues("7/9/2014", "7/10/2014", "Lab");
selectAdvancedSettings(); selectAdvancedSettings();
selectTimedExam("00:30"); selectTimedExam("00:30");
// time limit and hide after due should be visible, review rules should be hidden // time limit should be visible, review rules should be hidden
checkOptionFieldVisibility(true, false, true); checkOptionFieldVisibility(true, false);
$(".wrapper-modal-window .action-save").click(); $(".wrapper-modal-window .action-save").click();
}); });
...@@ -941,12 +931,13 @@ define(["jquery", "edx-ui-toolkit/js/utils/spec-helpers/ajax-helpers", "common/j ...@@ -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() { it('can select the Proctored exam option', function() {
createCourseOutlinePage(this, mockCourseJSON, false); createCourseOutlinePage(this, mockCourseJSON, false);
outlinePage.$('.outline-subsection .configure-button').click(); outlinePage.$('.outline-subsection .configure-button').click();
setEditModalValues("7/9/2014", "7/10/2014", "Lab", true); setEditModalValues("7/9/2014", "7/10/2014", "Lab");
selectAdvancedSettings(); selectAdvancedSettings();
selectProctoredExam("00:30"); selectProctoredExam("00:30");
setContentVisibility("staff_only");
// time limit and review rules should be visible, hide after due should be hidden // time limit and review rules should be visible
checkOptionFieldVisibility(true, true, false); checkOptionFieldVisibility(true, true);
$(".wrapper-modal-window .action-save").click(); $(".wrapper-modal-window .action-save").click();
...@@ -955,9 +946,10 @@ define(["jquery", "edx-ui-toolkit/js/utils/spec-helpers/ajax-helpers", "common/j ...@@ -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() { it('entering invalid time format uses default value of 30 minutes.', function() {
createCourseOutlinePage(this, mockCourseJSON, false); createCourseOutlinePage(this, mockCourseJSON, false);
outlinePage.$('.outline-subsection .configure-button').click(); outlinePage.$('.outline-subsection .configure-button').click();
setEditModalValues("7/9/2014", "7/10/2014", "Lab", true); setEditModalValues("7/9/2014", "7/10/2014", "Lab");
selectAdvancedSettings(); selectAdvancedSettings();
selectProctoredExam("abcd"); selectProctoredExam("abcd");
setContentVisibility("staff_only");
// time limit field should be visible and have the correct value // time limit field should be visible and have the correct value
expect($('.field-time-limit').is(':visible')).toBe(true); 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 ...@@ -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.no_special_exam").is(":checked")).toBe(true);
expect($("input.practice_exam").is(":checked")).toBe(false); expect($("input.practice_exam").is(":checked")).toBe(false);
expect($(".field-time-limit input").val()).toBe("02:30"); 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() { 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 ...@@ -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.no_special_exam").is(":checked")).toBe(false);
expect($("input.practice_exam").is(":checked")).toBe(false); expect($("input.practice_exam").is(":checked")).toBe(false);
expect($(".field-time-limit input").val()).toBe("00:10"); 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() { 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 ...@@ -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.no_special_exam").is(":checked")).toBe(false);
expect($("input.practice_exam").is(":checked")).toBe(true); expect($("input.practice_exam").is(":checked")).toBe(true);
expect($(".field-time-limit input").val()).toBe("02:30"); 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() { 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 ...@@ -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.no_special_exam").is(":checked")).toBe(false);
expect($("input.practice_exam").is(":checked")).toBe(false); expect($("input.practice_exam").is(":checked")).toBe(false);
expect($(".field-time-limit input").val()).toBe("02:30"); 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() { 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 ...@@ -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.timed_exam").is(":checked")).toBe(true);
expect($("input.no_special_exam").is(":checked")).toBe(false); expect($("input.no_special_exam").is(":checked")).toBe(false);
expect($(".field-time-limit input").val()).toBe("02:30"); expect($(".field-time-limit input").val()).toBe("02:30");
expect($('.field-hide-after-due input').is(":checked")).toBe(true);
}); });
it('can select prerequisite', function() { it('can select prerequisite', function() {
...@@ -1300,7 +1287,7 @@ define(["jquery", "edx-ui-toolkit/js/utils/spec-helpers/ajax-helpers", "common/j ...@@ -1300,7 +1287,7 @@ define(["jquery", "edx-ui-toolkit/js/utils/spec-helpers/ajax-helpers", "common/j
]); ]);
createCourseOutlinePage(this, mockCourseWithPreqsJSON, false); createCourseOutlinePage(this, mockCourseWithPreqsJSON, false);
outlinePage.$('.outline-subsection .configure-button').click(); outlinePage.$('.outline-subsection .configure-button').click();
selectAccessSettings(); selectAdvancedSettings();
selectLastPrerequisiteSubsection(''); selectLastPrerequisiteSubsection('');
expect($('#prereq_min_score_error').css('display')).toBe('none'); expect($('#prereq_min_score_error').css('display')).toBe('none');
selectLastPrerequisiteSubsection('80'); selectLastPrerequisiteSubsection('80');
...@@ -1314,7 +1301,8 @@ define(["jquery", "edx-ui-toolkit/js/utils/spec-helpers/ajax-helpers", "common/j ...@@ -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() { it('release date, due date, grading type, and staff lock can be cleared.', function() {
createCourseOutlinePage(this, mockCourseJSON, false); createCourseOutlinePage(this, mockCourseJSON, false);
outlinePage.$('.outline-item .outline-subsection .configure-button').click(); 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(); $(".wrapper-modal-window .action-save").click();
// This is the response for the change operation. // 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 ...@@ -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($("#start_date").val()).toBe('7/9/2014');
expect($("#due_date").val()).toBe('7/10/2014'); expect($("#due_date").val()).toBe('7/10/2014');
expect($("#grading_type").val()).toBe('Lab'); 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 .scheduled-date-input .action-clear").click();
$(".wrapper-modal-window .due-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 ...@@ -1347,7 +1335,7 @@ define(["jquery", "edx-ui-toolkit/js/utils/spec-helpers/ajax-helpers", "common/j
expect($("#due_date").val()).toBe(''); expect($("#due_date").val()).toBe('');
$("#grading_type").val('notgraded'); $("#grading_type").val('notgraded');
$("#staff_lock").prop('checked', false); setContentVisibility("visible");
$(".wrapper-modal-window .action-save").click(); $(".wrapper-modal-window .action-save").click();
......
...@@ -52,7 +52,10 @@ function($, date, TriggerChangeEventOnEnter) { ...@@ -52,7 +52,10 @@ function($, date, TriggerChangeEventOnEnter) {
// given a pair of inputs (datepicker and timepicker), return a JS Date // given a pair of inputs (datepicker and timepicker), return a JS Date
// object that corresponds to the datetime.js that they represent. Assume // object that corresponds to the datetime.js that they represent. Assume
// UTC timezone, NOT the timezone of the user's browser. // 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) { if (timepickerInput.length > 0) {
time = $(timepickerInput).timepicker("getTime"); time = $(timepickerInput).timepicker("getTime");
} }
......
...@@ -7,14 +7,15 @@ ...@@ -7,14 +7,15 @@
*/ */
define(['jquery', 'backbone', 'underscore', 'gettext', 'js/views/baseview', define(['jquery', 'backbone', 'underscore', 'gettext', 'js/views/baseview',
'js/views/modals/base_modal', 'date', 'js/views/utils/xblock_utils', '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( ], function(
$, Backbone, _, gettext, BaseView, BaseModal, date, XBlockViewUtils, DateUtils $, Backbone, _, gettext, BaseView, BaseModal, date, XBlockViewUtils, DateUtils, HtmlUtils, StringUtils
) { ) {
'use strict'; 'use strict';
var CourseOutlineXBlockModal, SettingsXBlockModal, PublishXBlockModal, AbstractEditor, BaseDateEditor, var CourseOutlineXBlockModal, SettingsXBlockModal, PublishXBlockModal, AbstractEditor, BaseDateEditor,
ReleaseDateEditor, DueDateEditor, GradingEditor, PublishEditor, StaffLockEditor, ReleaseDateEditor, DueDateEditor, GradingEditor, PublishEditor, AbstractVisibilityEditor, StaffLockEditor,
VerificationAccessEditor, TimedExaminationPreferenceEditor, AccessEditor; ContentVisibilityEditor, VerificationAccessEditor, TimedExaminationPreferenceEditor, AccessEditor;
CourseOutlineXBlockModal = BaseModal.extend({ CourseOutlineXBlockModal = BaseModal.extend({
events : _.extend({}, BaseModal.prototype.events, { events : _.extend({}, BaseModal.prototype.events, {
...@@ -105,9 +106,9 @@ define(['jquery', 'backbone', 'underscore', 'gettext', 'js/views/baseview', ...@@ -105,9 +106,9 @@ define(['jquery', 'backbone', 'underscore', 'gettext', 'js/views/baseview',
SettingsXBlockModal = CourseOutlineXBlockModal.extend({ SettingsXBlockModal = CourseOutlineXBlockModal.extend({
getTitle: function () { getTitle: function () {
return interpolate( return StringUtils.interpolate(
gettext('%(display_name)s Settings'), gettext('{display_name} Settings'),
{ display_name: this.model.get('display_name') }, true { display_name: this.model.get('display_name') }
); );
}, },
...@@ -115,9 +116,10 @@ define(['jquery', 'backbone', 'underscore', 'gettext', 'js/views/baseview', ...@@ -115,9 +116,10 @@ define(['jquery', 'backbone', 'underscore', 'gettext', 'js/views/baseview',
var message = ''; var message = '';
var tabs = this.options.tabs; var tabs = this.options.tabs;
if (!tabs || tabs.length < 2) { if (!tabs || tabs.length < 2) {
message = interpolate( message = StringUtils.interpolate(
gettext('Change the settings for %(display_name)s'), gettext('Change the settings for {display_name}'),
{ display_name: this.model.get('display_name') }, true); { display_name: this.model.get('display_name') }
);
} }
return message; return message;
}, },
...@@ -127,7 +129,7 @@ define(['jquery', 'backbone', 'underscore', 'gettext', 'js/views/baseview', ...@@ -127,7 +129,7 @@ define(['jquery', 'backbone', 'underscore', 'gettext', 'js/views/baseview',
if (tabs && tabs.length > 0) { if (tabs && tabs.length > 0) {
if (tabs.length > 1) { if (tabs.length > 1) {
var tabsTemplate = this.loadTemplate('settings-modal-tabs'); 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) { _.each(this.options.tabs, function(tab) {
this.options.editors.push.apply( this.options.editors.push.apply(
this.options.editors, this.options.editors,
...@@ -196,16 +198,16 @@ define(['jquery', 'backbone', 'underscore', 'gettext', 'js/views/baseview', ...@@ -196,16 +198,16 @@ define(['jquery', 'backbone', 'underscore', 'gettext', 'js/views/baseview',
}, },
getTitle: function () { getTitle: function () {
return interpolate( return StringUtils.interpolate(
gettext('Publish %(display_name)s'), gettext('Publish {display_name}'),
{ display_name: this.model.get('display_name') }, true { display_name: this.model.get('display_name') }
); );
}, },
getIntroductionMessage: function () { getIntroductionMessage: function () {
return interpolate( return StringUtils.interpolate(
gettext('Publish all unpublished changes for this %(item)s?'), gettext('Publish all unpublished changes for this {item}?'),
{ item: this.options.xblockType }, true { item: this.options.xblockType }
); );
}, },
...@@ -233,7 +235,7 @@ define(['jquery', 'backbone', 'underscore', 'gettext', 'js/views/baseview', ...@@ -233,7 +235,7 @@ define(['jquery', 'backbone', 'underscore', 'gettext', 'js/views/baseview',
enable_timed_exam: this.options.enable_timed_exams enable_timed_exam: this.options.enable_timed_exams
}, this.getContext())); }, this.getContext()));
this.$el.html(html); HtmlUtils.setHtml(this.$el, HtmlUtils.HTML(html));
this.parentElement.append(this.$el); this.parentElement.append(this.$el);
}, },
...@@ -343,7 +345,7 @@ define(['jquery', 'backbone', 'underscore', 'gettext', 'js/views/baseview', ...@@ -343,7 +345,7 @@ define(['jquery', 'backbone', 'underscore', 'gettext', 'js/views/baseview',
this.$('.exam-options').hide(); this.$('.exam-options').hide();
this.$('.field-time-limit input').val('00:00'); this.$('.field-time-limit input').val('00:00');
}, },
selectSpecialExam: function (showRulesField, showHideAfterDueField) { selectSpecialExam: function (showRulesField) {
this.$('.exam-options').show(); this.$('.exam-options').show();
this.$('.field-time-limit').show(); this.$('.field-time-limit').show();
if (!this.isValidTimeLimit(this.$('.field-time-limit input').val())) { if (!this.isValidTimeLimit(this.$('.field-time-limit input').val())) {
...@@ -355,24 +357,18 @@ define(['jquery', 'backbone', 'underscore', 'gettext', 'js/views/baseview', ...@@ -355,24 +357,18 @@ define(['jquery', 'backbone', 'underscore', 'gettext', 'js/views/baseview',
else { else {
this.$('.field-exam-review-rules').hide(); this.$('.field-exam-review-rules').hide();
} }
if (showHideAfterDueField) {
this.$('.field-hide-after-due').show();
}
else {
this.$('.field-hide-after-due').hide();
}
}, },
setTimedExam: function (event) { setTimedExam: function (event) {
event.preventDefault(); event.preventDefault();
this.selectSpecialExam(false, true); this.selectSpecialExam(false);
}, },
setPracticeExam: function (event) { setPracticeExam: function (event) {
event.preventDefault(); event.preventDefault();
this.selectSpecialExam(false, false); this.selectSpecialExam(false);
}, },
setProctoredExam: function (event) { setProctoredExam: function (event) {
event.preventDefault(); event.preventDefault();
this.selectSpecialExam(true, false); this.selectSpecialExam(true);
}, },
timeLimitFocusout: function(event) { timeLimitFocusout: function(event) {
event.preventDefault(); event.preventDefault();
...@@ -395,12 +391,10 @@ define(['jquery', 'backbone', 'underscore', 'gettext', 'js/views/baseview', ...@@ -395,12 +391,10 @@ define(['jquery', 'backbone', 'underscore', 'gettext', 'js/views/baseview',
this.setExamTime(this.model.get('default_time_limit_minutes')); this.setExamTime(this.model.get('default_time_limit_minutes'));
this.setReviewRules(this.model.get('exam_review_rules')); 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) { setExamType: function(is_time_limited, is_proctored_exam, is_practice_exam) {
this.$('.field-time-limit').hide(); this.$('.field-time-limit').hide();
this.$('.field-exam-review-rules').hide(); this.$('.field-exam-review-rules').hide();
this.$('.field-hide-after-due').hide();
if (!is_time_limited) { if (!is_time_limited) {
this.$('input.no_special_exam').prop('checked', true); this.$('input.no_special_exam').prop('checked', true);
...@@ -421,7 +415,6 @@ define(['jquery', 'backbone', 'underscore', 'gettext', 'js/views/baseview', ...@@ -421,7 +415,6 @@ define(['jquery', 'backbone', 'underscore', 'gettext', 'js/views/baseview',
// if the subsection is not time limited, then // if the subsection is not time limited, then
// here we rightfully assume that it just a timed exam // here we rightfully assume that it just a timed exam
this.$('input.timed_exam').prop('checked', true); this.$('input.timed_exam').prop('checked', true);
this.$('.field-hide-after-due').show();
} }
}, },
setExamTime: function(value) { setExamTime: function(value) {
...@@ -431,9 +424,6 @@ define(['jquery', 'backbone', 'underscore', 'gettext', 'js/views/baseview', ...@@ -431,9 +424,6 @@ define(['jquery', 'backbone', 'underscore', 'gettext', 'js/views/baseview',
setReviewRules: function (value) { setReviewRules: function (value) {
this.$('.field-exam-review-rules textarea').val(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) { isValidTimeLimit: function(time_limit) {
var pattern = new RegExp('^\\d{1,2}:[0-5][0-9]$'); var pattern = new RegExp('^\\d{1,2}:[0-5][0-9]$');
return pattern.test(time_limit) && time_limit !== "00:00"; return pattern.test(time_limit) && time_limit !== "00:00";
...@@ -459,7 +449,6 @@ define(['jquery', 'backbone', 'underscore', 'gettext', 'js/views/baseview', ...@@ -459,7 +449,6 @@ define(['jquery', 'backbone', 'underscore', 'gettext', 'js/views/baseview',
var is_proctored_exam; var is_proctored_exam;
var time_limit = this.getExamTimeLimit(); var time_limit = this.getExamTimeLimit();
var exam_review_rules = this.$('.field-exam-review-rules textarea').val(); 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')){ if (this.$('input.no_special_exam').is(':checked')){
is_time_limited = false; is_time_limited = false;
...@@ -484,7 +473,6 @@ define(['jquery', 'backbone', 'underscore', 'gettext', 'js/views/baseview', ...@@ -484,7 +473,6 @@ define(['jquery', 'backbone', 'underscore', 'gettext', 'js/views/baseview',
'is_practice_exam': is_practice_exam, 'is_practice_exam': is_practice_exam,
'is_time_limited': is_time_limited, 'is_time_limited': is_time_limited,
'exam_review_rules': exam_review_rules, 'exam_review_rules': exam_review_rules,
'hide_after_due': hide_after_due,
// We have to use the legacy field name // We have to use the legacy field name
// as the Ajax handler directly populates // as the Ajax handler directly populates
// the xBlocks fields. We will have to // the xBlocks fields. We will have to
...@@ -590,9 +578,11 @@ define(['jquery', 'backbone', 'underscore', 'gettext', 'js/views/baseview', ...@@ -590,9 +578,11 @@ define(['jquery', 'backbone', 'underscore', 'gettext', 'js/views/baseview',
} }
}); });
StaffLockEditor = AbstractEditor.extend({ AbstractVisibilityEditor = AbstractEditor.extend({
templateName: 'staff-lock-editor', afterRender: function () {
className: 'edit-staff-lock', AbstractEditor.prototype.afterRender.call(this);
},
isModelLocked: function() { isModelLocked: function() {
return this.model.get('has_explicit_staff_lock'); return this.model.get('has_explicit_staff_lock');
}, },
...@@ -601,8 +591,19 @@ define(['jquery', 'backbone', 'underscore', 'gettext', 'js/views/baseview', ...@@ -601,8 +591,19 @@ define(['jquery', 'backbone', 'underscore', 'gettext', 'js/views/baseview',
return this.model.get('ancestor_has_staff_lock'); 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 () { afterRender: function () {
AbstractEditor.prototype.afterRender.call(this); AbstractVisibilityEditor.prototype.afterRender.call(this);
this.setLock(this.isModelLocked()); this.setLock(this.isModelLocked());
}, },
...@@ -619,20 +620,101 @@ define(['jquery', 'backbone', 'underscore', 'gettext', 'js/views/baseview', ...@@ -619,20 +620,101 @@ define(['jquery', 'backbone', 'underscore', 'gettext', 'js/views/baseview',
}, },
getRequestData: function() { getRequestData: function() {
return this.hasChanges() ? { if (this.hasChanges()) {
return {
publish: 'republish', publish: 'republish',
metadata: { metadata: {
visible_to_staff_only: this.isLocked() ? true : null 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;
}
getContext: function () {
return { return {
hasExplicitStaffLock: this.isModelLocked(), publish: 'republish',
ancestorLocked: this.isAncestorLocked() metadata: metadata
}; };
} }
else {
return {};
}
},
getContext: function () {
return $.extend(
{},
AbstractVisibilityEditor.prototype.getContext.call(this),
{ hide_after_due: this.modelVisibility() === 'hide_after_due'}
);
}
}); });
VerificationAccessEditor = AbstractEditor.extend({ VerificationAccessEditor = AbstractEditor.extend({
...@@ -748,41 +830,45 @@ define(['jquery', 'backbone', 'underscore', 'gettext', 'js/views/baseview', ...@@ -748,41 +830,45 @@ define(['jquery', 'backbone', 'underscore', 'gettext', 'js/views/baseview',
}, },
getEditModal: function (xblockInfo, options) { getEditModal: function (xblockInfo, options) {
var editors = [];
var tabs = []; var tabs = [];
var editors = [];
if (xblockInfo.isVertical()) {
editors = [StaffLockEditor];
if (xblockInfo.isChapter()) { if (xblockInfo.hasVerifiedCheckpoints()) {
editors = [ReleaseDateEditor, StaffLockEditor]; editors.push(VerificationAccessEditor);
} else if (xblockInfo.isSequential()) { }
tabs.push({ } else {
tabs = [
{
name: 'basic', name: 'basic',
displayName: gettext('Basic'), displayName: gettext('Basic'),
editors: [ReleaseDateEditor, GradingEditor, DueDateEditor, StaffLockEditor] editors: []
}); },
{
if (options.enable_proctored_exams || options.enable_timed_exams) {
tabs.push({
name: 'advanced', name: 'advanced',
displayName: gettext('Advanced'), displayName: gettext('Advanced'),
editors: [TimedExaminationPreferenceEditor] 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 (typeof(xblockInfo.get('is_prereq')) !== 'undefined') { if (options.enable_proctored_exams || options.enable_timed_exams) {
tabs.push({ tabs[1].editors.push(TimedExaminationPreferenceEditor);
name: 'access',
// Translators: This label refers to access to course content.
displayName: gettext('Access'),
editors: [AccessEditor]
});
} }
} else if (xblockInfo.isVertical()) {
editors = [StaffLockEditor];
if (xblockInfo.hasVerifiedCheckpoints()) { if (typeof(xblockInfo.get('is_prereq')) !== 'undefined') {
editors.push(VerificationAccessEditor); tabs[1].editors.push(AccessEditor);
} }
} }
}
/* globals course */ /* globals course */
if (course.get('self_paced')) { if (course.get('self_paced')) {
editors = _.without(editors, ReleaseDateEditor, DueDateEditor); editors = _.without(editors, ReleaseDateEditor, DueDateEditor);
...@@ -790,6 +876,7 @@ define(['jquery', 'backbone', 'underscore', 'gettext', 'js/views/baseview', ...@@ -790,6 +876,7 @@ define(['jquery', 'backbone', 'underscore', 'gettext', 'js/views/baseview',
tab.editors = _.without(tab.editors, ReleaseDateEditor, DueDateEditor); tab.editors = _.without(tab.editors, ReleaseDateEditor, DueDateEditor);
}); });
} }
return new SettingsXBlockModal($.extend({ return new SettingsXBlockModal($.extend({
tabs: tabs, tabs: tabs,
editors: editors, editors: editors,
......
...@@ -669,8 +669,13 @@ ...@@ -669,8 +669,13 @@
} }
} }
.edit-staff-lock { .edit-staff-lock, .edit-content-visibility {
margin-bottom: $baseline; margin-bottom: $baseline;
.tip {
font-weight: bold;
font-size: 12px;
}
} }
// UI: staff lock section // UI: staff lock section
......
<%page expression_filter="h"/> <%page expression_filter="h"/>
<%inherit file="base.html" /> <%inherit file="base.html" />
<%def name="online_help_token()"><% return "outline" %></%def> <%def name="online_help_token()"><% return "develop_course" %></%def>
<%! <%!
import logging import logging
from util.date_utils import get_default_time_display from util.date_utils import get_default_time_display
...@@ -26,7 +26,7 @@ from openedx.core.djangolib.markup import HTML, Text ...@@ -26,7 +26,7 @@ from openedx.core.djangolib.markup import HTML, Text
<%block name="header_extras"> <%block name="header_extras">
<link rel="stylesheet" type="text/css" href="${static.url('js/vendor/timepicker/jquery.timepicker.css')}" /> <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', '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']:
<script type="text/template" id="${template_name}-tpl"> <script type="text/template" id="${template_name}-tpl">
<%static:include path="js/${template_name}.underscore" /> <%static:include path="js/${template_name}.underscore" />
</script> </script>
...@@ -42,7 +42,7 @@ from openedx.core.djangolib.markup import HTML, Text ...@@ -42,7 +42,7 @@ from openedx.core.djangolib.markup import HTML, Text
<div class="copy"> <div class="copy">
<h2 class="title title-3">${_("This course was created as a re-run. Some manual configuration is needed.")}</h2> <h2 class="title title-3">${_("This course was created as a re-run. Some manual configuration is needed.")}</h2>
<p>${_("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.")}</p> <p>${_("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.")}</p>
</div> </div>
<ul class="nav-actions"> <ul class="nav-actions">
...@@ -193,16 +193,29 @@ from openedx.core.djangolib.markup import HTML, Text ...@@ -193,16 +193,29 @@ from openedx.core.djangolib.markup import HTML, Text
<h3 class="title-3">${_("Creating your course organization")}</h3> <h3 class="title-3">${_("Creating your course organization")}</h3>
<p>${_("You add sections, subsections, and units directly in the outline.")}</p> <p>${_("You add sections, subsections, and units directly in the outline.")}</p>
<p>${_("Create a section, then add subsections and units. Open a unit to add course components.")}</p> <p>${_("Create a section, then add subsections and units. Open a unit to add course components.")}</p>
</div>
<div class="bit">
<h3 class="title-3">${_("Reorganizing your course")}</h3> <h3 class="title-3">${_("Reorganizing your course")}</h3>
<p>${_("Drag sections, subsections, and units to new locations in the outline.")}</p> <p>${_("Drag sections, subsections, and units to new locations in the outline.")}</p>
<div class="external-help">
<a href="${get_online_help_info('outline')['doc_url']}" target="_blank" class="button external-help-button">${_("Learn more about the course outline")}</a>
</div>
</div>
<div class="bit">
<h3 class="title-3">${_("Setting release dates and grading policies")}</h3> <h3 class="title-3">${_("Setting release dates and grading policies")}</h3>
<p>${_("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.")}</p> <p>${_("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.")}</p>
<h3 class="title-3">${_("Changing the content students see")}</h3> <div class="external-help">
<a href="${get_online_help_info('grading')['doc_url']}" target="_blank" class="button external-help-button">${_("Learn more about grading policy settings")}</a>
</div>
</div>
<div class="bit">
<h3 class="title-3">${_("Changing the content learners see")}</h3>
<p>${_("To publish draft content, select the Publish icon for a section, subsection, or unit.")}</p> <p>${_("To publish draft content, select the Publish icon for a section, subsection, or unit.")}</p>
<p>${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("<strong>"), em_end=HTML("</strong>"))}</p> <p>${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("<strong>"), em_end=HTML("</strong>"))}</p>
<p>${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("<strong>"), em_end=HTML("</strong>"))}</p>
<div class="external-help">
<a href="${get_online_help_info('visibility')['doc_url']}" target="_blank" class="button external-help-button">${_("Learn more about content visibility settings")}</a>
</div> </div>
<div class="bit external-help">
<a href="${get_online_help_info(online_help_token())['doc_url']}" target="_blank" class="button external-help-button">${_("Learn more about the course outline")}</a>
</div> </div>
</aside> </aside>
......
<form>
<h3 class="modal-section-title" id="content_visibility_label"><%- gettext('Subsection Visibility') %></h3>
<div class="modal-section-content staff-lock">
<ul class="list-fields list-input content-visibility" role="group" aria-labelledby="content_visibility_label">
<li class="field-radio">
<label class="label">
<input class="input input-radio" name="content-visibility" type="radio" value="visible" aria-described-by"visible_description">
<%- gettext('Show entire subsection') %>
</label>
<p class='field-message' id='visible_description'> <%- gettext('Learners see the published subsection and can access its content.') %> </p>
</li>
<li class="field-radio">
<label class="label">
<input class="input input-radio" name="content-visibility" type="radio" value="hide_after_due" aria-described-by="hide_after_due_description">
<%- gettext('Hide content after due date') %>
</label>
<p class='field-message' id='hide_after_due_description'> <%- gettext('After the subsection\'s due date has passed, learners can no longer access its content. The subsection remains included in grade calculations.') %> </p>
</li>
<li class="field-radio">
<label class="label">
<input class="input input-radio" name="content-visibility" type="radio" value="staff_only" aria-described-by="staff_only_description">
<%- gettext('Hide entire subsection') %>
</label>
<p class='field-message' id='staff_only_description'> <%- gettext('Learners do not see the subsection in the course outline. The subsection is not included in grade calculations.') %> </p>
</li>
</ul>
<% if (hasExplicitStaffLock && !ancestorLocked) { %>
<p class="tip tip-warning">
<%- gettext('Units inherit the visibility setting of the subsection they are in. If you make this subsection visible to learners, published units that were previously hidden also become visible. Only units that were explicitly hidden remain hidden regardless of the option you select for this subsection.') %>
</p>
<% } %>
</div>
</form>
...@@ -177,17 +177,6 @@ if (is_proctored_exam) { ...@@ -177,17 +177,6 @@ if (is_proctored_exam) {
<% } %> <% } %>
</p> </p>
</div> </div>
<div class="status-hide-after-due">
<p>
<% if (!is_proctored_exam && xblockInfo.get('hide_after_due')) { %>
<span class="icon fa fa-eye-slash" aria-hidden="true"></span>
<span class="status-hide-after-due-value"> <%- gettext("Exam will remain hidden after due date") %> </span>
<% } else { %>
<span class="icon fa fa-eye" aria-hidden="true"></span>
<span class="status-hide-after-due-value"> <%- gettext("Exam will be visible after due date") %> </span>
<% } %>
</p>
</div>
<% } else if (xblockInfo.get('due_date') || xblockInfo.get('graded')) { %> <% } else if (xblockInfo.get('due_date') || xblockInfo.get('graded')) { %>
<div class="status-grading"> <div class="status-grading">
<p> <p>
...@@ -200,6 +189,14 @@ if (is_proctored_exam) { ...@@ -200,6 +189,14 @@ if (is_proctored_exam) {
</p> </p>
</div> </div>
<% } %> <% } %>
<div class="status-hide-after-due">
<p>
<% if (xblockInfo.get('hide_after_due')) { %>
<span class="icon fa fa-eye-slash" aria-hidden="true"></span>
<span class="status-hide-after-due-value"> <%- gettext("Subsection is hidden after due date") %> </span>
<% } %>
</p>
</div>
<% } %> <% } %>
<% if (statusMessage) { %> <% if (statusMessage) { %>
<div class="status-message"> <div class="status-message">
......
...@@ -81,7 +81,7 @@ var visibleToStaffOnly = visibilityState === 'staff_only'; ...@@ -81,7 +81,7 @@ var visibleToStaffOnly = visibilityState === 'staff_only';
<% } %> <% } %>
</p> </p>
<% } else { %> <% } else { %>
<p class="visbility-copy copy"><%- gettext("Staff and Students") %></p> <p class="visbility-copy copy"><%- gettext("Staff and Learners") %></p>
<% } %> <% } %>
<% if (hasContentGroupComponents) { %> <% if (hasContentGroupComponents) { %>
<p class="note-visibility"> <p class="note-visibility">
...@@ -97,10 +97,13 @@ var visibleToStaffOnly = visibilityState === 'staff_only'; ...@@ -97,10 +97,13 @@ var visibleToStaffOnly = visibilityState === 'staff_only';
<% } else { %> <% } else { %>
<span class="icon fa fa-square-o" aria-hidden="true"></span> <span class="icon fa fa-square-o" aria-hidden="true"></span>
<% } %> <% } %>
<%- gettext('Hide from students') %> <%- gettext('Hide from learners') %>
</a> </a>
</li> </li>
</ul> </ul>
<p>
<%- gettext("Note: Do not hide graded assignments after they have been released.") %>
</p>
</div> </div>
<div class="wrapper-pub-actions bar-mod-actions"> <div class="wrapper-pub-actions bar-mod-actions">
......
<h3 class="modal-section-title"><%= gettext('Release Date and Time') %></h3> <h3 class="modal-section-title"><%- gettext('Release Date and Time') %></h3>
<div class="modal-section-content has-actions"> <div class="modal-section-content has-actions">
<ul class="list-fields list-input datepair"> <ul class="list-fields list-input datepair">
<li class="field field-text field-start-date field-release-date"> <li class="field field-text field-start-date field-release-date">
<label for="start_date" class="label"><%= gettext('Release Date:') %></label> <label for="start_date" class="label"><%- gettext('Release Date:') %></label>
<input type="text" id="start_date" name="start_date" <input type="text" id="start_date" name="start_date"
value="" value=""
placeholder="MM/DD/YYYY" class="start-date release-date date input input-text" autocomplete="off" /> placeholder="MM/DD/YYYY" class="start-date release-date date input input-text" autocomplete="off" />
</li> </li>
<li class="field field-text field-start-time field-release-time"> <li class="field field-text field-start-time field-release-time">
<label for="start_time" class="label"><%= gettext('Release Time in UTC:') %></label> <label for="start_time" class="label"><%- gettext('Release Time in UTC:') %></label>
<input type="text" id="start_time" name="start_time" <input type="text" id="start_time" name="start_time"
value="" value=""
placeholder="HH:MM" class="start-time release-time time input input-text" autocomplete="off" /> placeholder="HH:MM" class="start-time release-time time input input-text" autocomplete="off" />
...@@ -18,9 +18,9 @@ ...@@ -18,9 +18,9 @@
<% if (xblockInfo.isSequential()) { %> <% if (xblockInfo.isSequential()) { %>
<ul class="list-actions"> <ul class="list-actions">
<li class="action-item"> <li class="action-item">
<a href="#" data-tooltip="<%= gettext('Clear Release Date/Time') %>" class="clear-date action-button action-clear"> <a href="#" data-tooltip="<%- gettext('Clear Release Date/Time') %>" class="clear-date action-button action-clear">
<span class="icon fa fa-undo" aria-hidden="true"></span> <span class="icon fa fa-undo" aria-hidden="true"></span>
<span class="sr"><%= gettext('Clear Release Date/Time') %></span> <span class="sr"><%- gettext('Clear Release Date/Time') %></span>
</a> </a>
</li> </li>
</ul> </ul>
......
<form> <form>
<h3 class="modal-section-title"><%= gettext('Student Visibility') %></h3> <h3 class="modal-section-title">
<% if (xblockInfo.isVertical()) { %>
<%- gettext('Unit Visibility') %>
<% } else { %>
<%- gettext('Section Visibility') %>
<% } %>
</h3>
<div class="modal-section-content staff-lock"> <div class="modal-section-content staff-lock">
<ul class="list-fields list-input"> <ul class="list-fields list-input">
<li class="field field-checkbox checkbox-cosmetic"> <li class="field field-checkbox checkbox-cosmetic">
...@@ -7,25 +13,16 @@ ...@@ -7,25 +13,16 @@
<label for="staff_lock" class="label"> <label for="staff_lock" class="label">
<span class="icon fa fa-check-square-o input-checkbox-checked" aria-hidden="true"></span> <span class="icon fa fa-check-square-o input-checkbox-checked" aria-hidden="true"></span>
<span class="icon fa fa-square-o input-checkbox-unchecked" aria-hidden="true"></span> <span class="icon fa fa-square-o input-checkbox-unchecked" aria-hidden="true"></span>
<%= gettext('Hide from students') %> <%- gettext('Hide from learners') %>
</label> </label>
<% if (hasExplicitStaffLock && !ancestorLocked) { %> <% if (hasExplicitStaffLock && !ancestorLocked) { %>
<p class="tip tip-warning"> <p class="tip tip-warning">
<% if (xblockInfo.isVertical()) { %> <% if (xblockInfo.isVertical()) { %>
<%= gettext('If the unit was previously published and released to students, any changes you made to the unit when it was hidden will now be visible to students.') %> <%- gettext('If the unit was previously published and released to learners, any changes you made to the unit when it was hidden will now be visible to learners.') %>
<% } else { %> <% } else { %>
<% var message = gettext('If you make this %(xblockType)s visible to students, students will be able to see its content after the release date has passed and you have published the unit.'); %> <% var message = gettext('If you make this %(xblockType)s visible to learners, learners will be able to see its content after the release date has passed and you have published the unit. Only units that are explicitly hidden from learners will remain hidden after you clear this option for the %(xblockType)s.') %>
<%= interpolate(message, { xblockType: xblockType }, true) %> <%- interpolate(message, { xblockType: xblockType }, true) %>
<% } %>
</p>
<p class="tip tip-warning">
<% if (xblockInfo.isChapter()) { %>
<%= gettext('Any subsections or units that are explicitly hidden from students will remain hidden after you clear this option for the section.') %>
<% } %>
<% if (xblockInfo.isSequential()) { %>
<%= gettext('Any units that are explicitly hidden from students will remain hidden after you clear this option for the subsection.') %>
<% } %> <% } %>
</p> </p>
<% } %> <% } %>
......
...@@ -52,14 +52,6 @@ ...@@ -52,14 +52,6 @@
</label> </label>
<p class='field-message' id='review-rules-description'><%- gettext('Specify any additional rules or rule exceptions that the proctoring review team should enforce when reviewing the videos. For example, you could specify that calculators are allowed.') %></p> <p class='field-message' id='review-rules-description'><%- gettext('Specify any additional rules or rule exceptions that the proctoring review team should enforce when reviewing the videos. For example, you could specify that calculators are allowed.') %></p>
</li> </li>
<li class="field-checkbox field-hide-after-due">
<label class="label">
<input type="checkbox" class="input input-checkbox"
aria-describedby="hide-after-due-description"/>
<%- gettext('Hide Exam After Due Date') %>
</label>
<p class='field-message' id='hide-after-due-description'><%- gettext('By default, submitted exams are available for review after the due date has passed. Select this option to keep exams hidden after that date.') %></p>
</li>
</ul> </ul>
</div> </div>
</form> </form>
...@@ -142,6 +142,9 @@ class CourseOutlineItem(object): ...@@ -142,6 +142,9 @@ class CourseOutlineItem(object):
Puts the item into editable form. Puts the item into editable form.
""" """
self.q(css=self._bounded_selector(self.CONFIGURATION_BUTTON_SELECTOR)).first.click() # pylint: disable=no-member self.q(css=self._bounded_selector(self.CONFIGURATION_BUTTON_SELECTOR)).first.click() # pylint: disable=no-member
if 'subsection' in self.BODY_SELECTOR:
modal = SubsectionOutlineModal(self)
else:
modal = CourseOutlineModal(self) modal = CourseOutlineModal(self)
EmptyPromise(lambda: modal.is_shown(), 'Modal is shown.') # pylint: disable=unnecessary-lambda EmptyPromise(lambda: modal.is_shown(), 'Modal is shown.') # pylint: disable=unnecessary-lambda
return modal return modal
...@@ -569,12 +572,15 @@ class CourseOutlinePage(CoursePage, CourseOutlineContainer): ...@@ -569,12 +572,15 @@ class CourseOutlinePage(CoursePage, CourseOutlineContainer):
self.q(css=".action-save").first.click() self.q(css=".action-save").first.click()
self.wait_for_ajax() self.wait_for_ajax()
def select_advanced_tab(self): def select_advanced_tab(self, desired_item='special_exam'):
""" """
Select the advanced settings tab Select the advanced settings tab
""" """
self.q(css=".settings-tab-button[data-tab='advanced']").first.click() self.q(css=".settings-tab-button[data-tab='advanced']").first.click()
if desired_item == 'special_exam':
self.wait_for_element_presence('input.no_special_exam', 'Special exam settings fields not present.') self.wait_for_element_presence('input.no_special_exam', 'Special exam settings fields not present.')
if desired_item == 'gated_content':
self.wait_for_element_visibility('#is_prereq', 'Gating settings fields are present.')
def make_exam_proctored(self): def make_exam_proctored(self):
""" """
...@@ -590,7 +596,7 @@ class CourseOutlinePage(CoursePage, CourseOutlineContainer): ...@@ -590,7 +596,7 @@ class CourseOutlinePage(CoursePage, CourseOutlineContainer):
""" """
self.q(css="input.timed_exam").first.click() self.q(css="input.timed_exam").first.click()
if hide_after_due: if hide_after_due:
self.q(css='.field-hide-after-due input').first.click() self.q(css='input[name=content-visibility][value=hide_after_due]').first.click()
self.q(css=".action-save").first.click() self.q(css=".action-save").first.click()
self.wait_for_ajax() self.wait_for_ajax()
...@@ -630,12 +636,6 @@ class CourseOutlinePage(CoursePage, CourseOutlineContainer): ...@@ -630,12 +636,6 @@ class CourseOutlinePage(CoursePage, CourseOutlineContainer):
""" """
return self.q(css=".field-exam-review-rules").visible return self.q(css=".field-exam-review-rules").visible
def hide_after_due_field_visible(self):
"""
Returns whether the hide after due field is visible
"""
return self.q(css=".field-hide-after-due").visible
def proctoring_items_are_displayed(self): def proctoring_items_are_displayed(self):
""" """
Returns True if all the items are found. Returns True if all the items are found.
...@@ -659,13 +659,6 @@ class CourseOutlinePage(CoursePage, CourseOutlineContainer): ...@@ -659,13 +659,6 @@ class CourseOutlinePage(CoursePage, CourseOutlineContainer):
return True return True
def select_access_tab(self):
"""
Select the access settings tab.
"""
self.q(css=".settings-tab-button[data-tab='access']").first.click()
self.wait_for_element_visibility('#is_prereq', 'Gating settings fields are present.')
def make_gating_prerequisite(self): def make_gating_prerequisite(self):
""" """
Makes a subsection a gating prerequisite. Makes a subsection a gating prerequisite.
...@@ -848,6 +841,8 @@ class CourseOutlinePage(CoursePage, CourseOutlineContainer): ...@@ -848,6 +841,8 @@ class CourseOutlinePage(CoursePage, CourseOutlineContainer):
class CourseOutlineModal(object): class CourseOutlineModal(object):
""" """
Page object specifically for a modal window on the course outline page. Page object specifically for a modal window on the course outline page.
Subsections are handled slightly differently in some regards, and should use SubsectionOutlineModal.
""" """
MODAL_SELECTOR = ".wrapper-modal-window" MODAL_SELECTOR = ".wrapper-modal-window"
...@@ -1037,17 +1032,38 @@ class CourseOutlineModal(object): ...@@ -1037,17 +1032,38 @@ class CourseOutlineModal(object):
).fulfill() ).fulfill()
@property @property
def is_staff_lock_visible(self):
"""
Returns True if the staff lock option is visible.
"""
return self.find_css('#staff_lock').visible
def ensure_staff_lock_visible(self):
"""
Ensures the staff lock option is visible, clicking on the advanced tab
if needed.
"""
if not self.is_staff_lock_visible:
self.find_css(".settings-tab-button[data-tab=advanced]").click()
EmptyPromise(
lambda: self.is_staff_lock_visible,
"Staff lock option is visible",
).fulfill()
@property
def is_explicitly_locked(self): def is_explicitly_locked(self):
""" """
Returns true if the explict staff lock checkbox is checked, false otherwise. Returns true if the explict staff lock checkbox is checked, false otherwise.
""" """
self.ensure_staff_lock_visible()
return self.find_css('#staff_lock')[0].is_selected() return self.find_css('#staff_lock')[0].is_selected()
@is_explicitly_locked.setter @is_explicitly_locked.setter
def is_explicitly_locked(self, value): def is_explicitly_locked(self, value):
""" """
Checks the explicit staff lock box if value is true, otherwise unchecks the box. Checks the explicit staff lock box if value is true, otherwise selects "visible".
""" """
self.ensure_staff_lock_visible()
if value != self.is_explicitly_locked: if value != self.is_explicitly_locked:
self.find_css('label[for="staff_lock"]').click() self.find_css('label[for="staff_lock"]').click()
EmptyPromise(lambda: value == self.is_explicitly_locked, "Explicit staff lock is updated").fulfill() EmptyPromise(lambda: value == self.is_explicitly_locked, "Explicit staff lock is updated").fulfill()
...@@ -1067,3 +1083,52 @@ class CourseOutlineModal(object): ...@@ -1067,3 +1083,52 @@ class CourseOutlineModal(object):
return select.first_selected_option.text return select.first_selected_option.text
else: else:
return None return None
class SubsectionOutlineModal(CourseOutlineModal):
"""
Subclass to handle a few special cases with subsection modals.
"""
def __init__(self, page):
super(SubsectionOutlineModal, self).__init__(page)
@property
def is_explicitly_locked(self):
"""
Override - returns True if staff_only is set.
"""
return self.subsection_visibility == 'staff_only'
@property
def subsection_visibility(self):
"""
Returns the current visibility setting for a subsection
"""
self.ensure_staff_lock_visible()
return self.find_css('input[name=content-visibility]:checked').first.attrs('value')[0]
@is_explicitly_locked.setter
def is_explicitly_locked(self, value): # pylint: disable=arguments-differ
"""
Override - sets visibility to staff_only if True, else 'visible'.
For hide_after_due, use the set_subsection_visibility method directly.
"""
self.subsection_visibility = 'staff_only' if value else 'visible'
@subsection_visibility.setter
def subsection_visibility(self, value):
"""
Sets the subsection visibility to the given value.
"""
self.ensure_staff_lock_visible()
self.find_css('input[name=content-visibility][value=' + value + ']').click()
EmptyPromise(lambda: value == self.subsection_visibility, "Subsection visibility is updated").fulfill()
@property
def is_staff_lock_visible(self):
"""
Override - Returns true if the staff lock option is visible.
"""
return self.find_css('input[name=content-visibility]').visible
...@@ -331,11 +331,11 @@ class ProctoredExamTest(UniqueCourseTest): ...@@ -331,11 +331,11 @@ class ProctoredExamTest(UniqueCourseTest):
And the subsection edit dialog is open And the subsection edit dialog is open
select advanced settings tab select advanced settings tab
For each of None, Timed, Proctored, and Practice exam types For each of None, Timed, Proctored, and Practice exam types
The time allotted, review rules, and hide after due fields have proper visibility The time allotted and review rules fields have proper visibility
None: False, False, False None: False, False
Timed: True, False, True Timed: True, False
Proctored: True, True, False Proctored: True, True
Practice: True, False, False Practice: True, False
""" """
LogoutPage(self.browser).visit() LogoutPage(self.browser).visit()
self._auto_auth("STAFF_TESTER", "staff101@example.com", True) self._auto_auth("STAFF_TESTER", "staff101@example.com", True)
...@@ -347,22 +347,18 @@ class ProctoredExamTest(UniqueCourseTest): ...@@ -347,22 +347,18 @@ class ProctoredExamTest(UniqueCourseTest):
self.course_outline.select_none_exam() self.course_outline.select_none_exam()
self.assertFalse(self.course_outline.time_allotted_field_visible()) self.assertFalse(self.course_outline.time_allotted_field_visible())
self.assertFalse(self.course_outline.exam_review_rules_field_visible()) self.assertFalse(self.course_outline.exam_review_rules_field_visible())
self.assertFalse(self.course_outline.hide_after_due_field_visible())
self.course_outline.select_timed_exam() self.course_outline.select_timed_exam()
self.assertTrue(self.course_outline.time_allotted_field_visible()) self.assertTrue(self.course_outline.time_allotted_field_visible())
self.assertFalse(self.course_outline.exam_review_rules_field_visible()) self.assertFalse(self.course_outline.exam_review_rules_field_visible())
self.assertTrue(self.course_outline.hide_after_due_field_visible())
self.course_outline.select_proctored_exam() self.course_outline.select_proctored_exam()
self.assertTrue(self.course_outline.time_allotted_field_visible()) self.assertTrue(self.course_outline.time_allotted_field_visible())
self.assertTrue(self.course_outline.exam_review_rules_field_visible()) self.assertTrue(self.course_outline.exam_review_rules_field_visible())
self.assertFalse(self.course_outline.hide_after_due_field_visible())
self.course_outline.select_practice_exam() self.course_outline.select_practice_exam()
self.assertTrue(self.course_outline.time_allotted_field_visible()) self.assertTrue(self.course_outline.time_allotted_field_visible())
self.assertFalse(self.course_outline.exam_review_rules_field_visible()) self.assertFalse(self.course_outline.exam_review_rules_field_visible())
self.assertFalse(self.course_outline.hide_after_due_field_visible())
class CoursewareMultipleVerticalsTest(UniqueCourseTest, EventsTestMixin): class CoursewareMultipleVerticalsTest(UniqueCourseTest, EventsTestMixin):
......
...@@ -87,7 +87,7 @@ class GatingTest(UniqueCourseTest): ...@@ -87,7 +87,7 @@ class GatingTest(UniqueCourseTest):
# Make the first subsection a prerequisite # Make the first subsection a prerequisite
self.course_outline.visit() self.course_outline.visit()
self.course_outline.open_subsection_settings_dialog(0) self.course_outline.open_subsection_settings_dialog(0)
self.course_outline.select_access_tab() self.course_outline.select_advanced_tab(desired_item='gated_content')
self.course_outline.make_gating_prerequisite() self.course_outline.make_gating_prerequisite()
def _setup_gated_subsection(self): def _setup_gated_subsection(self):
...@@ -100,7 +100,7 @@ class GatingTest(UniqueCourseTest): ...@@ -100,7 +100,7 @@ class GatingTest(UniqueCourseTest):
# Gate the second subsection based on the score achieved in the first subsection # Gate the second subsection based on the score achieved in the first subsection
self.course_outline.visit() self.course_outline.visit()
self.course_outline.open_subsection_settings_dialog(1) self.course_outline.open_subsection_settings_dialog(1)
self.course_outline.select_access_tab() self.course_outline.select_advanced_tab(desired_item='gated_content')
self.course_outline.add_prerequisite_to_subsection("80") self.course_outline.add_prerequisite_to_subsection("80")
def test_subsection_gating_in_studio(self): def test_subsection_gating_in_studio(self):
...@@ -116,7 +116,7 @@ class GatingTest(UniqueCourseTest): ...@@ -116,7 +116,7 @@ class GatingTest(UniqueCourseTest):
# Assert settings are displayed correctly for a prerequisite subsection # Assert settings are displayed correctly for a prerequisite subsection
self.course_outline.visit() self.course_outline.visit()
self.course_outline.open_subsection_settings_dialog(0) self.course_outline.open_subsection_settings_dialog(0)
self.course_outline.select_access_tab() self.course_outline.select_advanced_tab(desired_item='gated_content')
self.assertTrue(self.course_outline.gating_prerequisite_checkbox_is_visible()) self.assertTrue(self.course_outline.gating_prerequisite_checkbox_is_visible())
self.assertTrue(self.course_outline.gating_prerequisite_checkbox_is_checked()) self.assertTrue(self.course_outline.gating_prerequisite_checkbox_is_checked())
self.assertFalse(self.course_outline.gating_prerequisites_dropdown_is_visible()) self.assertFalse(self.course_outline.gating_prerequisites_dropdown_is_visible())
...@@ -127,7 +127,7 @@ class GatingTest(UniqueCourseTest): ...@@ -127,7 +127,7 @@ class GatingTest(UniqueCourseTest):
# Assert settings are displayed correctly for a gated subsection # Assert settings are displayed correctly for a gated subsection
self.course_outline.visit() self.course_outline.visit()
self.course_outline.open_subsection_settings_dialog(1) self.course_outline.open_subsection_settings_dialog(1)
self.course_outline.select_access_tab() self.course_outline.select_advanced_tab(desired_item='gated_content')
self.assertTrue(self.course_outline.gating_prerequisite_checkbox_is_visible()) self.assertTrue(self.course_outline.gating_prerequisite_checkbox_is_visible())
self.assertTrue(self.course_outline.gating_prerequisites_dropdown_is_visible()) self.assertTrue(self.course_outline.gating_prerequisites_dropdown_is_visible())
self.assertTrue(self.course_outline.gating_prerequisite_min_score_is_visible()) self.assertTrue(self.course_outline.gating_prerequisite_min_score_is_visible())
......
...@@ -16,8 +16,10 @@ pdf_file = edx-partner-course-staff.pdf ...@@ -16,8 +16,10 @@ pdf_file = edx-partner-course-staff.pdf
[pages] [pages]
default = index.html default = index.html
home = getting_started/get_started.html home = getting_started/get_started.html
develop_course = developing_course/index.html
outline = developing_course/course_outline.html outline = developing_course/course_outline.html
unit = developing_course/course_units.html unit = developing_course/course_units.html
visibility = developing_course/controlling_content_visibility.html
updates = course_assets/handouts_updates.html updates = course_assets/handouts_updates.html
pages = course_assets/pages.html pages = course_assets/pages.html
files = course_assets/course_files.html files = course_assets/course_files.html
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment