Merge with master again.

This commit is contained in:
Nicholas Staples
2016-03-02 13:53:05 +00:00
18 changed files with 276 additions and 109 deletions

View File

@@ -3,7 +3,24 @@ from flask import Blueprint
main = Blueprint('main', __name__)
from app.main.views import (
index, sign_in, sign_out, register, two_factor, verify, send, add_service,
code_not_received, jobs, dashboard, templates, service_settings, forgot_password,
new_password, styleguide, user_profile, choose_service, api_keys, manage_users
index,
sign_in,
sign_out,
register,
two_factor,
verify,
send,
add_service,
code_not_received,
jobs, dashboard,
templates,
service_settings,
forgot_password,
new_password,
styleguide,
user_profile,
choose_service,
api_keys,
manage_users,
invites
)

34
app/main/views/invites.py Normal file
View File

@@ -0,0 +1,34 @@
from flask import (
redirect,
url_for,
abort
)
from notifications_python_client.errors import HTTPError
from app.main import main
from app import (
invite_api_client,
user_api_client
)
@main.route("/invitation/<token>")
def accept_invite(token):
try:
invited_user = invite_api_client.accept_invite(token)
existing_user = user_api_client.get_user_by_email(invited_user.email_address)
if existing_user:
user_api_client.add_user_to_service(invited_user.service,
existing_user.id)
return redirect(url_for('main.service_dashboard', service_id=invited_user.service))
else:
# TODO implement registration flow for new users
abort(404)
except HTTPError as e:
if e.status_code == 404:
abort(404)
else:
raise e

View File

@@ -58,8 +58,8 @@ def invite_user(service_id):
email_address = form.email_address.data
permissions = _get_permissions(request.form)
try:
resp = invite_api_client.create_invite(current_user.id, service_id, email_address, permissions)
flash('Invite sent to {}'.format(resp['email_address']), 'default_with_tick')
invited_user = invite_api_client.create_invite(current_user.id, service_id, email_address, permissions)
flash('Invite sent to {}'.format(invited_user.email_address), 'default_with_tick')
return redirect(url_for('.manage_users', service_id=service_id))
except HTTPError as e:

View File

@@ -28,6 +28,7 @@ from app.main.dao import services_dao
from app import job_api_client
from app.utils import (
validate_recipient, InvalidPhoneError, InvalidEmailError, user_has_permissions)
from utils.process_csv import first_column_heading
manage_service_page_headings = {
@@ -115,11 +116,12 @@ def send_messages(service_id, template_id):
templates_dao.get_service_template_or_404(service_id, template_id)['data'],
prefix=service['name']
)
recipient_column = first_column_heading[template.template_type]
return render_template(
'views/send.html',
template=template,
column_headers=['to'] + template.placeholders_as_markup,
recipient_column=first_column_heading[template.template_type],
form=form,
service=service,
service_id=service_id
@@ -130,17 +132,19 @@ def send_messages(service_id, template_id):
@login_required
@user_has_permissions('send_messages', 'manage_templates', or_=True)
def get_example_csv(service_id, template_id):
template = templates_dao.get_service_template_or_404(service_id, template_id)['data']
placeholders = list(Template(template).placeholders)
template = Template(templates_dao.get_service_template_or_404(service_id, template_id)['data'])
output = io.StringIO()
writer = csv.writer(output)
writer.writerow(['to'] + placeholders)
writer.writerow(
[first_column_heading[template.template_type]] +
list(template.placeholders)
)
writer.writerow([
{
'email': current_user.email_address,
'sms': current_user.mobile_number
}[template['template_type']]
] + ["test {}".format(header) for header in placeholders])
}[template.template_type]
] + ["test {}".format(header) for header in template.placeholders])
return output.getvalue(), 200, {'Content-Type': 'text/csv; charset=utf-8'}
@@ -148,12 +152,17 @@ def get_example_csv(service_id, template_id):
@login_required
@user_has_permissions('send_messages')
def send_message_to_self(service_id, template_id):
template = templates_dao.get_service_template_or_404(service_id, template_id)['data']
placeholders = list(Template(template).placeholders)
template = Template(templates_dao.get_service_template_or_404(service_id, template_id)['data'])
output = io.StringIO()
writer = csv.writer(output)
writer.writerow(['to'] + placeholders)
writer.writerow([current_user.mobile_number] + ["test {}".format(header) for header in placeholders])
writer.writerow(
[first_column_heading[template.template_type]] +
list(template.placeholders)
)
writer.writerow(
[current_user.mobile_number] +
["test {}".format(header) for header in template.placeholders]
)
filedata = {
'file_name': 'Test run',
'data': output.getvalue().splitlines()
@@ -187,7 +196,7 @@ def check_messages(service_id, upload_id):
template = Template(
raw_template,
values=upload_result['rows'][0] if upload_result['valid'] else {},
drop_values={'to'},
drop_values={first_column_heading[raw_template['template_type']]},
prefix=service['name']
)
return render_template(
@@ -195,7 +204,7 @@ def check_messages(service_id, upload_id):
upload_result=upload_result,
template=template,
page_heading=get_page_headings(template.template_type),
column_headers=['to'] + list(template.placeholders_as_markup),
column_headers=[first_column_heading[template.template_type]] + list(template.placeholders_as_markup),
original_file_name=upload_data.get('original_file_name'),
service_id=service_id,
service=service,
@@ -257,10 +266,13 @@ def _get_rows(contents, raw_template):
rows.append(row)
try:
validate_recipient(
row.get('to', ''),
template_type=raw_template['template_type']
row, template_type=raw_template['template_type']
)
Template(raw_template, values=row, drop_values={'to'}).replaced
Template(
raw_template,
values=row,
drop_values={first_column_heading[raw_template['template_type']]}
).replaced
except (InvalidEmailError, InvalidPhoneError, NeededByTemplateError, NoPlaceholderForDataError):
valid = False
return {"valid": valid, "rows": rows}

View File

@@ -1,6 +1,5 @@
from notifications_python_client.base import BaseAPIClient
from app.notify_client.models import User
from app.notify_client.models import InvitedUser
class InviteApiClient(BaseAPIClient):
@@ -22,9 +21,23 @@ class InviteApiClient(BaseAPIClient):
'permissions': permissions
}
resp = self.post(url='/service/{}/invite'.format(service_id), data=data)
return resp['data']
return InvitedUser(**resp['data'])
def get_invites_for_service(self, service_id):
endpoint = '/service/{}/invite'.format(service_id)
resp = self.get(endpoint)
return [User(data) for data in resp['data']]
invites = resp['data']
invited_users = _get_invited_users(invites)
return invited_users
def accept_invite(self, token):
resp = self.get(url='/invite/{}'.format(token))
return InvitedUser(**resp['data'])
def _get_invited_users(invites):
invited_users = []
for invite in invites:
invited_user = InvitedUser(**invite)
invited_users.append(invited_user)
return invited_users

View File

@@ -114,3 +114,18 @@ class User(UserMixin):
def set_password(self, pwd):
self._password = pwd
class InvitedUser(object):
def __init__(self, id, service, from_user, email_address, permissions, status, created_at):
self.id = id
self.service = str(service)
self.from_user = from_user
self.email_address = email_address
self.permissions = permissions.split(',')
self.status = status
self.created_at = created_at
def has_permissions(self, permission):
return permission in self.permissions

View File

@@ -1,3 +1,5 @@
import json
from notifications_python_client.notifications import BaseAPIClient
from notifications_python_client.errors import HTTPError
@@ -85,3 +87,8 @@ class UserApiClient(BaseAPIClient):
endpoint = '/service/{}/users'.format(service_id)
resp = self.get(endpoint)
return [User(data) for data in resp['data']]
def add_user_to_service(self, service_id, user_id):
endpoint = '/service/{}/users/{}'.format(service_id, user_id)
resp = self.post(endpoint, data={})
return User(resp['data'], max_failed_login_count=self.max_failed_login_count)

View File

@@ -61,13 +61,17 @@
caption=original_file_name,
field_headings=column_headers
) %}
{% if item.to or ''|valid_phone_number %}
{% if item.get('phone number', '')|valid_phone_number %}
{% call field() %}
{{ item.to }}
{{ item['phone number'] }}
{% endcall %}
{% elif item.get('email address') %}
{% call field() %}
{{ item['email address'] }}
{% endcall %}
{% else %}
{% call field(status='missing') %}
{{ item.to }}
{{ item['phone number'] }}
{% endcall %}
{% endif %}
{% for column in template.placeholders %}

View File

@@ -3,6 +3,7 @@
{% from "components/big-number.html" import big_number %}
{% from "components/banner.html" import banner %}
{% from "components/sms-message.html" import sms_message %}
{% from "components/email-message.html" import email_message %}
{% block page_title %}
{{ uploaded_file_name }} GOV.UK Notify

View File

@@ -43,9 +43,9 @@ Manage users GOV.UK Notify
{% call field() %}
{{ item.email_address }}
{% endcall %}
{{ boolean_field(item.has_permissions(service_id, 'send_messages')) }}
{{ boolean_field(item.has_permissions(service_id, 'manage_service')) }}
{{ boolean_field(item.has_permissions(service_id, 'api_keys')) }}
{{ boolean_field(item.has_permissions('send_messages')) }}
{{ boolean_field(item.has_permissions('manage_service')) }}
{{ boolean_field(item.has_permissions('manage_api_keys')) }}
{% call field(align='right') %}
<a href="{{ url_for('.edit_user', service_id=service_id, user_id=item.id)}}">Change</a>
{% endcall %}

View File

@@ -40,7 +40,7 @@
{% endif %}
</p>
<p class="bottom-gutter-2-3">
<span class='placeholder'>to</span>
<span class='placeholder'>{{ recipient_column }}</span>
{{ template.placeholders_as_markup|join(" ") }}
</p>
<p>

View File

@@ -3,6 +3,8 @@ import re
from functools import wraps
from flask import (abort, session)
from utils.process_csv import get_recipient_from_row
class BrowsableItem(object):
"""
@@ -87,11 +89,11 @@ def validate_email_address(email_address):
raise InvalidEmailError('Not a valid email address')
def validate_recipient(recipient, template_type):
def validate_recipient(row, template_type):
return {
'email': validate_email_address,
'sms': validate_phone_number
}[template_type](recipient)
}[template_type](get_recipient_from_row(row, template_type))
def user_has_permissions(*permissions, or_=False):