diff --git a/ckanext-hdx_theme/ckanext/hdx_theme/helpers/ui_constants/onboarding/__init__.py b/ckanext-hdx_theme/ckanext/hdx_theme/helpers/ui_constants/onboarding/__init__.py
index f014d2a4d0..7c2eca3aae 100644
--- a/ckanext-hdx_theme/ckanext/hdx_theme/helpers/ui_constants/onboarding/__init__.py
+++ b/ckanext-hdx_theme/ckanext/hdx_theme/helpers/ui_constants/onboarding/__init__.py
@@ -1,6 +1,7 @@
from ckanext.hdx_theme.helpers.ui_constants.onboarding.user_info import CONSTANTS as USER_INFO_CONSTANTS
from ckanext.hdx_theme.helpers.ui_constants.onboarding.value_proposition import CONSTANTS as VALUE_PROPOSITION_CONSTANTS
from ckanext.hdx_theme.helpers.ui_constants.onboarding.verify_email import CONSTANTS as VERIFY_EMAIL_CONSTANTS
+from ckanext.hdx_theme.helpers.ui_constants.onboarding.change_email import CONSTANTS as CHANGE_EMAIL_CONSTANTS
from ckanext.hdx_theme.helpers.ui_constants.onboarding.account_validated import CONSTANTS as ACCOUNT_VALIDATED_CONSTANTS
from ckanext.hdx_theme.helpers.ui_constants.onboarding.request_join_organisation import CONSTANTS as REQUEST_JOIN_ORGANISATION_CONSTANTS
from ckanext.hdx_theme.helpers.ui_constants.onboarding.confirm_organisation_choice import CONSTANTS as CONFIRM_ORGANISATION_CHOICE_CONSTANTS
@@ -15,6 +16,7 @@
'USER_INFO': USER_INFO_CONSTANTS,
'VALUE_PROPOSITION': VALUE_PROPOSITION_CONSTANTS,
'VERIFY_EMAIL': VERIFY_EMAIL_CONSTANTS,
+ 'CHANGE_EMAIL': CHANGE_EMAIL_CONSTANTS,
'ACCOUNT_VALIDATED': ACCOUNT_VALIDATED_CONSTANTS,
'REQUEST_JOIN_ORGANISATION': REQUEST_JOIN_ORGANISATION_CONSTANTS,
'CONFIRM_ORGANISATION_CHOICE': CONFIRM_ORGANISATION_CHOICE_CONSTANTS,
diff --git a/ckanext-hdx_theme/ckanext/hdx_theme/helpers/ui_constants/onboarding/change_email.py b/ckanext-hdx_theme/ckanext/hdx_theme/helpers/ui_constants/onboarding/change_email.py
new file mode 100644
index 0000000000..cb4ec111a0
--- /dev/null
+++ b/ckanext-hdx_theme/ckanext/hdx_theme/helpers/ui_constants/onboarding/change_email.py
@@ -0,0 +1,18 @@
+from ckanext.hdx_theme.helpers.ui_constants.onboarding.user_info import CONSTANTS as USER_INFO_CONSTANTS
+
+CONSTANTS = {
+ 'STEPS_1': USER_INFO_CONSTANTS['STEPS_1'],
+ 'STEPS_2': USER_INFO_CONSTANTS['STEPS_2'],
+ 'STEPS_3': USER_INFO_CONSTANTS['STEPS_3'],
+
+ 'PAGE_TITLE': '''Re-enter your email address''',
+
+ 'INPUT_EMAIL_LABEL': '''Enter your email address''',
+ 'INPUT_EMAIL_PLACEHOLDER': USER_INFO_CONSTANTS['INPUT_EMAIL_PLACEHOLDER'],
+ 'INPUT_EMAIL_ERROR': USER_INFO_CONSTANTS['INPUT_EMAIL_ERROR'],
+ 'INPUT_EMAIL2_LABEL': USER_INFO_CONSTANTS['INPUT_EMAIL2_LABEL'],
+ 'INPUT_EMAIL2_PLACEHOLDER': USER_INFO_CONSTANTS['INPUT_EMAIL2_PLACEHOLDER'],
+ 'INPUT_EMAIL2_ERROR': USER_INFO_CONSTANTS['INPUT_EMAIL2_ERROR'],
+
+ 'BUTTON_SUBMIT': '''Next''',
+}
diff --git a/ckanext-hdx_theme/ckanext/hdx_theme/helpers/ui_constants/onboarding/verify_email.py b/ckanext-hdx_theme/ckanext/hdx_theme/helpers/ui_constants/onboarding/verify_email.py
index 1ef9bf23f1..7f9a5280dd 100644
--- a/ckanext-hdx_theme/ckanext/hdx_theme/helpers/ui_constants/onboarding/verify_email.py
+++ b/ckanext-hdx_theme/ckanext/hdx_theme/helpers/ui_constants/onboarding/verify_email.py
@@ -7,5 +7,7 @@
'PAGE_TITLE': '''Verify your email address''',
'BODY_MAIN_TEXT': '''We have sent an email to {0} so that you can verify your email address. If you don’t see the email, please check your spam or junk folder.''',
- 'BODY_MAIN_TEXT_WITHOUT_EMAIL': '''We have sent an email so that you can verify your email address. If you don’t see the email, please check your spam or junk folder.'''
+ 'BODY_MAIN_TEXT_WITHOUT_EMAIL': '''We have sent an email so that you can verify your email address. If you don’t see the email, please check your spam or junk folder.''',
+
+ 'CHANGE_EMAIL_TEXT': '''If you need to change your email address, click here''',
}
diff --git a/ckanext-hdx_theme/ckanext/hdx_theme/templates/onboarding/signup/change-email.html b/ckanext-hdx_theme/ckanext/hdx_theme/templates/onboarding/signup/change-email.html
new file mode 100644
index 0000000000..0fda8ce6dd
--- /dev/null
+++ b/ckanext-hdx_theme/ckanext/hdx_theme/templates/onboarding/signup/change-email.html
@@ -0,0 +1,42 @@
+{% extends "onboarding/base.html" %}
+
+{% block scripts %}
+ {{ super() }}
+ {% asset 'hdx_theme/hdx-form-validator' %}
+{% endblock %}
+
+{% set CONST = h.HDX_CONST('UI_CONSTANTS')['ONBOARDING']['CHANGE_EMAIL'] %}
+
+{% block subtitle %}{{ _('Re-enter email address') }}{% endblock %}
+
+{% block breadcrumb_content %}
+
{{ h.nav_link(_('Sign up'), named_route='hdx_user_onboarding.user-info') }}
+{% endblock %}
+
+{% block content %}
+
+
+
+
+
+ {{ h.snippet('bem.blocks/stepper.html', steps=[CONST.STEPS_1, CONST.STEPS_2, CONST.STEPS_3], spacing_class="my-5", current_step=2) }}
+
+ {{ h.snippet('bem.blocks/heading.html', title=CONST.PAGE_TITLE, spacing_class="mb-4") }}
+
+
+
+
+
+
+
+{% endblock %}
diff --git a/ckanext-hdx_theme/ckanext/hdx_theme/templates/onboarding/signup/verify-email.html b/ckanext-hdx_theme/ckanext/hdx_theme/templates/onboarding/signup/verify-email.html
index 4b15b4bddc..25e794440b 100644
--- a/ckanext-hdx_theme/ckanext/hdx_theme/templates/onboarding/signup/verify-email.html
+++ b/ckanext-hdx_theme/ckanext/hdx_theme/templates/onboarding/signup/verify-email.html
@@ -24,6 +24,10 @@
{{ h.snippet('bem.blocks/heading.html', title=CONST.PAGE_TITLE, spacing_class="mb-5") }}
{% set main_text = CONST.BODY_MAIN_TEXT.format(session.get('user_info_email')) if session.get('user_info_email') else CONST.BODY_MAIN_TEXT_WITHOUT_EMAIL %}
{{ h.snippet('bem.blocks/paragraph.html', text=main_text, paragraph_classes=["paragraph__text_font-size-big"], spacing_class="mb-5") }}
+ {% if session.get('user_info_id') %}
+ {{ h.snippet('bem.blocks/paragraph.html', text='--', spacing_class="mb-5") }}
+ {{ h.snippet('bem.blocks/paragraph.html', text=CONST.CHANGE_EMAIL_TEXT.format(h.url_for('hdx_user_onboarding.change_email')), spacing_class="mb-5") }}
+ {% endif %}
diff --git a/ckanext-hdx_users/ckanext/hdx_users/logic/schema.py b/ckanext-hdx_users/ckanext/hdx_users/logic/schema.py
index 7383ed74a0..cc6b0c709c 100644
--- a/ckanext-hdx_users/ckanext/hdx_users/logic/schema.py
+++ b/ckanext-hdx_users/ckanext/hdx_users/logic/schema.py
@@ -98,3 +98,12 @@ def onboarding_user_edit_form_schema(not_empty, strip_value, user_email_validato
schema['email'] = [not_empty, strip_value, user_email_validator, unicode_safe]
return schema
+
+
+@validator_args
+def onboarding_user_change_email_form_schema(not_empty, strip_value, user_email_validator, user_emails_match):
+ schema = {
+ 'email': [not_empty, strip_value, user_email_validator, user_emails_match, unicode_safe],
+ 'email2': [unicode_safe],
+ }
+ return schema
diff --git a/ckanext-hdx_users/ckanext/hdx_users/views/onboarding.py b/ckanext-hdx_users/ckanext/hdx_users/views/onboarding.py
index 9b3d3164cf..4d8f9b208c 100644
--- a/ckanext-hdx_users/ckanext/hdx_users/views/onboarding.py
+++ b/ckanext-hdx_users/ckanext/hdx_users/views/onboarding.py
@@ -20,7 +20,7 @@
from ckanext.hdx_users.helpers.constants import ONBOARDING_CAME_FROM_EXTRAS_KEY, ONBOARDING_CAME_FROM_STATE_EXTRAS_KEY, \
ONBOARDING_MAILCHIMP_OPTIN_KEY
from ckanext.hdx_users.views.user_view_helper import CaptchaNotValid, OnbCaptchaErr, error_message
-from ckanext.hdx_users.logic.schema import onboarding_user_new_form_schema
+from ckanext.hdx_users.logic.schema import onboarding_user_new_form_schema, onboarding_user_change_email_form_schema
log = logging.getLogger(__name__)
@@ -151,6 +151,7 @@ def post(self) -> Union[Response, str]:
validation_link=h.url_for('hdx_user_onboarding.validate_account', token=token['token'], qualified=True)
)
+ session['user_info_id'] = user_dict.get('id')
session['user_info_email'] = user_dict.get('email')
return redirect('hdx_user_onboarding.verify_email', user_id=user_dict.get('id'))
@@ -196,6 +197,85 @@ def verify_email(user_id: str) -> str:
return abort(404, _(u'Page not found'))
+def change_email() -> str:
+ if session.get('user_info_id'):
+ user_id = session.get('user_info_id')
+
+ try:
+ context = {
+ 'model': model,
+ 'session': model.Session,
+ 'schema': onboarding_user_change_email_form_schema(),
+ 'keep_email': True,
+ 'ignore_auth': True
+ }
+
+ user_dict = get_action('user_show')(context, {'id': user_id})
+
+ is_user_validated_and_token_disabled = tokens.is_user_validated_and_token_disabled(user_dict)
+ if is_user_validated_and_token_disabled:
+ return redirect('hdx_user_onboarding.validated_account', user_id=user_id)
+
+ if request.method == 'POST':
+ try:
+ data_dict = logic.clean_dict(
+ dictization_functions.unflatten(logic.tuplize_dict(logic.parse_params(request.form))))
+ except dictization_functions.DataError:
+ abort(400, _(u'Integrity Error'))
+
+ # email update
+ try:
+ updated_user = get_action('user_patch')(context, {
+ 'id': user_dict['id'],
+ 'email': data_dict.get('email'),
+ 'email2': data_dict.get('email2'),
+ })
+
+ old_token = tokens.token_show(context, user_dict)
+ new_token = tokens.refresh_token(context, old_token)
+ subject = h.HDX_CONST('UI_CONSTANTS')['ONBOARDING']['EMAIL_SUBJECTS']['EMAIL_CONFIRMATION']
+ tokens.send_validation_email(
+ updated_user,
+ new_token,
+ subject,
+ 'email/content/onboarding/email_confirmation.html',
+ validation_link=h.url_for('hdx_user_onboarding.validate_account', token=new_token['token'],
+ qualified=True)
+ )
+
+ session['user_info_email'] = updated_user.get('email')
+ return redirect('hdx_user_onboarding.verify_email', user_id=user_dict.get('id'))
+ except NotAuthorized:
+ log.error(f'Unauthorized to change email address for user "{user_id}"')
+ abort(403, _(u'Unauthorized to edit user %s') % user_id)
+ except NotFound:
+ log.error(f'User "{user_id}" could not be found to change email address')
+ abort(404, _(u'User not found'))
+ except ValidationError as e:
+ errors = e.error_dict
+ error_summary = e.error_summary
+ template_data = {
+ u'errors': errors,
+ u'error_summary': error_summary,
+ }
+ return render('onboarding/signup/change-email.html', template_data)
+ except Exception as e:
+ log.error(e)
+ abort(404, _(u'Something went wrong. Please contact support'))
+
+ template_data = {
+ u'errors': {},
+ u'error_summary': {},
+ }
+ return render('onboarding/signup/change-email.html', template_data)
+
+ except NotFound:
+ log.error(f'User "{user_id}" could not be found to change email address')
+ abort(404, _(u'User not found'))
+
+ return abort(404, _(u'Page not found'))
+
+
def validate_account(token: str) -> str:
if request.user_agent.string.strip() and request.method == 'GET':
# we don't want to run this for 'HEAD' requests or for requests that don't come from a browser
@@ -233,6 +313,8 @@ def validate_account(token: str) -> str:
if session.get('user_info_email'):
session.pop('user_info_email')
+ if session.get('user_info_id'):
+ session.pop('user_info_id')
template_data = {
'fullname': user_dict.get('fullname', '')
@@ -269,6 +351,8 @@ def validated_account(user_id: str) -> str:
methods=[u'GET', u'POST'], strict_slashes=False)
hdx_user_onboarding.add_url_rule(u'/verify-email//', view_func=verify_email, methods=[u'GET'],
strict_slashes=False)
+hdx_user_onboarding.add_url_rule(u'/change-email/', view_func=change_email, methods=[u'GET', u'POST'],
+ strict_slashes=False)
hdx_user_onboarding.add_url_rule(u'/validate-account//', view_func=validate_account,
methods=[u'GET'], strict_slashes=False)
hdx_user_onboarding.add_url_rule(u'/validated-account//', view_func=validated_account, methods=[u'GET'],