Commit c2df64c3 by Brittney Exline

ENT-304 Add confirm email field to registration

parent f4e72c80
......@@ -168,6 +168,7 @@ AWS_SES_REGION_ENDPOINT = ENV_TOKENS.get('AWS_SES_REGION_ENDPOINT', 'email.us-ea
REGISTRATION_EXTRA_FIELDS = ENV_TOKENS.get('REGISTRATION_EXTRA_FIELDS', REGISTRATION_EXTRA_FIELDS)
REGISTRATION_EXTENSION_FORM = ENV_TOKENS.get('REGISTRATION_EXTENSION_FORM', REGISTRATION_EXTENSION_FORM)
REGISTRATION_EMAIL_PATTERNS_ALLOWED = ENV_TOKENS.get('REGISTRATION_EMAIL_PATTERNS_ALLOWED')
REGISTRATION_FIELD_ORDER = ENV_TOKENS.get('REGISTRATION_FIELD_ORDER', REGISTRATION_FIELD_ORDER)
# Set the names of cookies shared with the marketing site
# These have the same cookie domain as the session, which in production
......
......@@ -2417,6 +2417,7 @@ XDOMAIN_PROXY_CACHE_TIMEOUT = 60 * 15
# - 'hidden': to not display the field
REGISTRATION_EXTRA_FIELDS = {
'confirm_email': 'hidden',
'level_of_education': 'optional',
'gender': 'optional',
'year_of_birth': 'optional',
......@@ -2428,6 +2429,28 @@ REGISTRATION_EXTRA_FIELDS = {
'country': 'hidden',
}
REGISTRATION_FIELD_ORDER = [
"email",
"confirm_email",
"name",
"username",
"password",
"first_name",
"last_name",
"city",
"state",
"country",
"gender",
"year_of_birth",
"level_of_education",
"company",
"title",
"mailing_address",
"goals",
"honor_code",
"terms_of_service",
]
# Optional setting to restrict registration / account creation to only emails
# that match a regex in this list. Set to None to allow any email (default).
REGISTRATION_EMAIL_PATTERNS_ALLOWED = None
......
......@@ -26,6 +26,7 @@
year_of_birth: 2014,
mailing_address: '141 Portland',
goals: 'To boldly learn what no letter of the alphabet has learned before',
confirm_email: 'xsy@edx.org',
honor_code: true
},
THIRD_PARTY_AUTH = {
......@@ -62,6 +63,16 @@
restrictions: {}
},
{
placeholder: '',
name: 'confirm_email',
label: 'Confirm Email',
defaultValue: '',
type: 'text',
required: true,
instructions: 'Enter your email.',
restrictions: {}
},
{
placeholder: 'Jane Doe',
name: 'name',
label: 'Full Name',
......@@ -203,6 +214,7 @@
// Simulate manual entry of registration form data
$('#register-email').val(USER_DATA.email);
$('#register-confirm_email').val(USER_DATA.email);
$('#register-name').val(USER_DATA.name);
$('#register-username').val(USER_DATA.username);
$('#register-password').val(USER_DATA.password);
......
......@@ -127,6 +127,37 @@
jsHook: this.authWarningJsHook,
message: fullMsg
});
},
getFormData: function() {
var obj = FormView.prototype.getFormData.apply(this, arguments),
$form = this.$form,
$label,
$emailElement,
$confirmEmailElement,
email = '',
confirmEmail = '';
$emailElement = $form.find('input[name=email]');
$confirmEmailElement = $form.find('input[name=confirm_email]');
if ($confirmEmailElement.length) {
email = $emailElement.val();
confirmEmail = $confirmEmailElement.val();
$label = $form.find('label[for=' + $confirmEmailElement.attr('id') + ']');
if (confirmEmail !== '' && email !== confirmEmail) {
this.errors.push('<li>' + $confirmEmailElement.data('errormsg-required') + '</li>');
$confirmEmailElement.addClass('error');
$label.addClass('error');
} else if (confirmEmail !== '') {
obj.confirm_email = confirmEmail;
$confirmEmailElement.removeClass('error');
$label.removeClass('error');
}
}
return obj;
}
});
});
......
......@@ -1188,6 +1188,20 @@ class RegistrationViewTest(ThirdPartyAuthTestMixin, UserAPITestCase):
}
)
def test_registration_form_confirm_email(self):
self._assert_reg_field(
{"confirm_email": "required"},
{
"name": "confirm_email",
"type": "text",
"required": True,
"label": "Confirm Email",
"errorMessages": {
"required": "Please confirm your email.",
}
}
)
@override_settings(
MKTG_URLS={"ROOT": "https://www.test.com/", "HONOR": "honor"},
)
......@@ -1343,6 +1357,7 @@ class RegistrationViewTest(ThirdPartyAuthTestMixin, UserAPITestCase):
"state": "optional",
"country": "required",
"honor_code": "required",
"confirm_email": "required",
},
REGISTRATION_EXTENSION_FORM='openedx.core.djangoapps.user_api.tests.test_helpers.TestCaseForm',
)
......@@ -1360,6 +1375,123 @@ class RegistrationViewTest(ThirdPartyAuthTestMixin, UserAPITestCase):
"password",
"favorite_movie",
"favorite_editor",
"confirm_email",
"city",
"state",
"country",
"gender",
"year_of_birth",
"level_of_education",
"mailing_address",
"goals",
"honor_code",
])
@override_settings(
REGISTRATION_EXTRA_FIELDS={
"level_of_education": "optional",
"gender": "optional",
"year_of_birth": "optional",
"mailing_address": "optional",
"goals": "optional",
"city": "optional",
"state": "optional",
"country": "required",
"honor_code": "required",
"confirm_email": "required",
},
REGISTRATION_FIELD_ORDER=[
"name",
"username",
"email",
"confirm_email",
"password",
"first_name",
"last_name",
"city",
"state",
"country",
"gender",
"year_of_birth",
"level_of_education",
"company",
"title",
"mailing_address",
"goals",
"honor_code",
"terms_of_service",
],
)
def test_field_order_override(self):
response = self.client.get(self.url)
self.assertHttpOK(response)
# Verify that all fields render in the correct order
form_desc = json.loads(response.content)
field_names = [field["name"] for field in form_desc["fields"]]
self.assertEqual(field_names, [
"name",
"username",
"email",
"confirm_email",
"password",
"city",
"state",
"country",
"gender",
"year_of_birth",
"level_of_education",
"mailing_address",
"goals",
"honor_code",
])
@override_settings(
REGISTRATION_EXTRA_FIELDS={
"level_of_education": "optional",
"gender": "optional",
"year_of_birth": "optional",
"mailing_address": "optional",
"goals": "optional",
"city": "optional",
"state": "optional",
"country": "required",
"honor_code": "required",
"confirm_email": "required",
},
REGISTRATION_EXTENSION_FORM='openedx.core.djangoapps.user_api.tests.test_helpers.TestCaseForm',
REGISTRATION_FIELD_ORDER=[
"name",
"confirm_email",
"password",
"first_name",
"last_name",
"gender",
"year_of_birth",
"level_of_education",
"company",
"title",
"mailing_address",
"goals",
"honor_code",
"terms_of_service",
],
)
def test_field_order_invalid_override(self):
response = self.client.get(self.url)
self.assertHttpOK(response)
# Verify that all fields render in the correct order
form_desc = json.loads(response.content)
field_names = [field["name"] for field in form_desc["fields"]]
self.assertEqual(field_names, [
"email",
"name",
"username",
"password",
"favorite_movie",
"favorite_editor",
"confirm_email",
"city",
"state",
"country",
......
......@@ -160,6 +160,7 @@ class RegistrationView(APIView):
DEFAULT_FIELDS = ["email", "name", "username", "password"]
EXTRA_FIELDS = [
"confirm_email",
"first_name",
"last_name",
"city",
......@@ -206,10 +207,21 @@ class RegistrationView(APIView):
# 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:
valid_fields = self.DEFAULT_FIELDS + self.EXTRA_FIELDS
for field_name in valid_fields:
handler = getattr(self, "_add_{field_name}_field".format(field_name=field_name))
self.field_handlers[field_name] = handler
field_order = configuration_helpers.get_value('REGISTRATION_FIELD_ORDER')
if not field_order:
field_order = settings.REGISTRATION_FIELD_ORDER or valid_fields
# Check that all of the valid_fields are in the field order and vice versa, if not set to the default order
if set(valid_fields) != set(field_order):
field_order = valid_fields
self.field_order = field_order
@method_decorator(ensure_csrf_cookie)
def get(self, request):
"""Return a description of the registration form.
......@@ -235,14 +247,14 @@ class RegistrationView(APIView):
form_desc = FormDescription("post", reverse("user_api_registration"))
self._apply_third_party_auth_overrides(request, form_desc)
# Default fields are always required
for field_name in self.DEFAULT_FIELDS:
self.field_handlers[field_name](form_desc, required=True)
# Custom form fields can be added via the form set in settings.REGISTRATION_EXTENSION_FORM
custom_form = get_registration_extension_form()
if custom_form:
# Default fields are always required
for field_name in self.DEFAULT_FIELDS:
self.field_handlers[field_name](form_desc, required=True)
for field_name, field in custom_form.fields.items():
restrictions = {}
if getattr(field, 'max_length', None):
......@@ -270,14 +282,24 @@ class RegistrationView(APIView):
include_default_option=field_options.get('include_default_option'),
)
# Extra fields configured in Django settings
# may be required, optional, or hidden
for field_name in self.EXTRA_FIELDS:
if self._is_field_visible(field_name):
self.field_handlers[field_name](
form_desc,
required=self._is_field_required(field_name)
)
# Extra fields configured in Django settings
# may be required, optional, or hidden
for field_name in self.EXTRA_FIELDS:
if self._is_field_visible(field_name):
self.field_handlers[field_name](
form_desc,
required=self._is_field_required(field_name)
)
else:
# Go through the fields in the fields order and add them if they are required or visible
for field_name in self.field_order:
if field_name in self.DEFAULT_FIELDS:
self.field_handlers[field_name](form_desc, required=True)
elif 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")
......@@ -386,6 +408,30 @@ class RegistrationView(APIView):
required=required
)
def _add_confirm_email_field(self, form_desc, required=True):
"""Add an email confirmation field to a form description.
Arguments:
form_desc: A form description
Keyword Arguments:
required (bool): Whether this field is required; defaults to True
"""
# Translators: This label appears above a field on the registration form
# meant to confirm the user's email address.
email_label = _(u"Confirm Email")
error_msg = _(u"Please confirm your email.")
form_desc.add_field(
"confirm_email",
label=email_label,
required=required,
error_messages={
"required": error_msg
}
)
def _add_name_field(self, form_desc, required=True):
"""Add a name field to a form description.
......
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