Merge pull request #14920 from edx/bexline/registration_email
ENT-304 Add confirm email field to registration
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -2419,6 +2419,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',
|
||||
@@ -2430,6 +2431,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 = {
|
||||
@@ -61,6 +62,16 @@
|
||||
instructions: 'Enter your email.',
|
||||
restrictions: {}
|
||||
},
|
||||
{
|
||||
placeholder: '',
|
||||
name: 'confirm_email',
|
||||
label: 'Confirm Email',
|
||||
defaultValue: '',
|
||||
type: 'text',
|
||||
required: true,
|
||||
instructions: 'Enter your email.',
|
||||
restrictions: {}
|
||||
},
|
||||
{
|
||||
placeholder: 'Jane Doe',
|
||||
name: '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.
|
||||
|
||||
|
||||
Reference in New Issue
Block a user