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 name="online_help_token()"><% return "develop_course" %> <%! 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.")}

+

+ <%- 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 @@ - +