diff --git a/cms/djangoapps/contentstore/tests/test_course_settings.py b/cms/djangoapps/contentstore/tests/test_course_settings.py index a9dd9ee8bd..57a9718cf5 100644 --- a/cms/djangoapps/contentstore/tests/test_course_settings.py +++ b/cms/djangoapps/contentstore/tests/test_course_settings.py @@ -16,6 +16,9 @@ from models.settings.course_details import (CourseDetails, CourseSettingsEncoder from models.settings.course_grading import CourseGradingModel from contentstore.utils import reverse_course_url, reverse_usage_url from xmodule.modulestore.tests.factories import CourseFactory +from student.roles import CourseInstructorRole +from student.tests.factories import UserFactory + from models.settings.course_metadata import CourseMetadata from xmodule.fields import Date @@ -1106,3 +1109,116 @@ class CourseGraderUpdatesTest(CourseTestCase): self.assertEqual(obj, grader) current_graders = CourseGradingModel.fetch(self.course.id).graders self.assertEqual(len(self.starting_graders) + 1, len(current_graders)) + + +class CourseEnrollmentEndFieldTest(CourseTestCase): + """ + Base class to test the enrollment end fields in the course settings details view in Studio + when using marketing site flag and global vs non-global staff to access the page. + """ + NOT_EDITABLE_HELPER_MESSAGE = "Contact your edX Partner Manager to update these settings." + NOT_EDITABLE_DATE_WRAPPER = "
" + NOT_EDITABLE_TIME_WRAPPER = "
" + NOT_EDITABLE_DATE_FIELD = "" + NOT_EDITABLE_TIME_FIELD = "" + + EDITABLE_DATE_WRAPPER = "
" + EDITABLE_TIME_WRAPPER = "
" + EDITABLE_DATE_FIELD = "" + EDITABLE_TIME_FIELD = "" + + EDITABLE_ELEMENTS = [ + EDITABLE_DATE_WRAPPER, + EDITABLE_TIME_WRAPPER, + EDITABLE_DATE_FIELD, + EDITABLE_TIME_FIELD, + ] + + NOT_EDITABLE_ELEMENTS = [ + NOT_EDITABLE_HELPER_MESSAGE, + NOT_EDITABLE_DATE_WRAPPER, + NOT_EDITABLE_TIME_WRAPPER, + NOT_EDITABLE_DATE_FIELD, + NOT_EDITABLE_TIME_FIELD, + ] + + def setUp(self): + """ Initialize course used to test enrollment fields. """ + super(CourseEnrollmentEndFieldTest, self).setUp() + self.course = CourseFactory.create(org='edX', number='dummy', display_name='Marketing Site Course') + self.course_details_url = reverse_course_url('settings_handler', unicode(self.course.id)) + + def _get_course_details_response(self, global_staff): + """ Return the course details page as either global or non-global staff""" + user = UserFactory(is_staff=global_staff) + CourseInstructorRole(self.course.id).add_users(user) + + self.client.login(username=user.username, password='test') + + return self.client.get_html(self.course_details_url) + + def _verify_editable(self, response): + """ Verify that the response has expected editable fields. + + Assert that all editable field content exists and no + uneditable field content exists for enrollment end fields. + """ + self.assertEqual(response.status_code, 200) + for element in self.NOT_EDITABLE_ELEMENTS: + self.assertNotContains(response, element) + + for element in self.EDITABLE_ELEMENTS: + self.assertContains(response, element) + + def _verify_not_editable(self, response): + """ Verify that the response has expected non-editable fields. + + Assert that all uneditable field content exists and no + editable field content exists for enrollment end fields. + """ + self.assertEqual(response.status_code, 200) + for element in self.NOT_EDITABLE_ELEMENTS: + self.assertContains(response, element) + + for element in self.EDITABLE_ELEMENTS: + self.assertNotContains(response, element) + + @mock.patch.dict("django.conf.settings.FEATURES", {'ENABLE_MKTG_SITE': False}) + def test_course_details_with_disabled_setting_global_staff(self): + """ Test that user enrollment end date is editable in response. + + Feature flag 'ENABLE_MKTG_SITE' is not enabled. + User is global staff. + """ + self._verify_editable(self._get_course_details_response(True)) + + @mock.patch.dict("django.conf.settings.FEATURES", {'ENABLE_MKTG_SITE': False}) + def test_course_details_with_disabled_setting_non_global_staff(self): + """ Test that user enrollment end date is editable in response. + + Feature flag 'ENABLE_MKTG_SITE' is not enabled. + User is non-global staff. + """ + self._verify_editable(self._get_course_details_response(False)) + + @mock.patch.dict("django.conf.settings.FEATURES", {'ENABLE_MKTG_SITE': True}) + def test_course_details_with_enabled_setting_global_staff(self): + """ Test that user enrollment end date is editable in response. + + Feature flag 'ENABLE_MKTG_SITE' is enabled. + User is global staff. + """ + self._verify_editable(self._get_course_details_response(True)) + + @mock.patch.dict("django.conf.settings.FEATURES", {'ENABLE_MKTG_SITE': True}) + def test_course_details_with_enabled_setting_non_global_staff(self): + """ Test that user enrollment end date is not editable in response. + + Feature flag 'ENABLE_MKTG_SITE' is enabled. + User is non-global staff. + """ + self._verify_not_editable(self._get_course_details_response(False)) diff --git a/cms/djangoapps/contentstore/views/course.py b/cms/djangoapps/contentstore/views/course.py index 5c230c13d7..113a543ee4 100644 --- a/cms/djangoapps/contentstore/views/course.py +++ b/cms/djangoapps/contentstore/views/course.py @@ -903,12 +903,15 @@ def settings_handler(request, course_key_string): # see if the ORG of this course can be attributed to a 'Microsite'. In that case, the # course about page should be editable in Studio - about_page_editable = not microsite.get_value_for_org( + marketing_site_enabled = microsite.get_value_for_org( course_module.location.org, 'ENABLE_MKTG_SITE', settings.FEATURES.get('ENABLE_MKTG_SITE', False) ) + about_page_editable = not marketing_site_enabled + enrollment_end_editable = GlobalStaff().has_user(request.user) or not marketing_site_enabled + short_description_editable = settings.FEATURES.get('EDITABLE_SHORT_DESCRIPTION', True) settings_context = { 'context_course': course_module, @@ -924,6 +927,7 @@ def settings_handler(request, course_key_string): 'credit_eligibility_enabled': credit_eligibility_enabled, 'is_credit_course': False, 'show_min_grade_warning': False, + 'enrollment_end_editable': enrollment_end_editable, } if prerequisite_course_enabled: courses, in_process_course_actions = get_courses_accessible_to_user(request) diff --git a/cms/static/sass/views/_settings.scss b/cms/static/sass/views/_settings.scss index 822be9f2ba..8be7e82dd5 100644 --- a/cms/static/sass/views/_settings.scss +++ b/cms/static/sass/views/_settings.scss @@ -62,6 +62,27 @@ margin-top: ($baseline); } + // specific fields - settings details + .settings-details { + + // course details that should appear more like content than elements to change + .is-not-editable { + + label { + + } + + input, textarea { + @extend %t-copy-lead1; + @extend %t-strong; + box-shadow: none; + border: none; + background: none; + margin: 0; + } + } + } + // in form - elements .group-settings { @@ -305,21 +326,10 @@ } } - // course details that should appear more like content than elements to change - .field.is-not-editable { - - label { - - } + .is-not-editable { input, textarea { - @extend %t-copy-lead1; - @extend %t-strong; - box-shadow: none; - border: none; - background: none; padding: 0; - margin: 0; } } @@ -438,6 +448,13 @@ padding-bottom: 0; } + .is-not-editable { + + input, textarea { + padding: 10px; + } + } + .field { @include float(left); width: flex-grid(3, 9); diff --git a/cms/templates/settings.html b/cms/templates/settings.html index 4f0769e080..9cccd92ecb 100644 --- a/cms/templates/settings.html +++ b/cms/templates/settings.html @@ -220,17 +220,25 @@ CMS.URL.UPLOAD_ASSET = '${upload_asset_url}'; ${_("(UTC)")}
- + <% + enrollment_end_readonly = "readonly aria-readonly=\"true\"" if not enrollment_end_editable else "" + enrollment_end_editable_class = "is-not-editable" if not enrollment_end_editable else "" + %>
  • -
    +
    - - ${_("Last day students can enroll")} + + + ${_("Last day students can enroll.")} + % if not enrollment_end_editable: + ${_("Contact your edX Partner Manager to update these settings.")} + % endif +
    -
    +
    - + ${_("(UTC)")}