diff --git a/common/djangoapps/user_api/tests/test_views.py b/common/djangoapps/user_api/tests/test_views.py
index 1edc3e8a65..350a5e4e44 100644
--- a/common/djangoapps/user_api/tests/test_views.py
+++ b/common/djangoapps/user_api/tests/test_views.py
@@ -983,6 +983,78 @@ class RegistrationViewTest(ApiTestCase):
}
)
+ @override_settings(MKTG_URLS={
+ "ROOT": "https://www.test.com/",
+ "HONOR": "honor",
+ "TOS": "tos",
+ })
+ @patch.dict(settings.FEATURES, {"ENABLE_MKTG_SITE": True})
+ def test_registration_separate_terms_of_service_mktg_site_enabled(self):
+ # Honor code field should say ONLY honor code,
+ # not "terms of service and honor code"
+ self._assert_reg_field(
+ {"honor_code": "required", "terms_of_service": "required"},
+ {
+ "label": "I agree to the Honor Code",
+ "name": "honor_code",
+ "default": False,
+ "type": "checkbox",
+ "required": True,
+ "placeholder": "",
+ "instructions": "",
+ "restrictions": {},
+ }
+ )
+
+ # Terms of service field should also be present
+ self._assert_reg_field(
+ {"honor_code": "required", "terms_of_service": "required"},
+ {
+ "label": "I agree to the Terms of Service",
+ "name": "terms_of_service",
+ "default": False,
+ "type": "checkbox",
+ "required": True,
+ "placeholder": "",
+ "instructions": "",
+ "restrictions": {},
+ }
+ )
+
+ @override_settings(MKTG_URLS_LINK_MAP={"HONOR": "honor", "TOS": "tos"})
+ @patch.dict(settings.FEATURES, {"ENABLE_MKTG_SITE": False})
+ def test_registration_separate_terms_of_service_mktg_site_disabled(self):
+ # Honor code field should say ONLY honor code,
+ # not "terms of service and honor code"
+ self._assert_reg_field(
+ {"honor_code": "required", "terms_of_service": "required"},
+ {
+ "label": "I agree to the Honor Code",
+ "name": "honor_code",
+ "default": False,
+ "type": "checkbox",
+ "required": True,
+ "placeholder": "",
+ "instructions": "",
+ "restrictions": {},
+ }
+ )
+
+ # Terms of service field should also be present
+ self._assert_reg_field(
+ {"honor_code": "required", "terms_of_service": "required"},
+ {
+ "label": "I agree to the Terms of Service",
+ "name": "terms_of_service",
+ "default": False,
+ "type": "checkbox",
+ "required": True,
+ "placeholder": "",
+ "instructions": "",
+ "restrictions": {},
+ }
+ )
+
@override_settings(REGISTRATION_EXTRA_FIELDS={
"level_of_education": "optional",
"gender": "optional",
diff --git a/common/djangoapps/user_api/views.py b/common/djangoapps/user_api/views.py
index 3059c08168..7d891fae82 100644
--- a/common/djangoapps/user_api/views.py
+++ b/common/djangoapps/user_api/views.py
@@ -136,12 +136,31 @@ class RegistrationView(APIView):
EXTRA_FIELDS = [
"city", "country", "level_of_education", "gender",
"year_of_birth", "mailing_address", "goals",
- "honor_code",
+ "honor_code", "terms_of_service",
]
+ def _is_field_visible(self, field_name):
+ """Check whether a field is visible based on Django settings. """
+ return self._extra_fields_setting.get(field_name) in ["required", "optional"]
+
+ def _is_field_required(self, field_name):
+ """Check whether a field is required based on Django settings. """
+ return self._extra_fields_setting.get(field_name) == "required"
+
def __init__(self, *args, **kwargs):
super(RegistrationView, self).__init__(*args, **kwargs)
+ # Backwards compatibility: Honor code is required by default, unless
+ # explicitly set to "optional" in Django settings.
+ self._extra_fields_setting = copy.deepcopy(settings.REGISTRATION_EXTRA_FIELDS)
+ self._extra_fields_setting["honor_code"] = self._extra_fields_setting.get("honor_code", "required")
+
+ # Check that the setting is configured correctly
+ for field_name in self.EXTRA_FIELDS:
+ if self._extra_fields_setting.get(field_name, "hidden") not in ["required", "optional", "hidden"]:
+ msg = u"Setting REGISTRATION_EXTRA_FIELDS values must be either required, optional, or hidden."
+ raise ImproperlyConfigured(msg)
+
# Map field names to the instance method used to add the field to the form
self.field_handlers = {}
for field_name in (self.DEFAULT_FIELDS + self.EXTRA_FIELDS):
@@ -175,22 +194,14 @@ class RegistrationView(APIView):
for field_name in self.DEFAULT_FIELDS:
self.field_handlers[field_name](form_desc, required=True)
- # Backwards compatibility: Honor code is required by default, unless
- # explicitly set to "optional" in Django settings.
- extra_fields_setting = copy.deepcopy(settings.REGISTRATION_EXTRA_FIELDS)
- extra_fields_setting["honor_code"] = extra_fields_setting.get("honor_code", "required")
-
# Extra fields configured in Django settings
# may be required, optional, or hidden
for field_name in self.EXTRA_FIELDS:
- field_setting = extra_fields_setting.get(field_name, "hidden")
- handler = self.field_handlers[field_name]
-
- if field_setting in ["required", "optional"]:
- handler(form_desc, required=(field_setting == "required"))
- elif field_setting != "hidden":
- msg = u"Setting REGISTRATION_EXTRA_FIELDS values must be either required, optional, or hidden."
- raise ImproperlyConfigured(msg)
+ if self._is_field_visible(field_name):
+ self.field_handlers[field_name](
+ form_desc,
+ required=self._is_field_required(field_name)
+ )
return HttpResponse(form_desc.to_json(), content_type="application/json")
@@ -342,8 +353,14 @@ class RegistrationView(APIView):
)
def _add_honor_code_field(self, form_desc, required=True):
- # Translators: This is a legal document users must agree to in order to register a new account.
- terms_text = _(u"Terms of Service and Honor Code")
+ # Separate terms of service and honor code checkboxes
+ if self._is_field_visible("terms_of_service"):
+ terms_text = _(u"Honor Code")
+
+ # Combine terms of service and honor code checkboxes
+ else:
+ # Translators: This is a legal document users must agree to in order to register a new account.
+ terms_text = _(u"Terms of Service and Honor Code")
# Translators: "Terms of service" is a legal document users must agree to in order to register a new account.
label = _(
@@ -355,10 +372,6 @@ class RegistrationView(APIView):
)
)
- # TODO: Open source installations may need
- # separate checkboxes for terms or service or privacy
- # policies. Instead of hard-coding this, we should create a more flexible
- # mechanism for configuring which fields appear on the registration form.
form_desc.add_field(
"honor_code",
label=label,
@@ -367,6 +380,28 @@ class RegistrationView(APIView):
required=required,
)
+ def _add_terms_of_service_field(self, form_desc, required=True):
+ # Translators: This is a legal document users must agree to in order to register a new account.
+ terms_text = _(u"Terms of Service")
+
+ # Translators: "Terms of service" is a legal document users must agree to in order to register a new account.
+ label = _(
+ u"I agree to the {terms_of_service}"
+ ).format(
+ terms_of_service=u"{terms_text}".format(
+ url=marketing_link("TOS"),
+ terms_text=terms_text
+ )
+ )
+
+ form_desc.add_field(
+ "terms_of_service",
+ label=label,
+ field_type="checkbox",
+ default=False,
+ required=required,
+ )
+
def _options_with_default(self, options):
"""Include a default option as the first option. """
return (
diff --git a/lms/envs/common.py b/lms/envs/common.py
index c57ff34565..8ccc48ad0b 100644
--- a/lms/envs/common.py
+++ b/lms/envs/common.py
@@ -1536,6 +1536,7 @@ REGISTRATION_EXTRA_FIELDS = {
'mailing_address': 'optional',
'goals': 'optional',
'honor_code': 'required',
+ 'terms_of_service': 'hidden',
'city': 'hidden',
'country': 'hidden',
}