mirror of
https://github.com/GSA/notifications-admin.git
synced 2026-09-02 17:48:50 -04:00
Merge pull request #68 from alphagov/user-profile-flow
Add user profile flow
This commit is contained in:
@@ -4,5 +4,6 @@ main = Blueprint('main', __name__)
|
|||||||
|
|
||||||
from app.main.views import (
|
from app.main.views import (
|
||||||
index, sign_in, sign_out, register, two_factor, verify, sms, add_service,
|
index, sign_in, sign_out, register, two_factor, verify, sms, add_service,
|
||||||
code_not_received, jobs, dashboard, templates, service_settings, forgot_password, new_password, styleguide
|
code_not_received, jobs, dashboard, templates, service_settings, forgot_password,
|
||||||
|
new_password, styleguide, user_profile
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -31,8 +31,8 @@ def mobile_number():
|
|||||||
Regexp(regex=mobile_number_regex, message='Enter a +44 mobile number')])
|
Regexp(regex=mobile_number_regex, message='Enter a +44 mobile number')])
|
||||||
|
|
||||||
|
|
||||||
def password():
|
def password(label='Create a password'):
|
||||||
return PasswordField('Create a password',
|
return PasswordField(label,
|
||||||
validators=[DataRequired(message='Password can not be empty'),
|
validators=[DataRequired(message='Password can not be empty'),
|
||||||
Length(10, 255, message='Password must be at least 10 characters'),
|
Length(10, 255, message='Password must be at least 10 characters'),
|
||||||
Blacklist(message='That password is blacklisted, too common')])
|
Blacklist(message='That password is blacklisted, too common')])
|
||||||
@@ -152,6 +152,31 @@ class NewPasswordForm(Form):
|
|||||||
new_password = password()
|
new_password = password()
|
||||||
|
|
||||||
|
|
||||||
|
class ChangePasswordForm(Form):
|
||||||
|
old_password = password('Current password')
|
||||||
|
new_password = password('New password')
|
||||||
|
|
||||||
|
|
||||||
class CsvUploadForm(Form):
|
class CsvUploadForm(Form):
|
||||||
file = FileField('File to upload', validators=[DataRequired(
|
file = FileField('File to upload', validators=[DataRequired(
|
||||||
message='Please pick a file'), CsvFileValidator()])
|
message='Please pick a file'), CsvFileValidator()])
|
||||||
|
|
||||||
|
|
||||||
|
class ChangeNameForm(Form):
|
||||||
|
new_name = StringField(u'Your name')
|
||||||
|
|
||||||
|
|
||||||
|
class ChangeEmailForm(Form):
|
||||||
|
email_address = email_address()
|
||||||
|
|
||||||
|
|
||||||
|
class ConfirmEmailForm(Form):
|
||||||
|
email_code = email_code()
|
||||||
|
|
||||||
|
|
||||||
|
class ChangeMobileNumberForm(Form):
|
||||||
|
mobile_number = mobile_number()
|
||||||
|
|
||||||
|
|
||||||
|
class ConfirmMobileNumberForm(Form):
|
||||||
|
sms_code = sms_code()
|
||||||
|
|||||||
@@ -32,12 +32,6 @@ def checkemail():
|
|||||||
return render_template('views/check-email.html')
|
return render_template('views/check-email.html')
|
||||||
|
|
||||||
|
|
||||||
@main.route("/user-profile")
|
|
||||||
@login_required
|
|
||||||
def userprofile():
|
|
||||||
return render_template('views/user-profile.html')
|
|
||||||
|
|
||||||
|
|
||||||
@main.route("/manage-users")
|
@main.route("/manage-users")
|
||||||
@login_required
|
@login_required
|
||||||
def manageusers():
|
def manageusers():
|
||||||
|
|||||||
139
app/main/views/user_profile.py
Normal file
139
app/main/views/user_profile.py
Normal file
@@ -0,0 +1,139 @@
|
|||||||
|
from flask import request, render_template, redirect, url_for
|
||||||
|
from flask.ext.login import current_user
|
||||||
|
from app.main import main
|
||||||
|
from app.main.forms import (
|
||||||
|
ChangePasswordForm, ChangeNameForm, ChangeEmailForm, ConfirmEmailForm,
|
||||||
|
ChangeMobileNumberForm, ConfirmMobileNumberForm, ConfirmPasswordForm
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@main.route("/user-profile")
|
||||||
|
def userprofile():
|
||||||
|
return render_template('views/user-profile.html')
|
||||||
|
|
||||||
|
|
||||||
|
@main.route("/user-profile/name", methods=['GET', 'POST'])
|
||||||
|
def userprofile_name():
|
||||||
|
|
||||||
|
form = ChangeNameForm()
|
||||||
|
|
||||||
|
if request.method == 'GET':
|
||||||
|
if current_user.is_authenticated():
|
||||||
|
form.new_name.data = current_user.name
|
||||||
|
return render_template(
|
||||||
|
'views/user-profile/change.html',
|
||||||
|
thing='name',
|
||||||
|
form_field=form.new_name
|
||||||
|
)
|
||||||
|
elif request.method == 'POST':
|
||||||
|
return redirect(url_for('.userprofile'))
|
||||||
|
|
||||||
|
|
||||||
|
@main.route("/user-profile/email", methods=['GET', 'POST'])
|
||||||
|
def userprofile_email():
|
||||||
|
|
||||||
|
form = ChangeEmailForm()
|
||||||
|
|
||||||
|
if request.method == 'GET':
|
||||||
|
if current_user.is_authenticated():
|
||||||
|
form.email_address.data = current_user.email_address
|
||||||
|
return render_template(
|
||||||
|
'views/user-profile/change.html',
|
||||||
|
thing='email address',
|
||||||
|
form_field=form.email_address
|
||||||
|
)
|
||||||
|
elif request.method == 'POST':
|
||||||
|
return redirect(url_for('.userprofile_email_authenticate'))
|
||||||
|
|
||||||
|
|
||||||
|
@main.route("/user-profile/email/authenticate", methods=['GET', 'POST'])
|
||||||
|
def userprofile_email_authenticate():
|
||||||
|
|
||||||
|
form = ConfirmPasswordForm()
|
||||||
|
|
||||||
|
if request.method == 'GET':
|
||||||
|
return render_template(
|
||||||
|
'views/user-profile/authenticate.html',
|
||||||
|
thing='email address',
|
||||||
|
form=form,
|
||||||
|
back_link=url_for('.userprofile_email')
|
||||||
|
)
|
||||||
|
elif request.method == 'POST':
|
||||||
|
return redirect(url_for('.userprofile_email_confirm'))
|
||||||
|
|
||||||
|
|
||||||
|
@main.route("/user-profile/email/confirm", methods=['GET', 'POST'])
|
||||||
|
def userprofile_email_confirm():
|
||||||
|
|
||||||
|
form = ConfirmEmailForm()
|
||||||
|
|
||||||
|
if request.method == 'GET':
|
||||||
|
return render_template(
|
||||||
|
'views/user-profile/confirm.html',
|
||||||
|
form_field=form.email_code,
|
||||||
|
thing='email address'
|
||||||
|
)
|
||||||
|
elif request.method == 'POST':
|
||||||
|
return redirect(url_for('.userprofile'))
|
||||||
|
|
||||||
|
|
||||||
|
@main.route("/user-profile/mobile-number", methods=['GET', 'POST'])
|
||||||
|
def userprofile_mobile_number():
|
||||||
|
|
||||||
|
form = ChangeMobileNumberForm()
|
||||||
|
|
||||||
|
if request.method == 'GET':
|
||||||
|
if current_user.is_authenticated():
|
||||||
|
form.mobile_number.data = current_user.mobile_number
|
||||||
|
return render_template(
|
||||||
|
'views/user-profile/change.html',
|
||||||
|
thing='mobile number',
|
||||||
|
form_field=form.mobile_number
|
||||||
|
)
|
||||||
|
elif request.method == 'POST':
|
||||||
|
return redirect(url_for('.userprofile_mobile_number_authenticate'))
|
||||||
|
|
||||||
|
|
||||||
|
@main.route("/user-profile/mobile-number/authenticate", methods=['GET', 'POST'])
|
||||||
|
def userprofile_mobile_number_authenticate():
|
||||||
|
|
||||||
|
form = ConfirmPasswordForm()
|
||||||
|
|
||||||
|
if request.method == 'GET':
|
||||||
|
return render_template(
|
||||||
|
'views/user-profile/authenticate.html',
|
||||||
|
thing='mobile number',
|
||||||
|
form=form,
|
||||||
|
back_link=url_for('.userprofile_mobile_number_confirm')
|
||||||
|
)
|
||||||
|
elif request.method == 'POST':
|
||||||
|
return redirect(url_for('.userprofile_mobile_number_confirm'))
|
||||||
|
|
||||||
|
|
||||||
|
@main.route("/user-profile/mobile-number/confirm", methods=['GET', 'POST'])
|
||||||
|
def userprofile_mobile_number_confirm():
|
||||||
|
|
||||||
|
form = ConfirmMobileNumberForm()
|
||||||
|
|
||||||
|
if request.method == 'GET':
|
||||||
|
return render_template(
|
||||||
|
'views/user-profile/confirm.html',
|
||||||
|
form_field=form.sms_code,
|
||||||
|
thing='mobile number'
|
||||||
|
)
|
||||||
|
elif request.method == 'POST':
|
||||||
|
return redirect(url_for('.userprofile'))
|
||||||
|
|
||||||
|
|
||||||
|
@main.route("/user-profile/password", methods=['GET', 'POST'])
|
||||||
|
def userprofile_password():
|
||||||
|
|
||||||
|
form = ChangePasswordForm()
|
||||||
|
|
||||||
|
if request.method == 'GET':
|
||||||
|
return render_template(
|
||||||
|
'views/user-profile/change-password.html',
|
||||||
|
form=form
|
||||||
|
)
|
||||||
|
elif request.method == 'POST':
|
||||||
|
return redirect(url_for('.userprofile'))
|
||||||
@@ -17,7 +17,7 @@ Sign in
|
|||||||
<form autocomplete="off" method="post">
|
<form autocomplete="off" method="post">
|
||||||
{{ textbox(form.email_address) }}
|
{{ textbox(form.email_address) }}
|
||||||
{{ textbox(form.password) }}
|
{{ textbox(form.password) }}
|
||||||
{{ page_footer("Continue", back_link="#", back_link_text="Forgotten password?") }}
|
{{ page_footer("Continue", back_link=url_for('.forgot_password'), back_link_text="Forgotten password?") }}
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,19 +1,35 @@
|
|||||||
{% extends "withnav_template.html" %}
|
{% extends "withnav_template.html" %}
|
||||||
{% from "components/page-footer.html" import page_footer %}
|
{% from "components/table.html" import list_table, row, field %}
|
||||||
|
|
||||||
{% block page_title %}
|
{% block page_title %}
|
||||||
GOV.UK Notify | User settings
|
GOV.UK Notify | Your profile
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block maincolumn_content %}
|
{% block maincolumn_content %}
|
||||||
|
|
||||||
<h1 class="heading-xlarge">User profile</h1>
|
<h1 class="heading-xlarge">Your profile</h1>
|
||||||
|
|
||||||
<p>Here's where users can update their profile, password etc.</p>
|
{% call(item) list_table(
|
||||||
|
[
|
||||||
{{ page_footer(
|
{'label': 'Name', 'value': current_user.name, 'url': url_for('.userprofile_name')},
|
||||||
back_link = url_for('.dashboard'),
|
{'label': 'Email address', 'value': current_user.email_address, 'url': url_for('.userprofile_email')},
|
||||||
back_link_text = 'Back to dashboard'
|
{'label': 'Mobile number', 'value': current_user.mobile_number, 'url': url_for('.userprofile_mobile_number')},
|
||||||
) }}
|
{'label': 'Password', 'value': 'Last changed 1 January 2016, 10:00AM', 'url': url_for('.userprofile_password')},
|
||||||
|
],
|
||||||
|
caption='Account settings',
|
||||||
|
field_headings=['Setting', 'Value', 'Link to change'],
|
||||||
|
field_headings_visible=False,
|
||||||
|
caption_visible=False
|
||||||
|
) %}
|
||||||
|
{% call field() %}
|
||||||
|
{{ item.label }}
|
||||||
|
{% endcall %}
|
||||||
|
{% call field() %}
|
||||||
|
{{ item.value }}
|
||||||
|
{% endcall %}
|
||||||
|
{% call field(align='right') %}
|
||||||
|
<a href="{{ item.url }}">Change</a>
|
||||||
|
{% endcall %}
|
||||||
|
{% endcall %}
|
||||||
|
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|||||||
26
app/templates/views/user-profile/authenticate.html
Normal file
26
app/templates/views/user-profile/authenticate.html
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
{% extends "withnav_template.html" %}
|
||||||
|
{% from "components/textbox.html" import textbox %}
|
||||||
|
{% from "components/page-footer.html" import page_footer %}
|
||||||
|
|
||||||
|
{% block page_title %}
|
||||||
|
GOV.UK Notify | Service settings
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block maincolumn_content %}
|
||||||
|
|
||||||
|
<h1 class="heading-xlarge">Change your {{ thing }}</h1>
|
||||||
|
|
||||||
|
<div class="grid-row">
|
||||||
|
<div class="column-three-quarters">
|
||||||
|
|
||||||
|
<form method="post">
|
||||||
|
{{ textbox(form.password) }}
|
||||||
|
{{ page_footer(
|
||||||
|
'Confirm',
|
||||||
|
back_link=back_link
|
||||||
|
) }}
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{% endblock %}
|
||||||
27
app/templates/views/user-profile/change-password.html
Normal file
27
app/templates/views/user-profile/change-password.html
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
{% extends "withnav_template.html" %}
|
||||||
|
{% from "components/textbox.html" import textbox %}
|
||||||
|
{% from "components/page-footer.html" import page_footer %}
|
||||||
|
|
||||||
|
{% block page_title %}
|
||||||
|
GOV.UK Notify | Service settings
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block maincolumn_content %}
|
||||||
|
|
||||||
|
<h1 class="heading-xlarge">Change your password</h1>
|
||||||
|
|
||||||
|
<div class="grid-row">
|
||||||
|
<div class="column-three-quarters">
|
||||||
|
<form method="post">
|
||||||
|
{{ textbox(form.old_password) }}
|
||||||
|
{{ textbox(form.new_password) }}
|
||||||
|
{{ page_footer(
|
||||||
|
'Save',
|
||||||
|
back_link=url_for('.userprofile'),
|
||||||
|
back_link_text="Back to your profile"
|
||||||
|
) }}
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{% endblock %}
|
||||||
31
app/templates/views/user-profile/change.html
Normal file
31
app/templates/views/user-profile/change.html
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
{% extends "withnav_template.html" %}
|
||||||
|
{% from "components/textbox.html" import textbox %}
|
||||||
|
{% from "components/page-footer.html" import page_footer %}
|
||||||
|
|
||||||
|
{% block page_title %}
|
||||||
|
GOV.UK Notify | Service settings
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block maincolumn_content %}
|
||||||
|
|
||||||
|
<h1 class="heading-xlarge">Change your {{ thing }}</h1>
|
||||||
|
|
||||||
|
<div class="grid-row">
|
||||||
|
{% if preamble %}
|
||||||
|
<p>
|
||||||
|
{{ preamble }}
|
||||||
|
</p>
|
||||||
|
{% endif %}
|
||||||
|
<div class="column-three-quarters">
|
||||||
|
<form method="post">
|
||||||
|
{{ textbox(form_field) }}
|
||||||
|
{{ page_footer(
|
||||||
|
'Save',
|
||||||
|
back_link=url_for('.userprofile'),
|
||||||
|
back_link_text="Back to your profile"
|
||||||
|
) }}
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{% endblock %}
|
||||||
29
app/templates/views/user-profile/confirm.html
Normal file
29
app/templates/views/user-profile/confirm.html
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
{% extends "withnav_template.html" %}
|
||||||
|
{% from "components/textbox.html" import textbox %}
|
||||||
|
{% from "components/page-footer.html" import page_footer %}
|
||||||
|
|
||||||
|
{% block page_title %}
|
||||||
|
GOV.UK Notify | Service settings
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block maincolumn_content %}
|
||||||
|
|
||||||
|
<h1 class="heading-xlarge">Change your {{ thing }}</h1>
|
||||||
|
|
||||||
|
<div class="grid-row">
|
||||||
|
<div class="column-three-quarters">
|
||||||
|
<p>
|
||||||
|
We’ve sent a code to your new {{ thing }}.
|
||||||
|
</p>
|
||||||
|
<form method="post">
|
||||||
|
{{ textbox(form_field) }}
|
||||||
|
{{ page_footer(
|
||||||
|
'Confirm',
|
||||||
|
destructive=destructive,
|
||||||
|
back_link=url_for('.service_settings')
|
||||||
|
) }}
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{% endblock %}
|
||||||
121
tests/app/main/views/test_user_profile.py
Normal file
121
tests/app/main/views/test_user_profile.py
Normal file
@@ -0,0 +1,121 @@
|
|||||||
|
def test_should_show_overview_page(notifications_admin):
|
||||||
|
response = notifications_admin.test_client().get('/user-profile')
|
||||||
|
|
||||||
|
assert 'Your profile' in response.get_data(as_text=True)
|
||||||
|
assert response.status_code == 200
|
||||||
|
|
||||||
|
|
||||||
|
def test_should_show_name_page(notifications_admin):
|
||||||
|
response = notifications_admin.test_client().get('/user-profile/name')
|
||||||
|
|
||||||
|
assert 'Change your name' in response.get_data(as_text=True)
|
||||||
|
assert response.status_code == 200
|
||||||
|
|
||||||
|
|
||||||
|
def test_should_redirect_after_name_change(notifications_admin):
|
||||||
|
response = notifications_admin.test_client().post('/user-profile/name')
|
||||||
|
|
||||||
|
assert response.status_code == 302
|
||||||
|
assert response.location == 'http://localhost/user-profile'
|
||||||
|
|
||||||
|
|
||||||
|
def test_should_show_email_page(notifications_admin):
|
||||||
|
response = notifications_admin.test_client().get('/user-profile/email')
|
||||||
|
|
||||||
|
assert 'Change your email address' in response.get_data(as_text=True)
|
||||||
|
assert response.status_code == 200
|
||||||
|
|
||||||
|
|
||||||
|
def test_should_redirect_after_email_change(notifications_admin):
|
||||||
|
response = notifications_admin.test_client().post('/user-profile/email')
|
||||||
|
|
||||||
|
assert response.status_code == 302
|
||||||
|
assert response.location == 'http://localhost/user-profile/email/authenticate'
|
||||||
|
|
||||||
|
|
||||||
|
def test_should_show_authenticate_after_email_change(notifications_admin):
|
||||||
|
response = notifications_admin.test_client().get('/user-profile/email/authenticate')
|
||||||
|
|
||||||
|
assert 'Change your email address' in response.get_data(as_text=True)
|
||||||
|
assert 'Confirm' in response.get_data(as_text=True)
|
||||||
|
assert response.status_code == 200
|
||||||
|
|
||||||
|
|
||||||
|
def test_should_redirect_after_email_change_confirm(notifications_admin):
|
||||||
|
response = notifications_admin.test_client().post('/user-profile/email/authenticate')
|
||||||
|
|
||||||
|
assert response.status_code == 302
|
||||||
|
assert response.location == 'http://localhost/user-profile/email/confirm'
|
||||||
|
|
||||||
|
|
||||||
|
def test_should_show_confirm_after_email_change(notifications_admin):
|
||||||
|
response = notifications_admin.test_client().get('/user-profile/email/confirm')
|
||||||
|
|
||||||
|
assert 'Change your email address' in response.get_data(as_text=True)
|
||||||
|
assert 'Confirm' in response.get_data(as_text=True)
|
||||||
|
assert response.status_code == 200
|
||||||
|
|
||||||
|
|
||||||
|
def test_should_redirect_after_email_change_confirm(notifications_admin):
|
||||||
|
response = notifications_admin.test_client().post('/user-profile/email/confirm')
|
||||||
|
|
||||||
|
assert response.status_code == 302
|
||||||
|
assert response.location == 'http://localhost/user-profile'
|
||||||
|
|
||||||
|
|
||||||
|
def test_should_show_mobile_number_page(notifications_admin):
|
||||||
|
response = notifications_admin.test_client().get('/user-profile/mobile-number')
|
||||||
|
|
||||||
|
assert 'Change your mobile number' in response.get_data(as_text=True)
|
||||||
|
assert response.status_code == 200
|
||||||
|
|
||||||
|
|
||||||
|
def test_should_redirect_after_mobile_number_change(notifications_admin):
|
||||||
|
response = notifications_admin.test_client().post('/user-profile/email')
|
||||||
|
|
||||||
|
assert response.status_code == 302
|
||||||
|
assert response.location == 'http://localhost/user-profile/email/authenticate'
|
||||||
|
|
||||||
|
|
||||||
|
def test_should_show_authenticate_after_mobile_number_change(notifications_admin):
|
||||||
|
response = notifications_admin.test_client().get('/user-profile/mobile-number/authenticate')
|
||||||
|
|
||||||
|
assert 'Change your mobile number' in response.get_data(as_text=True)
|
||||||
|
assert 'Confirm' in response.get_data(as_text=True)
|
||||||
|
assert response.status_code == 200
|
||||||
|
|
||||||
|
|
||||||
|
def test_should_redirect_after_mobile_number_authenticate(notifications_admin):
|
||||||
|
response = notifications_admin.test_client().post('/user-profile/mobile-number/authenticate')
|
||||||
|
|
||||||
|
assert response.status_code == 302
|
||||||
|
assert response.location == 'http://localhost/user-profile/mobile-number/confirm'
|
||||||
|
|
||||||
|
|
||||||
|
def test_should_show_confirm_after_mobile_number_change(notifications_admin):
|
||||||
|
response = notifications_admin.test_client().get('/user-profile/mobile-number/confirm')
|
||||||
|
|
||||||
|
assert 'Change your mobile number' in response.get_data(as_text=True)
|
||||||
|
assert 'Confirm' in response.get_data(as_text=True)
|
||||||
|
assert response.status_code == 200
|
||||||
|
|
||||||
|
|
||||||
|
def test_should_redirect_after_mobile_number_confirm(notifications_admin):
|
||||||
|
response = notifications_admin.test_client().post('/user-profile/mobile-number/confirm')
|
||||||
|
|
||||||
|
assert response.status_code == 302
|
||||||
|
assert response.location == 'http://localhost/user-profile'
|
||||||
|
|
||||||
|
|
||||||
|
def test_should_show_password_page(notifications_admin):
|
||||||
|
response = notifications_admin.test_client().get('/user-profile/password')
|
||||||
|
|
||||||
|
assert 'Change your password' in response.get_data(as_text=True)
|
||||||
|
assert response.status_code == 200
|
||||||
|
|
||||||
|
|
||||||
|
def test_should_redirect_after_password_change(notifications_admin):
|
||||||
|
response = notifications_admin.test_client().post('/user-profile/password')
|
||||||
|
|
||||||
|
assert response.status_code == 302
|
||||||
|
assert response.location == 'http://localhost/user-profile'
|
||||||
Reference in New Issue
Block a user