diff --git a/cms/djangoapps/contentstore/config/waffle.py b/cms/djangoapps/contentstore/config/waffle.py index 4682473a3c..6457e77b03 100644 --- a/cms/djangoapps/contentstore/config/waffle.py +++ b/cms/djangoapps/contentstore/config/waffle.py @@ -36,3 +36,9 @@ ENABLE_CHECKLISTS_QUALITY = CourseWaffleFlag( flag_name=u'enable_checklists_quality', flag_undefined_default=True ) + +SHOW_REVIEW_RULES_FLAG = CourseWaffleFlag( + waffle_namespace=waffle_flags(), + flag_name=u'show_review_rules', + flag_undefined_default=False +) diff --git a/cms/djangoapps/contentstore/views/item.py b/cms/djangoapps/contentstore/views/item.py index 88d2a030c7..71d4cf44db 100644 --- a/cms/djangoapps/contentstore/views/item.py +++ b/cms/djangoapps/contentstore/views/item.py @@ -69,6 +69,7 @@ from xmodule.services import ConfigurationService, SettingsService from xmodule.tabs import CourseTabList from xmodule.x_module import DEPRECATION_VSCOMPAT_EVENT, PREVIEW_VIEWS, STUDENT_VIEW, STUDIO_VIEW from edx_proctoring.api import get_exam_configuration_dashboard_url, does_backend_support_onboarding +from cms.djangoapps.contentstore.config.waffle import SHOW_REVIEW_RULES_FLAG __all__ = [ 'orphan_handler', 'xblock_handler', 'xblock_view_handler', 'xblock_outline_handler', 'xblock_container_handler' @@ -1218,7 +1219,7 @@ def create_xblock_info(xblock, data=None, metadata=None, include_ancestor_info=F xblock_info.update({ 'enable_proctored_exams': xblock.enable_proctored_exams, 'create_zendesk_tickets': xblock.create_zendesk_tickets, - 'enable_timed_exams': xblock.enable_timed_exams + 'enable_timed_exams': xblock.enable_timed_exams, }) elif xblock.category == 'sequential': rules_url = settings.PROCTORING_SETTINGS.get('LINK_URLS', {}).get('online_proctoring_rules', "") @@ -1239,6 +1240,7 @@ def create_xblock_info(xblock, data=None, metadata=None, include_ancestor_info=F 'default_time_limit_minutes': xblock.default_time_limit_minutes, 'proctoring_exam_configuration_link': proctoring_exam_configuration_link, 'supports_onboarding': supports_onboarding, + 'show_review_rules': SHOW_REVIEW_RULES_FLAG.is_enabled(xblock.location.course_key), }) # Update with gating info diff --git a/cms/static/js/spec/views/pages/course_outline_spec.js b/cms/static/js/spec/views/pages/course_outline_spec.js index a3965bef17..af26914fbf 100644 --- a/cms/static/js/spec/views/pages/course_outline_spec.js +++ b/cms/static/js/spec/views/pages/course_outline_spec.js @@ -14,7 +14,7 @@ describe('CourseOutlinePage', function() { selectVisibilitySettings, selectAdvancedSettings, createMockCourseJSON, createMockSectionJSON, createMockSubsectionJSON, verifyTypePublishable, mockCourseJSON, mockEmptyCourseJSON, setSelfPaced, mockSingleSectionCourseJSON, createMockVerticalJSON, createMockIndexJSON, mockCourseEntranceExamJSON, - selectOnboardingExam, + selectOnboardingExam, createMockCourseJSONWithReviewRules,mockCourseJSONWithReviewRules, mockOutlinePage = readFixtures('templates/mock/mock-course-outline-page.underscore'), mockRerunNotification = readFixtures('templates/mock/mock-course-rerun-notification.underscore'); @@ -44,6 +44,33 @@ describe('CourseOutlinePage', function() { }, options, {child_info: {children: children}}); }; + createMockCourseJSONWithReviewRules = function(options, children) { + return $.extend(true, {}, { + id: 'mock-course', + display_name: 'Mock Course', + category: 'course', + enable_proctored_exams: true, + enable_timed_exams: true, + studio_url: '/course/slashes:MockCourse', + is_container: true, + has_changes: false, + published: true, + edited_on: 'Jul 02, 2014 at 20:56 UTC', + edited_by: 'MockUser', + has_explicit_staff_lock: false, + child_info: { + category: 'chapter', + display_name: 'Section', + children: [] + }, + user_partitions: [], + show_review_rules: true, + user_partition_info: {}, + highlights_enabled: true, + highlights_enabled_for_messaging: false + }, options, {child_info: {children: children}}); + }; + createMockSectionJSON = function(options, children) { return $.extend(true, {}, { id: 'mock-section', @@ -281,6 +308,13 @@ describe('CourseOutlinePage', function() { ]) ]) ]); + mockCourseJSONWithReviewRules = createMockCourseJSONWithReviewRules({}, [ + createMockSectionJSON({}, [ + createMockSubsectionJSON({}, [ + createMockVerticalJSON() + ]) + ]) + ]); mockEmptyCourseJSON = createMockCourseJSON(); mockSingleSectionCourseJSON = createMockCourseJSON({}, [ createMockSectionJSON() @@ -987,7 +1021,7 @@ describe('CourseOutlinePage', function() { var getDisplayNameWrapper, setEditModalValues, setContentVisibility, mockServerValuesJson, selectDisableSpecialExams, selectTimedExam, selectProctoredExam, selectPracticeExam, selectPrerequisite, selectLastPrerequisiteSubsection, checkOptionFieldVisibility, - defaultModalSettings, getMockNoPrereqOrExamsCourseJSON, expectShowCorrectness; + defaultModalSettings, modalSettingsWithExamReviewRules, getMockNoPrereqOrExamsCourseJSON, expectShowCorrectness; getDisplayNameWrapper = function() { return getItemHeaders('subsection').find('.wrapper-xblock-field'); @@ -1075,7 +1109,19 @@ describe('CourseOutlinePage', function() { due: null, is_practice_exam: false, is_time_limited: false, - exam_review_rules: '', + is_proctored_enabled: false, + default_time_limit_minutes: null, + is_onboarding_exam: false + } + }; + + modalSettingsWithExamReviewRules = { + graderType: 'notgraded', + isPrereq: false, + metadata: { + due: null, + is_practice_exam: false, + is_time_limited: false, is_proctored_enabled: false, default_time_limit_minutes: null, is_onboarding_exam: false @@ -1277,7 +1323,6 @@ describe('CourseOutlinePage', function() { visible_to_staff_only: null, start: '2014-07-09T00:00:00.000Z', due: '2014-07-10T00:00:00.000Z', - exam_review_rules: '', is_time_limited: true, is_practice_exam: false, is_proctored_enabled: false, @@ -1320,6 +1365,14 @@ describe('CourseOutlinePage', function() { expectShowCorrectness('never'); }); + it('review rules exists', function() { + createCourseOutlinePage(this, mockCourseJSONWithReviewRules, false); + outlinePage.$('.outline-subsection .configure-button').click(); + $('.wrapper-modal-window .action-save').click(); + AjaxHelpers.expectJsonRequest(requests, 'POST', '/xblock/mock-subsection', modalSettingsWithExamReviewRules); + expect(requests[0].requestHeaders['X-HTTP-Method-Override']).toBe('PATCH'); + }); + 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(); @@ -1363,6 +1416,7 @@ describe('CourseOutlinePage', function() { is_proctored_exam: true, default_time_limit_minutes: 150, supports_onboarding: true, + show_review_rules: true }, [ ]) ]) @@ -1452,7 +1506,26 @@ describe('CourseOutlinePage', function() { }); it('can select the Proctored exam option', function() { - createCourseOutlinePage(this, mockCourseJSON, false); + var mockCourseWithSpecialExamJSON = createMockCourseJSON({}, [ + createMockSectionJSON({ + has_changes: true, + enable_proctored_exams: true, + enable_timed_exams: true + + }, [ + createMockSubsectionJSON({ + has_changes: true, + is_time_limited: true, + is_practice_exam: true, + is_proctored_exam: true, + default_time_limit_minutes: 150, + supports_onboarding: false, + show_review_rules: true, + }, [ + ]) + ]) + ]); + createCourseOutlinePage(this, mockCourseWithSpecialExamJSON, false); outlinePage.$('.outline-subsection .configure-button').click(); setEditModalValues('7/9/2014', '7/10/2014', 'Lab'); selectVisibilitySettings(); diff --git a/cms/templates/js/timed-examination-preference-editor.underscore b/cms/templates/js/timed-examination-preference-editor.underscore index 9683c71e43..6c2f480f87 100644 --- a/cms/templates/js/timed-examination-preference-editor.underscore +++ b/cms/templates/js/timed-examination-preference-editor.underscore @@ -47,30 +47,33 @@
-