Commit c6b54aa6 by Bill DeRusha

Readonly for enrollment end date/time on edX (for non-global-staff)

TNL-2694

Add accessibility aria-readonly attributes
parent ac0d9264
......@@ -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 = "<div class=\"field date is-not-editable\" id=\"field-enrollment-end-date\">"
NOT_EDITABLE_TIME_WRAPPER = "<div class=\"field time is-not-editable\" id=\"field-enrollment-end-time\">"
NOT_EDITABLE_DATE_FIELD = "<input type=\"text\" class=\"end-date date end\" \
id=\"course-enrollment-end-date\" placeholder=\"MM/DD/YYYY\" autocomplete=\"off\" readonly aria-readonly=\"true\" />"
NOT_EDITABLE_TIME_FIELD = "<input type=\"text\" class=\"time end\" id=\"course-enrollment-end-time\" \
value=\"\" placeholder=\"HH:MM\" autocomplete=\"off\" readonly aria-readonly=\"true\" />"
EDITABLE_DATE_WRAPPER = "<div class=\"field date \" id=\"field-enrollment-end-date\">"
EDITABLE_TIME_WRAPPER = "<div class=\"field time \" id=\"field-enrollment-end-time\">"
EDITABLE_DATE_FIELD = "<input type=\"text\" class=\"end-date date end\" \
id=\"course-enrollment-end-date\" placeholder=\"MM/DD/YYYY\" autocomplete=\"off\" />"
EDITABLE_TIME_FIELD = "<input type=\"text\" class=\"time end\" \
id=\"course-enrollment-end-time\" value=\"\" placeholder=\"HH:MM\" autocomplete=\"off\" />"
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))
......@@ -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)
......
......@@ -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);
......
......@@ -220,17 +220,25 @@ CMS.URL.UPLOAD_ASSET = '${upload_asset_url}';
<span class="tip tip-stacked timezone">${_("(UTC)")}</span>
</div>
</li>
<%
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 ""
%>
<li class="field-group field-group-enrollment-end" id="enrollment-end">
<div class="field date" id="field-enrollment-end-date">
<div class="field date ${enrollment_end_editable_class}" id="field-enrollment-end-date">
<label for="course-enrollment-end-date">${_("Enrollment End Date")}</label>
<input type="text" class="end-date date end" id="course-enrollment-end-date" placeholder="MM/DD/YYYY" autocomplete="off" />
<span class="tip tip-stacked">${_("Last day students can enroll")}</span>
<input type="text" class="end-date date end" id="course-enrollment-end-date" placeholder="MM/DD/YYYY" autocomplete="off" ${enrollment_end_readonly} />
<span class="tip tip-stacked">
${_("Last day students can enroll.")}
% if not enrollment_end_editable:
${_("Contact your edX Partner Manager to update these settings.")}
% endif
</span>
</div>
<div class="field time" id="field-enrollment-end-time">
<div class="field time ${enrollment_end_editable_class}" id="field-enrollment-end-time">
<label for="course-enrollment-end-time">${_("Enrollment End Time")}</label>
<input type="text" class="time end" id="course-enrollment-end-time" value="" placeholder="HH:MM" autocomplete="off" />
<input type="text" class="time end" id="course-enrollment-end-time" value="" placeholder="HH:MM" autocomplete="off" ${enrollment_end_readonly} />
<span class="tip tip-stacked timezone">${_("(UTC)")}</span>
</div>
</li>
......
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