Merge branch 'master' of github.com:alphagov/notifications-admin into api-keys-flow

Conflicts:
	tests/app/main/views/test_api_keys.py
This commit is contained in:
Rebecca Law
2016-01-21 12:31:28 +00:00
30 changed files with 419 additions and 316 deletions

View File

@@ -1,3 +1,3 @@
$(() => GOVUK.modules.start());
$(() => new GOVUK.SelectionButtons('.block-label input'));
$(() => new GOVUK.SelectionButtons('.block-label input, .sms-message-option input'));

View File

@@ -1,53 +1,63 @@
.sms-message {
position: relative;
%sms-message-wrapper,
.sms-message-wrapper {
width: 100%;
box-sizing: border-box;
padding: $gutter/2;
background: $panel-colour;
border: 1px solid $panel-colour;
border-radius: 5px;
white-space: normal;
margin: 0 0 $gutter 0;
}
.sms-message-wrapper-with-radio {
@extend %sms-message-wrapper;
padding-left: 45px;
cursor: pointer;
}
.sms-message-recipient {
@include copy-19;
color: $secondary-text-colour;
margin: -$gutter-half 0 $gutter 0;
}
.sms-message-name {
@include bold-19;
margin: 30px 0 10px 0;
}
.sms-message-picker {
display: block;
margin: 7px 0 0 0;
position: absolute;
left: 15px;
top: 50%;
z-index: 50;
}
label.sms-message-option {
display: block;
position: relative;
&.selected {
.sms-message-wrapper-with-radio {
background: $white;
border: 1px solid $text-colour;
}
&:before {
content: '';
position: absolute;
z-index: 10;
bottom: -12px;
right: 5px;
width: 20px;
height: 25px;
border-radius: 100%;
background: $panel-colour;
}
&:after {
content: '';
position: absolute;
z-index: 20;
bottom: -15px;
right: -5px;
border-radius: 100%;
width: 20px;
height: 20px;
background: $white;
}
&.focused {
&-wrapper {
width: 100%;
display: inline-block;
box-sizing: border-box;
position: relative;
z-index: 30;
padding: $gutter/2;
background: $panel-colour;
border-radius: 5px;
white-space: normal;
}
outline: none;
&-recipient {
@include copy-19;
color: $secondary-text-colour;
margin: -$gutter-half 0 $gutter 0;
}
.sms-message-wrapper-with-radio {
box-shadow: 0 0 0 3px $yellow;
}
&-name {
@include bold-19;
margin: 50px 0 10px 0;
}
}

View File

@@ -1,33 +0,0 @@
.template-picker {
&-name {
@include bold-19;
padding-left: 5px;
}
&-option {
white-space: nowrap;
clear: both;
&-radio {
padding-right: 10px;
box-sizing: border-box;
vertical-align: top;
width: 33%;
display: inline-block;
}
.block-label {
display: inline-block;
}
.sms-message {
display: inline-block;
vertical-align: top;
max-width: 66%;
}
}
}

View File

@@ -32,7 +32,6 @@
// Specific to this application
@import 'grids';
@import 'components/template-picker';
@import 'components/placeholder';
@import 'components/sms-message';
@import 'components/page-footer';

View File

@@ -6,6 +6,8 @@ from app import db, login_manager
from app.models import User
from app.main.encryption import hashpw
from app import user_api_client
@login_manager.user_loader
def load_user(user_id):
@@ -21,7 +23,7 @@ def insert_user(user):
# TODO Would be better to have a generic get and update for user
# something that replicates the sql functionality.
def get_user_by_id(id):
return User.query.filter_by(id=id).first()
return user_api_client.get_user(id)
def get_all_users():
@@ -38,11 +40,9 @@ def increment_failed_login_count(id):
db.session.commit()
def activate_user(id):
user = get_user_by_id(id)
def activate_user(user):
user.state = 'active'
db.session.add(user)
db.session.commit()
return user_api_client.update_user(user)
def update_email_address(id, email_address):

View File

@@ -1,39 +1,12 @@
templates = [
{
'type': 'sms',
'name': 'Confirmation',
'body': 'Lasting power of attorney: Weve received your application. Applications take between 8 and 10 weeks to process.' # noqa
'name': 'Confirmation with details Jan 2016',
'body': '((name)), weve received your ((thing)). Well contact you again within 1 week.'
},
{
'type': 'sms',
'name': 'Reminder',
'body': 'Vehicle tax: Your vehicle tax for ((registration number)) expires on ((date)). Tax your vehicle at www.gov.uk/vehicle-tax' # noqa
},
{
'type': 'sms',
'name': 'Warning',
'body': 'Vehicle tax: Your vehicle tax for ((registration number)) has expired. Tax your vehicle at www.gov.uk/vehicle-tax' # noqa
},
{
'type': 'email',
'name': 'Application alert 06/2016',
'subject': 'Your lasting power of attorney application',
'body': """Dear ((name)),
When youve made your lasting power of attorney (LPA), you need to register it \
with the Office of the Public Guardian (OPG).
You can apply to register your LPA yourself if youre able to make your own decisions.
Your attorney can also register it for you. Youll be told if they do and you can \
object to the registration.
It takes between 8 and 10 weeks to register an LPA if there are no mistakes in the application.
"""
},
{
'type': 'sms',
'name': 'Air quality alert',
'body': 'Air pollution levels will be ((level)) in ((region)) tomorrow.'
},
'name': 'Confirmation Jan 2016',
'body': 'Weve received your payment. Well contact you again within 1 week.'
}
]

View File

@@ -4,14 +4,12 @@ from flask import (
render_template,
redirect,
session,
current_app,
abort
)
from client.errors import HTTPError
from app.main import main
from app.models import User
from app.main.dao import users_dao
from app.main.forms import RegisterUserForm
@@ -27,7 +25,6 @@ def register():
form = RegisterUserForm(users_dao.get_user_by_email)
if form.validate_on_submit():
try:
user = user_api_client.register_user(form.name.data,
form.email_address.data,

View File

@@ -16,6 +16,8 @@ def sign_in():
if form.validate_on_submit():
user = users_dao.get_user_by_email(form.email_address.data)
if user:
# TODO move to user API in next pr to actually do password check as this
# is totally broken now
if not user.is_locked() and user.is_active() and check_hash(form.password.data, user.password):
send_sms_code(user.id, user.mobile_number)
session['user_email'] = user.email_address

View File

@@ -1,11 +1,13 @@
from flask import (
render_template,
redirect,
jsonify,
session,
url_for
url_for,
abort
)
from client.errors import HTTPError
from flask_login import login_user
from app.main import main
@@ -24,9 +26,15 @@ def verify():
verify_codes_dao.use_code_for_user_and_type(user_id=user_id, code_type='email')
verify_codes_dao.use_code_for_user_and_type(user_id=user_id, code_type='sms')
# TODO complete verify and login flow
# users_dao.activate_user(user.id)
# login_user(user)
try:
user = users_dao.get_user_by_id(user_id)
activated_user = users_dao.activate_user(user)
login_user(activated_user)
return redirect(url_for('main.add_service', first='first'))
except HTTPError as e:
if e.status_code == 404:
abort(404)
else:
raise e
return redirect(url_for('.add_service', first='first'))
return render_template('views/verify.html', form=form)

View File

@@ -24,6 +24,17 @@ class UserApiClient(BaseAPIClient):
user_data = self.post("/user", data)
return User(user_data['data'], max_failed_login_count=self.user_max_failed_login_count)
def get_user(self, id):
url = "{}/user/{}".format(self.base_url, id)
user_data = self.get(url)
return User(user_data['data'], max_failed_login_count=self.user_max_failed_login_count)
def update_user(self, user):
data = user.serialize()
url = "{}/user/{}".format(self.base_url, user.id)
user_data = self.put(url, data=data)
return User(user_data['data'], max_failed_login_count=self.user_max_failed_login_count)
class User(object):
@@ -51,24 +62,28 @@ class User(object):
def password_changed_at(self):
return self.fields.get('password_changed_at')
@property
def get_id(self):
return self.id
def is_authenticated(self):
return self.fields.get('is_authenticated')
return True
@property
def is_active(self):
if self.fields.get('state') != 'active':
return False
else:
return True
return self.state == 'active'
@property
def state(self):
return self.fields['state']
@state.setter
def state(self, state):
self.fields['state'] = state
def is_anonymous(self):
return False
@property
def is_locked(self):
if self.fields.get('failed_login_count') < self.max_failed_login_count:
return False
else:
return True
return self.fields.get('failed_login_count') > self.max_failed_login_count
def serialize(self):
return self.fields

View File

@@ -55,8 +55,10 @@
<summary class="dropdown-toggle">
Service name
</summary>
<a href="#">Switch to A N Other service</a>
<a href="{{ url_for('.add_service') }}">Add a new service to GOV.UK Notify</a>
<div>
<a href="#">Switch to A N Other service</a>
<a href="{{ url_for('.add_service') }}">Add a new service to GOV.UK Notify</a>
</div>
</details>
</div>
<div class="column-half management-navigation-account">

View File

@@ -1,4 +1,9 @@
{% macro sms_message(body, recipient=None, name=None, edit_link=None) %}
{% macro sms_message(
body, recipient=None, name=None, edit_link=None, input_name=None, input_index=None
) %}
{% if input_name %}
<label class="sms-message-option" for="{{ input_name }}-{{ input_index }}">
{% endif %}
{% if name %}
<h3 class="sms-message-name">
{% if edit_link %}
@@ -8,14 +13,18 @@
{% endif %}
</h3>
{% endif %}
<div class="sms-message">
<div class="sms-message-wrapper">
{{ body|placeholders }}
</div>
{% if input_name %}
<input class="sms-message-picker" type="radio" id="{{ input_name }}-{{ input_index }}" name="{{ input_name }}" />
{% endif %}
<div class="sms-message-wrapper{% if input_name %}-with-radio{% endif %}">
{{ body|placeholders }}
</div>
{% if recipient %}
<p class="sms-message-recipient">
{{ recipient }}
</p>
{% endif %}
{% if input_name %}
</label>
{% endif %}
{% endmacro %}

View File

@@ -10,35 +10,36 @@
{% block maincolumn_content %}
<form method="POST" enctype="multipart/form-data">
<h1 class="heading-xlarge">Send text messages</h1>
<div class="grid-row">
<div class="column-three-quarters">
<fieldset class='form-group'>
<legend class="heading-medium">1. Choose text message template</legend>
{% for template in message_templates %}
<label class="block-label" for="template-{{loop.index}}">
{{ template.name }}
<input type="radio" name="template" id="template-{{loop.index}}" value="{{ template.name }}" />
</label>
{% endfor %}
</fieldset>
<h1 class="heading-xlarge">Send text messages</h1>
<p>
or <a href="{{ url_for('.add_service_template', service_id=service_id) }}">create a new template</a>
</p>
<fieldset class='form-group'>
<legend class="heading-medium">1. Choose text message template</legend>
{% for template in message_templates %}
{{ sms_message(
template.body, name=template.name, input_name='template', input_index=loop.index
) }}
{% endfor %}
</fieldset>
<h2 class="heading-medium">2. Add recipients</h2>
<h2 class="heading-medium">2. Add recipients</h2>
<p>
Upload a CSV file to add your recipients details.
</p>
<p>
You can also <a href="#">download an example CSV</a>.
</p>
<p>
{{textbox(form.file)}}
</p>
<p>
Upload a CSV file to add your recipients details.
</p>
<p>
You can also <a href="#">download an example CSV</a>.
</p>
<p>
{{textbox(form.file)}}
</p>
{{ page_footer("Continue") }}
{{ page_footer("Continue") }}
</div>
</div>
</form>

View File

@@ -92,7 +92,7 @@
<h2 class="heading-large">SMS message</h2>
<p>Used to show or preview an SMS message.</p>
<p>Used to show, preview or choose an SMS message.</p>
<div class="grid-row">
<div class="column-half">
@@ -102,6 +102,12 @@
"Your vehicle tax for registration number is due on date. Renew online at www.gov.uk/vehicle-tax",
"+44 7700 900 306"
) }}
{{ sms_message(
"Your vehicle tax for ((registration number)) is due on ((date)). Renew online at www.gov.uk/vehicle-tax",
name="Reminder",
input_name="template",
input_index=1
) }}
</div>
</div>