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', }