diff --git a/common/djangoapps/student/helpers.py b/common/djangoapps/student/helpers.py
index 35c6c1a7bb51..cfb047f1a947 100644
--- a/common/djangoapps/student/helpers.py
+++ b/common/djangoapps/student/helpers.py
@@ -30,6 +30,7 @@
Registration,
UserAttribute,
UserProfile,
+ SocialLink,
email_exists_or_retired,
unique_id_for_user,
username_exists_or_retired
@@ -740,7 +741,8 @@ def do_create_account(form, custom_form=None):
profile_fields = [
"name", "level_of_education", "gender", "mailing_address", "city", "country", "goals",
- "year_of_birth"
+ "year_of_birth", "national_id", "phone_number", "date_of_birth", "region", "address_line",
+ "english_language_level", "employment_status", "work_experience_level", "job_title", "terms_and_conditions"
]
profile = UserProfile(
user=user,
@@ -754,6 +756,12 @@ def do_create_account(form, custom_form=None):
except Exception:
log.exception(f"UserProfile creation failed for user {user.id}.")
raise
+
+ try:
+ linkedin_social_link = SocialLink(user_profile=profile, platform="linkedin", social_link=form.cleaned_data.get("linkedin_account"))
+ linkedin_social_link.save()
+ except Exception:
+ log.exception(f"SocialLink creation failed for user {user.id}.")
return user, profile, registration
diff --git a/common/djangoapps/student/migrations/0045_auto_20231005_0906.py b/common/djangoapps/student/migrations/0045_auto_20231005_0906.py
new file mode 100644
index 000000000000..a870e083e0b7
--- /dev/null
+++ b/common/djangoapps/student/migrations/0045_auto_20231005_0906.py
@@ -0,0 +1,58 @@
+# Generated by Django 3.2.20 on 2023-10-05 09:06
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('student', '0044_courseenrollmentcelebration_celebrate_weekly_goal'),
+ ]
+
+ operations = [
+ migrations.AddField(
+ model_name='userprofile',
+ name='address_line',
+ field=models.TextField(blank=True, null=True),
+ ),
+ migrations.AddField(
+ model_name='userprofile',
+ name='date_of_birth',
+ field=models.DateField(blank=True, default=None, null=True),
+ ),
+ migrations.AddField(
+ model_name='userprofile',
+ name='employment_status',
+ field=models.CharField(blank=True, choices=[('PU', 'Public industry'), ('PR', 'Private industry'), ('JS', 'Job seeker'), ('ST', 'Student')], max_length=3, null=True),
+ ),
+ migrations.AddField(
+ model_name='userprofile',
+ name='english_language_level',
+ field=models.CharField(blank=True, choices=[('0', '0'), ('1', '1'), ('2', '2'), ('3', '3'), ('4', '4'), ('5', '5'), ('6', '6'), ('7', '7'), ('8', '8'), ('9', '9')], max_length=2, null=True),
+ ),
+ migrations.AddField(
+ model_name='userprofile',
+ name='job_title',
+ field=models.CharField(blank=True, max_length=63, null=True),
+ ),
+ migrations.AddField(
+ model_name='userprofile',
+ name='national_id',
+ field=models.CharField(blank=True, max_length=63, null=True),
+ ),
+ migrations.AddField(
+ model_name='userprofile',
+ name='region',
+ field=models.CharField(blank=True, choices=[('RD', 'Riyadh'), ('ER', 'Eastern'), ('AI', 'Asir'), ('JA', 'Jazan'), ('MN', 'Medina'), ('AS', 'Al-Qassim'), ('TU', 'Tabuk'), ('HI', "Ha'il"), ('NA', 'Najran'), ('AW', 'Al-Jawf'), ('AA', 'Al-Bahah'), ('NB', 'Northern Borders')], max_length=3, null=True),
+ ),
+ migrations.AddField(
+ model_name='userprofile',
+ name='work_experience_level',
+ field=models.CharField(blank=True, choices=[('JL', 'Junior level (0-2) years'), ('ML', 'Middle level (3-4) years'), ('SL', 'Senior level (5-10) years'), ('EL', 'Expert (+ 10 years)')], max_length=3, null=True),
+ ),
+ migrations.AlterField(
+ model_name='userprofile',
+ name='level_of_education',
+ field=models.CharField(blank=True, choices=[('MS', 'Middle School'), ('HS', 'High School'), ('DM', 'Diploma'), ('BS', 'Bachelor'), ('MR', 'Master'), ('PH', 'Ph.D.')], db_index=True, max_length=3, null=True),
+ ),
+ ]
diff --git a/common/djangoapps/student/migrations/0046_alter_userprofile_english_language_level.py b/common/djangoapps/student/migrations/0046_alter_userprofile_english_language_level.py
new file mode 100644
index 000000000000..9ce480102adc
--- /dev/null
+++ b/common/djangoapps/student/migrations/0046_alter_userprofile_english_language_level.py
@@ -0,0 +1,18 @@
+# Generated by Django 3.2.20 on 2023-10-06 13:04
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('student', '0045_auto_20231005_0906'),
+ ]
+
+ operations = [
+ migrations.AlterField(
+ model_name='userprofile',
+ name='english_language_level',
+ field=models.CharField(blank=True, choices=[('0', '0'), ('1', '1'), ('2', '2'), ('3', '3'), ('4', '4'), ('5', '5'), ('6', '6'), ('7', '7'), ('8', '8'), ('9', '9'), ('10', '10')], max_length=2, null=True),
+ ),
+ ]
diff --git a/common/djangoapps/student/migrations/0047_userprofile_terms_and_conditions.py b/common/djangoapps/student/migrations/0047_userprofile_terms_and_conditions.py
new file mode 100644
index 000000000000..b57074c545c1
--- /dev/null
+++ b/common/djangoapps/student/migrations/0047_userprofile_terms_and_conditions.py
@@ -0,0 +1,18 @@
+# Generated by Django 3.2.21 on 2023-10-23 06:59
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('student', '0046_alter_userprofile_english_language_level'),
+ ]
+
+ operations = [
+ migrations.AddField(
+ model_name='userprofile',
+ name='terms_and_conditions',
+ field=models.BooleanField(default=True),
+ ),
+ ]
diff --git a/common/djangoapps/student/models/user.py b/common/djangoapps/student/models/user.py
index f29480e0ce02..bee8d4ce16b1 100644
--- a/common/djangoapps/student/models/user.py
+++ b/common/djangoapps/student/models/user.py
@@ -468,20 +468,15 @@ class Meta:
# ('p_se', 'Doctorate in science or engineering'),
# ('p_oth', 'Doctorate in another field'),
LEVEL_OF_EDUCATION_CHOICES = (
- ('p', gettext_noop('Doctorate')),
- ('m', gettext_noop("Master's or professional degree")),
- ('b', gettext_noop("Bachelor's degree")),
- ('a', gettext_noop("Associate degree")),
- ('hs', gettext_noop("Secondary/high school")),
- ('jhs', gettext_noop("Junior secondary/junior high/middle school")),
- ('el', gettext_noop("Elementary/primary school")),
- # Translators: 'None' refers to the student's level of education
- ('none', gettext_noop("No formal education")),
- # Translators: 'Other' refers to the student's level of education
- ('other', gettext_noop("Other education"))
+ ('MS', 'Middle School'),
+ ('HS', 'High School'),
+ ('DM', 'Diploma'),
+ ('BS', 'Bachelor'),
+ ('MR', 'Master'),
+ ('PH', 'Ph.D.'),
)
level_of_education = models.CharField(
- blank=True, null=True, max_length=6, db_index=True,
+ blank=True, null=True, max_length=3, db_index=True,
choices=LEVEL_OF_EDUCATION_CHOICES
)
mailing_address = models.TextField(blank=True, null=True)
@@ -551,6 +546,57 @@ class Meta:
phone_regex = RegexValidator(regex=r'^\+?1?\d*$', message="Phone number can only contain numbers.")
phone_number = models.CharField(validators=[phone_regex], blank=True, null=True, max_length=50)
+ # fields related to sdaia - nafath
+ REGION_CHOICES = (
+ ('RD', 'Riyadh'),
+ ('ER', 'Eastern'),
+ ('AI', 'Asir'),
+ ('JA', 'Jazan'),
+ ('MN', 'Medina'),
+ ('AS', 'Al-Qassim'),
+ ('TU', 'Tabuk'),
+ ('HI', "Ha'il"),
+ ('NA', 'Najran'),
+ ('AW', 'Al-Jawf'),
+ ('AA', 'Al-Bahah'),
+ ('NB', 'Northern Borders'),
+ )
+ EMPLOYMENT_STATUS_CHOICES = (
+ ('PU', 'Public industry'),
+ ('PR', 'Private industry'),
+ ('JS', 'Job seeker'),
+ ('ST', 'Student'),
+ )
+ WORK_EXPERIENCE_LEVEL_CHOICES = (
+ ('JL', 'Junior level (0-2) years'),
+ ('ML', 'Middle level (3-4) years'),
+ ('SL', 'Senior level (5-10) years'),
+ ('EL', 'Expert (+ 10 years)'),
+ )
+ ENGLISH_LANGUAGE_LEVEL_CHOICES = (
+ ('0', '0'),
+ ('1', '1'),
+ ('2', '2'),
+ ('3', '3'),
+ ('4', '4'),
+ ('5', '5'),
+ ('6', '6'),
+ ('7', '7'),
+ ('8', '8'),
+ ('9', '9'),
+ ('10', '10'),
+ )
+ national_id = models.CharField(blank=True, null=True, max_length=63)
+ date_of_birth = models.DateField(default=None, null=True, blank=True)
+ region = models.CharField(blank=True, null=True, max_length=3, choices=REGION_CHOICES)
+ address_line = models.TextField(blank=True, null=True)
+ english_language_level = models.CharField(blank=True, null=True, max_length=2, choices=ENGLISH_LANGUAGE_LEVEL_CHOICES)
+ employment_status = models.CharField(blank=True, null=True, max_length=3, choices=EMPLOYMENT_STATUS_CHOICES)
+ work_experience_level = models.CharField(blank=True, null=True, max_length=3, choices=WORK_EXPERIENCE_LEVEL_CHOICES)
+ job_title = models.CharField(blank=True, null=True, max_length=63)
+ terms_and_conditions = models.BooleanField(default=True)
+
+
@property
def has_profile_image(self):
"""
diff --git a/lms/envs/common.py b/lms/envs/common.py
index c0182d47355a..e57774643e01 100644
--- a/lms/envs/common.py
+++ b/lms/envs/common.py
@@ -4141,11 +4141,17 @@ def _make_locale_paths(settings): # pylint: disable=missing-function-docstring
'bio',
'course_certificates',
'country',
+ 'city',
+ 'region',
'date_joined',
'language_proficiencies',
- "level_of_education",
+ 'level_of_education',
'social_links',
'time_zone',
+ 'english_language_level',
+ 'employment_status',
+ 'work_experience_level',
+ 'job_title',
# Not an actual field, but used to signal whether badges should be public.
'accomplishments_shared',
@@ -4179,6 +4185,9 @@ def _make_locale_paths(settings): # pylint: disable=missing-function-docstring
"phone_number",
"activation_key",
"pending_name_change",
+ "national_id",
+ "date_of_birth",
+ "address_line",
]
)
diff --git a/lms/templates/nafath_openedx_integration/base.html b/lms/templates/nafath_openedx_integration/base.html
new file mode 100644
index 000000000000..d13a13ed43ab
--- /dev/null
+++ b/lms/templates/nafath_openedx_integration/base.html
@@ -0,0 +1,22 @@
+
+{% comment %}
+As the developer of this package, don't place anything here if you can help it
+since this allows developers to have interoperability between your template
+structure and their own.
+
+Example: Developer melding the 2SoD pattern to fit inside with another pattern::
+
+ {% extends "base.html" %}
+ {% load static %}
+
+
+ {% block extra_js %}
+
+
+ {% block javascript %}
+
+ {% endblock javascript %}
+
+ {% endblock extra_js %}
+{% endcomment %}
+
diff --git a/lms/templates/nafath_openedx_integration/edx_ace/useractivationmessage/email/body.html b/lms/templates/nafath_openedx_integration/edx_ace/useractivationmessage/email/body.html
new file mode 100644
index 000000000000..876d582afd7e
--- /dev/null
+++ b/lms/templates/nafath_openedx_integration/edx_ace/useractivationmessage/email/body.html
@@ -0,0 +1,13 @@
+
+
+{% load i18n %}
+{% load static %}
+{% block content %}
+
+ {% autoescape off %}
+ {# xss-lint: disable=django-blocktrans-missing-escape-filter #}
+ {% blocktrans %}This is one time OTP {{ activation_code }}. Kindly use this to complete nafath authentication with SDAIA.{% endblocktrans %}
+ {% endautoescape %}
+
+
+{% endblock %}
diff --git a/lms/templates/nafath_openedx_integration/edx_ace/useractivationmessage/email/body.txt b/lms/templates/nafath_openedx_integration/edx_ace/useractivationmessage/email/body.txt
new file mode 100644
index 000000000000..01ac74599df1
--- /dev/null
+++ b/lms/templates/nafath_openedx_integration/edx_ace/useractivationmessage/email/body.txt
@@ -0,0 +1,2 @@
+{% load i18n %}{% autoescape off %}{% blocktrans %}This is one time OTP {{ activation_code }}. Kindly use this to complete nafath authentication with SDAIA.{% endblocktrans %}
+{% endautoescape %}
diff --git a/lms/templates/nafath_openedx_integration/edx_ace/useractivationmessage/email/from_name.txt b/lms/templates/nafath_openedx_integration/edx_ace/useractivationmessage/email/from_name.txt
new file mode 100644
index 000000000000..dcbc23c00480
--- /dev/null
+++ b/lms/templates/nafath_openedx_integration/edx_ace/useractivationmessage/email/from_name.txt
@@ -0,0 +1 @@
+{{ platform_name }}
diff --git a/lms/templates/nafath_openedx_integration/edx_ace/useractivationmessage/email/head.html b/lms/templates/nafath_openedx_integration/edx_ace/useractivationmessage/email/head.html
new file mode 100644
index 000000000000..83d045ed4c69
--- /dev/null
+++ b/lms/templates/nafath_openedx_integration/edx_ace/useractivationmessage/email/head.html
@@ -0,0 +1 @@
+
diff --git a/lms/templates/nafath_openedx_integration/edx_ace/useractivationmessage/email/subject.txt b/lms/templates/nafath_openedx_integration/edx_ace/useractivationmessage/email/subject.txt
new file mode 100644
index 000000000000..80037dcd1a41
--- /dev/null
+++ b/lms/templates/nafath_openedx_integration/edx_ace/useractivationmessage/email/subject.txt
@@ -0,0 +1,4 @@
+{% load i18n %}
+{% autoescape off %}
+{% blocktrans %}SDAIA - Complete Nafath Authentication{% endblocktrans %}
+{% endautoescape %}
diff --git a/openedx/core/djangoapps/user_api/accounts/__init__.py b/openedx/core/djangoapps/user_api/accounts/__init__.py
index 84daeccdb8f2..35e804b34121 100644
--- a/openedx/core/djangoapps/user_api/accounts/__init__.py
+++ b/openedx/core/djangoapps/user_api/accounts/__init__.py
@@ -22,6 +22,16 @@
EMAIL_MIN_LENGTH = 3
EMAIL_MAX_LENGTH = 254 # Limit per RFCs is 254
+# sdaia related constants
+PHONE_NUMBER_MAX_LENGTH = 50
+NATIONAL_ID_MAX_LENGTH = 63
+LINKEDIN_ACCOUNT_MAX_LENGTH = 100
+REGION_MAX_LENGTH = 3
+ENGLISH_LANGUAGE_LEVEL_MAX_LENGTH = 2
+EMPLOYMENT_STATUS_MAX_LENGTH = 3
+WORK_EXPERIENCE_LEVEL_MAX_LENGTH = 3
+JOB_TITLE_MAX_LENGTH = 63
+
ACCOUNT_VISIBILITY_PREF_KEY = 'account_privacy'
# Indicates the user's preference that all users can view the shareable fields in their account information.
diff --git a/openedx/core/djangoapps/user_api/accounts/serializers.py b/openedx/core/djangoapps/user_api/accounts/serializers.py
index 75ecfa35bf2b..2aef5a32409d 100644
--- a/openedx/core/djangoapps/user_api/accounts/serializers.py
+++ b/openedx/core/djangoapps/user_api/accounts/serializers.py
@@ -171,6 +171,15 @@ def to_representation(self, user): # lint-amnesty, pylint: disable=arguments-di
"phone_number": None,
"pending_name_change": None,
"verified_name": None,
+ "national_id":None,
+ "date_of_birth": None,
+ "city": None,
+ "region": None,
+ "address_line": None,
+ "english_language_level": None,
+ "employment_status": None,
+ "work_experience_level": None,
+ "job_title": None,
}
if user_profile:
@@ -200,6 +209,15 @@ def to_representation(self, user): # lint-amnesty, pylint: disable=arguments-di
).data,
"extended_profile": get_extended_profile(user_profile),
"phone_number": user_profile.phone_number,
+ "national_id": AccountLegacyProfileSerializer.convert_empty_to_None(user_profile.national_id),
+ "date_of_birth": AccountLegacyProfileSerializer.convert_empty_to_None(user_profile.date_of_birth),
+ "city": AccountLegacyProfileSerializer.convert_empty_to_None(user_profile.city),
+ "region": AccountLegacyProfileSerializer.convert_empty_to_None(user_profile.region),
+ "address_line": AccountLegacyProfileSerializer.convert_empty_to_None(user_profile.address_line),
+ "english_language_level": AccountLegacyProfileSerializer.convert_empty_to_None(user_profile.english_language_level),
+ "employment_status": AccountLegacyProfileSerializer.convert_empty_to_None(user_profile.employment_status),
+ "work_experience_level": AccountLegacyProfileSerializer.convert_empty_to_None(user_profile.work_experience_level),
+ "job_title": AccountLegacyProfileSerializer.convert_empty_to_None(user_profile.job_title),
}
)
@@ -289,7 +307,8 @@ class Meta:
fields = (
"name", "gender", "goals", "year_of_birth", "level_of_education", "country", "state", "social_links",
"mailing_address", "bio", "profile_image", "requires_parental_consent", "language_proficiencies",
- "phone_number", "city"
+ "phone_number", "city", "date_of_birth", "region", "city", "address_line", "english_language_level",
+ "employment_status", "work_experience_level", "job_title"
)
# Currently no read-only field, but keep this so view code doesn't need to know.
read_only_fields = ()
diff --git a/openedx/core/djangoapps/user_authn/views/login.py b/openedx/core/djangoapps/user_authn/views/login.py
index 79cb78a2727c..472790859e4c 100644
--- a/openedx/core/djangoapps/user_authn/views/login.py
+++ b/openedx/core/djangoapps/user_authn/views/login.py
@@ -129,6 +129,8 @@ def _get_user_by_email_or_username(request, api_version):
login_fields = ['email', 'password']
if is_api_v2:
login_fields = ['email_or_username', 'password']
+ if request.POST.get('is_nafath_user', False):
+ login_fields.remove('password')
if any(f not in request.POST.keys() for f in login_fields):
raise AuthFailedError(_('There was an error receiving your login information. Please email us.'))
@@ -239,6 +241,11 @@ def _authenticate_first_party(request, unauthenticated_user, third_party_auth_re
if not third_party_auth_requested:
_check_user_auth_flow(request.site, unauthenticated_user)
+ if request.POST.get('is_nafath_user', False):
+ return authenticate(
+ username=username,
+ request=request
+ )
password = normalize_password(request.POST['password'])
return authenticate(
username=username,
diff --git a/openedx/core/djangoapps/user_authn/views/register.py b/openedx/core/djangoapps/user_authn/views/register.py
index c494f8e015c2..30e850ce57ce 100644
--- a/openedx/core/djangoapps/user_authn/views/register.py
+++ b/openedx/core/djangoapps/user_authn/views/register.py
@@ -164,6 +164,22 @@ def create_account_with_params(request, params): # pylint: disable=too-many-sta
'REGISTRATION_EXTRA_FIELDS',
getattr(settings, 'REGISTRATION_EXTRA_FIELDS', {})
)
+ extra_fields["phone_number"] = "required"
+ extra_fields["national_id"] = "optional"
+ extra_fields["linkedin_account"] = "optional"
+ extra_fields["date_of_birth"] = "required"
+ extra_fields["year_of_birth"] = "required"
+ extra_fields["gender"] = "required"
+ extra_fields["region"] = "required"
+ extra_fields["city"] = "required"
+ extra_fields["address_line"] = "optional"
+ extra_fields["level_of_education"] = "required"
+ extra_fields["english_language_level"] = "optional"
+ extra_fields["employment_status"] = "required"
+ extra_fields["work_experience_level"] = "required"
+ extra_fields["job_title"] = "required"
+ extra_fields["terms_and_conditions"] = "required"
+
if is_registration_api_v1(request):
if 'confirm_email' in extra_fields:
del extra_fields['confirm_email']
@@ -575,6 +591,7 @@ def post(self, request):
)
data = request.POST.copy()
+ data["terms_and_conditions"] = True if data.get("terms_and_conditions")=="true" else False
self._handle_terms_of_service(data)
try:
diff --git a/openedx/core/djangoapps/user_authn/views/registration_form.py b/openedx/core/djangoapps/user_authn/views/registration_form.py
index e503265d1e68..42a4c481de4b 100644
--- a/openedx/core/djangoapps/user_authn/views/registration_form.py
+++ b/openedx/core/djangoapps/user_authn/views/registration_form.py
@@ -197,7 +197,10 @@ def __init__(
"mailing_address": _("Your mailing address is required"),
"goals": _("A description of your goals is required"),
"city": _("A city is required"),
- "country": _("A country is required")
+ "country": _("A country is required"),
+ "phone_number": _("Your phone number is required"),
+ "date_of_birth": _("Your date of birth is required"),
+ "gender": _("Your gender is required"),
}
for field_name, field_value in extra_fields.items():
if field_name not in self.fields:
@@ -349,6 +352,16 @@ def __init__(self):
"profession",
"specialty",
"marketing_emails_opt_in",
+ "phone_number",
+ "national_id",
+ "linkedin_account",
+ "date_of_birth",
+ "region",
+ "address_line",
+ "english_language_level",
+ "employment_status",
+ "work_experience_level",
+ "job_title",
]
if settings.ENABLE_COPPA_COMPLIANCE and 'year_of_birth' in self.EXTRA_FIELDS:
@@ -552,6 +565,252 @@ def _add_name_field(self, form_desc, required=True):
required=required
)
+ def _add_phone_number_field(self, form_desc, required=True):
+ """Add a phone number 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 hold the user's phone number.
+ phone_number_label = _("Phone Number")
+
+ # Translators: These instructions appear on the registration form, immediately
+ # below a field meant to hold the user's phone number.
+ phone_number_instructions = _("This number will be used to contact you.")
+
+ form_desc.add_field(
+ "phone_number",
+ label=phone_number_label,
+ instructions=phone_number_instructions,
+ restrictions={
+ "max_length": accounts.PHONE_NUMBER_MAX_LENGTH,
+ },
+ required=required
+ )
+
+ def _add_job_title_field(self, form_desc, required=True):
+ """Add a job title 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 hold the user's job title.
+ job_title_label = _("Job Title")
+
+ # Translators: These instructions appear on the registration form, immediately
+ # below a field meant to hold the user's job title.
+ job_title_instructions = _("This is the title of your current Job")
+
+ form_desc.add_field(
+ "job_title",
+ label=job_title_label,
+ instructions=job_title_instructions,
+ restrictions={
+ "max_length": accounts.JOB_TITLE_MAX_LENGTH,
+ },
+ required=required
+ )
+
+ def _add_work_experience_level_field(self, form_desc, required=True):
+ """Add a work experience level 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 hold the user's work experience level.
+ work_experience_level_label = _("Work Experience Level")
+
+ # Translators: These instructions appear on the registration form, immediately
+ # below a field meant to hold the user's work experience level.
+ work_experience_level_instructions = _("This show you work experience level")
+
+ form_desc.add_field(
+ "work_experience_level",
+ label=work_experience_level_label,
+ instructions=work_experience_level_instructions,
+ restrictions={
+ "max_length": accounts.WORK_EXPERIENCE_LEVEL_MAX_LENGTH,
+ },
+ required=required
+ )
+
+ def _add_employment_status_field(self, form_desc, required=True):
+ """Add a employment status 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 hold the user's employment status.
+ employment_status_label = _("Employment Status")
+
+ # Translators: These instructions appear on the registration form, immediately
+ # below a field meant to hold the user's employment status.
+ employment_status_instructions = _("This shows your employment status")
+
+ form_desc.add_field(
+ "employment_status",
+ label=employment_status_label,
+ instructions=employment_status_instructions,
+ restrictions={
+ "max_length": accounts.EMPLOYMENT_STATUS_MAX_LENGTH,
+ },
+ required=required
+ )
+
+ def _add_national_id_field(self, form_desc, required=False):
+ """Add a national id field to a form description.
+ Arguments:
+ form_desc: A form description
+ Keyword Arguments:
+ required (bool): Whether this field is required; defaults to False
+ """
+ # Translators: This label appears above a field on the registration form
+ # meant to hold the user's national id.
+ national_id_label = _("National Id")
+
+ # Translators: These instructions appear on the registration form, immediately
+ # below a field meant to hold the user's national id.
+ national_id_instructions = _("This field is optional, add your national id here.")
+
+ form_desc.add_field(
+ "national_id",
+ label=national_id_label,
+ instructions=national_id_instructions,
+ restrictions={
+ "max_length": accounts.NATIONAL_ID_MAX_LENGTH,
+ },
+ required=required
+ )
+
+ def _add_linkedin_account_field(self, form_desc, required=False):
+ """Add a linkedin account field to a form description.
+ Arguments:
+ form_desc: A form description
+ Keyword Arguments:
+ required (bool): Whether this field is required; defaults to False
+ """
+ # Translators: This label appears above a field on the registration form
+ # meant to hold the user's linkedin account.
+ linkedin_account_label = _("LinkedIn Account")
+
+ # Translators: These instructions appear on the registration form, immediately
+ # below a field meant to hold the user's linkedin account.
+ linkedin_account_instructions = _("This field is optional, add your linkedin account link here.")
+
+ form_desc.add_field(
+ "linkedin_account",
+ label=linkedin_account_label,
+ instructions=linkedin_account_instructions,
+ restrictions={
+ "max_length": accounts.LINKEDIN_ACCOUNT_MAX_LENGTH,
+ },
+ required=required
+ )
+
+ def _add_english_language_level_field(self, form_desc, required=False):
+ """Add a english language level field to a form description.
+ Arguments:
+ form_desc: A form description
+ Keyword Arguments:
+ required (bool): Whether this field is required; defaults to False
+ """
+ # Translators: This label appears above a field on the registration form
+ # meant to hold the user's english language level.
+ english_language_level_label = _("English Language Level")
+
+ # Translators: These instructions appear on the registration form, immediately
+ # below a field meant to hold the user's english language level.
+ english_language_level_instructions = _("This field is optional, select your english language level link here.")
+
+ form_desc.add_field(
+ "english_language_level",
+ label=english_language_level_label,
+ instructions=english_language_level_instructions,
+ restrictions={
+ "max_length": accounts.ENGLISH_LANGUAGE_LEVEL_MAX_LENGTH,
+ },
+ required=required
+ )
+
+ def _add_address_line_field(self, form_desc, required=False):
+ """Add a address line field to a form description.
+ Arguments:
+ form_desc: A form description
+ Keyword Arguments:
+ required (bool): Whether this field is required; defaults to False
+ """
+ # Translators: This label appears above a field on the registration form
+ # meant to hold the user's address line.
+ address_line_label = _("Address Line")
+
+ # Translators: These instructions appear on the registration form, immediately
+ # below a field meant to hold the user's address line.
+ address_line_instructions = _("This field is optional, add your address line here.")
+
+ form_desc.add_field(
+ "address_line",
+ label=address_line_label,
+ instructions=address_line_instructions,
+ restrictions={},
+ required=required
+ )
+
+ def _add_date_of_birth_field(self, form_desc, required=True):
+ """Add a date of birth field to a form description.
+ Arguments:
+ form_desc: A form description
+ Keyword Arguments:
+ required (datefield): Whether this field is required; defaults to True
+ """
+ # Translators: This label appears above a field on the registration form
+ # meant to hold the user's date of birth.
+ date_of_birth_label = _("Date of birth")
+
+ # Translators: These instructions appear on the registration form, immediately
+ # below a field meant to hold the user's date of birth.
+ date_of_birth_instructions = _("This field is required, add your date of birth here.")
+
+ form_desc.add_field(
+ "date_of_birth",
+ label=date_of_birth_label,
+ instructions=date_of_birth_instructions,
+ restrictions={},
+ required=required
+ )
+
+ def _add_region_field(self, form_desc, required=True):
+ """Add a region field to a form description.
+ Arguments:
+ form_desc: A form description
+ Keyword Arguments:
+ required (datefield): Whether this field is required; defaults to True
+ """
+ # Translators: This label appears above a field on the registration form
+ # meant to hold the user's region.
+ region_label = _("Date of birth")
+
+ # Translators: These instructions appear on the registration form, immediately
+ # below a field meant to hold the user's region.
+ region_instructions = _("This field is required, add your region here.")
+
+ form_desc.add_field(
+ "region",
+ label=region_label,
+ instructions=region_instructions,
+ restrictions={
+ "max_length": accounts.REGION_MAX_LENGTH,
+ },
+ required=required
+ )
+
def _add_username_field(self, form_desc, required=True):
"""Add a username field to a form description.
Arguments: