diff --git a/app/assets/javascripts/fullscreenTable.js b/app/assets/javascripts/fullscreenTable.js
index 27ff52636..f4ef998b6 100644
--- a/app/assets/javascripts/fullscreenTable.js
+++ b/app/assets/javascripts/fullscreenTable.js
@@ -52,7 +52,8 @@
'height': this.nativeHeight,
'top': this.topOffset
})
- );
+ )
+ .css('position', 'absolute');
this.$scrollableTable = this.$component.find('.fullscreen-scrollable-table');
this.$fixedTable = this.$component.find('.fullscreen-fixed-table');
@@ -100,6 +101,11 @@
this.$scrollableTable.scrollLeft() < (this.$table.width() - this.$scrollableTable.width())
);
+ setTimeout(
+ () => this.$component.find('.fullscreen-right-shadow').addClass('with-transition'),
+ 3000
+ );
+
};
};
diff --git a/app/assets/stylesheets/components/fullscreen-table.scss b/app/assets/stylesheets/components/fullscreen-table.scss
index b552fcbd8..d5c8f78fc 100644
--- a/app/assets/stylesheets/components/fullscreen-table.scss
+++ b/app/assets/stylesheets/components/fullscreen-table.scss
@@ -6,8 +6,7 @@
z-index: 10;
overflow-y: hidden;
box-sizing: border-box;
- position: absolute;
- margin: 5px 0 $gutter 0;
+ margin: 0 0 $gutter 0;
padding: 0 0 0 0;
overflow: hidden;
border-bottom: 1px solid $border-colour;
@@ -42,8 +41,13 @@
z-index: 200;
&.visible {
- transition: box-shadow 0.3s ease-in-out;
+
+ &.with-transition {
+ transition: box-shadow 0.6s ease-out;
+ }
+
box-shadow: inset -1px 0 0 0 $border-colour, inset -3px 0 0 0 rgba($border-colour, 0.2);
+
}
}
diff --git a/app/assets/stylesheets/components/radio-select.scss b/app/assets/stylesheets/components/radio-select.scss
index 19f835310..59586121a 100644
--- a/app/assets/stylesheets/components/radio-select.scss
+++ b/app/assets/stylesheets/components/radio-select.scss
@@ -1,5 +1,7 @@
.radio-select {
+ min-height: 39px;
+
&-column {
display: inline-block;
diff --git a/app/assets/stylesheets/components/table.scss b/app/assets/stylesheets/components/table.scss
index 5bd4c68c0..e7ad5f38a 100644
--- a/app/assets/stylesheets/components/table.scss
+++ b/app/assets/stylesheets/components/table.scss
@@ -129,8 +129,31 @@
}
&-index {
+
@include bold-16;
width: 15px;
+
+ a {
+
+ &:before {
+ content: '';
+ position: absolute;
+ top: 0;
+ left: 0;
+ right: 0;
+ bottom: 0;
+ }
+
+ &:focus {
+
+ &:before {
+ background: $yellow;
+ z-index: -1;
+ }
+
+ }
+
+ }
}
p {
diff --git a/app/main/forms.py b/app/main/forms.py
index 9901cb89a..90171249f 100644
--- a/app/main/forms.py
+++ b/app/main/forms.py
@@ -534,15 +534,9 @@ class RequestToGoLiveForm(StripWhitespaceForm):
'Will the number of messages increase and when will that start?',
validators=[DataRequired(message='Can’t be empty')]
)
- upload_or_api = RadioField(
- 'How are you going to send messages?',
- choices=[
- ('File upload', 'Upload a spreadsheet of recipients'),
- ('API', 'Integrate with the GOV.UK Notify API'),
- ('API and file upload', 'Both')
- ],
- validators=[DataRequired()]
- )
+ method_one_off = BooleanField('One at a time')
+ method_upload = BooleanField('Upload a spreadsheet of recipients')
+ method_api = BooleanField('Integrate with the GOV.UK Notify API')
class ProviderForm(StripWhitespaceForm):
diff --git a/app/main/views/manage_users.py b/app/main/views/manage_users.py
index 440ed6031..707317541 100644
--- a/app/main/views/manage_users.py
+++ b/app/main/views/manage_users.py
@@ -20,17 +20,10 @@ from app.main.forms import (
PermissionsForm
)
from app import (user_api_client, current_service, service_api_client, invite_api_client)
+from app.notify_client.models import roles
from app.utils import user_has_permissions
-roles = {
- 'send_messages': ['send_texts', 'send_emails', 'send_letters'],
- 'manage_templates': ['manage_templates'],
- 'manage_service': ['manage_users', 'manage_settings'],
- 'manage_api_keys': ['manage_api_keys']
-}
-
-
@main.route("/services//users")
@login_required
@user_has_permissions('view_activity', admin_override=True)
@@ -92,10 +85,9 @@ def edit_user_permissions(service_id, user_id):
user_has_no_mobile_number = user.mobile_number is None
form = PermissionsForm(
- **{role: user.has_permissions(permissions=permissions) for role, permissions in roles.items()},
+ **{role: user.has_permissions(*permissions) for role, permissions in roles.items()},
login_authentication=user.auth_type
)
-
if form.validate_on_submit():
user_api_client.set_user_permissions(
user_id, service_id,
@@ -122,7 +114,7 @@ def remove_user_from_service(service_id, user_id):
# Need to make the email address read only, or a disabled field?
# Do it through the template or the form class?
form = PermissionsForm(**{
- role: user.has_permissions(permissions=permissions) for role, permissions in roles.items()
+ role: user.has_permissions(*permissions) for role, permissions in roles.items()
})
if request.method == 'POST':
diff --git a/app/main/views/send.py b/app/main/views/send.py
index f375c1645..6ca253755 100644
--- a/app/main/views/send.py
+++ b/app/main/views/send.py
@@ -523,9 +523,12 @@ def _check_messages(service_id, template_type, upload_id, preview_row, letters_a
count_of_recipients = len(list(recipients.rows))
- if preview_row < count_of_recipients:
- template.values = recipients[preview_row]
- elif preview_row > 0:
+ if preview_row < 2:
+ abort(404)
+
+ if preview_row < count_of_recipients + 2:
+ template.values = recipients[preview_row - 2]
+ elif preview_row > 2:
abort(404)
session['upload_data']['notification_count'] = count_of_recipients
@@ -553,7 +556,8 @@ def _check_messages(service_id, template_type, upload_id, preview_row, letters_a
template.template_type == 'letter',
not request.args.get('from_test'),
)),
- required_recipient_columns=OrderedSet(recipients.recipient_column_headers) - optional_address_columns
+ required_recipient_columns=OrderedSet(recipients.recipient_column_headers) - optional_address_columns,
+ preview_row=preview_row,
)
@@ -561,7 +565,7 @@ def _check_messages(service_id, template_type, upload_id, preview_row, letters_a
@main.route("/services///check//row-", methods=['GET'])
@login_required
@user_has_permissions('send_texts', 'send_emails', 'send_letters')
-def check_messages(service_id, template_type, upload_id, row_index=0):
+def check_messages(service_id, template_type, upload_id, row_index=2):
data = _check_messages(service_id, template_type, upload_id, row_index)
@@ -589,7 +593,7 @@ def check_messages(service_id, template_type, upload_id, row_index=0):
@main.route("/services///check//row-.", methods=['GET'])
@login_required
@user_has_permissions('send_texts', 'send_emails', 'send_letters')
-def check_messages_preview(service_id, template_type, upload_id, filetype, row_index=0):
+def check_messages_preview(service_id, template_type, upload_id, filetype, row_index=2):
if filetype not in ('pdf', 'png'):
abort(404)
diff --git a/app/main/views/service_settings.py b/app/main/views/service_settings.py
index e1e11373b..c274c01bb 100644
--- a/app/main/views/service_settings.py
+++ b/app/main/views/service_settings.py
@@ -153,7 +153,6 @@ def service_request_to_go_live(service_id):
form = RequestToGoLiveForm()
if form.validate_on_submit():
-
data = {
'person_email': current_user.email_address,
'person_name': current_user.name,
@@ -161,13 +160,17 @@ def service_request_to_go_live(service_id):
'agent_team_id': current_app.config.get('DESKPRO_ASSIGNED_AGENT_TEAM_ID'),
'subject': 'Request to go live - {}'.format(current_service['name']),
'message': (
- 'On behalf of {} ({})\n\nExpected usage\n---'
+ 'On behalf of {} ({})\n'
+ '\n---'
+ '\nOrganisation type: {}'
'\nMOU in place: {}'
'\nChannel: {}\nStart date: {}\nStart volume: {}'
- '\nPeak volume: {}\nUpload or API: {}'
+ '\nPeak volume: {}'
+ '\nFeatures: {}'
).format(
current_service['name'],
url_for('main.service_dashboard', service_id=current_service['id'], _external=True),
+ current_service['organisation_type'],
form.mou.data,
formatted_list(filter(None, (
'email' if form.channel_email.data else None,
@@ -177,7 +180,11 @@ def service_request_to_go_live(service_id):
form.start_date.data,
form.start_volume.data,
form.peak_volume.data,
- form.upload_or_api.data
+ formatted_list(filter(None, (
+ 'one off' if form.method_one_off.data else None,
+ 'file upload' if form.method_upload.data else None,
+ 'API' if form.method_api.data else None,
+ )), before_each='', after_each='')
)
}
diff --git a/app/main/views/templates.py b/app/main/views/templates.py
index 90f7d6b82..bd2ba0799 100644
--- a/app/main/views/templates.py
+++ b/app/main/views/templates.py
@@ -354,7 +354,7 @@ def add_service_template(service_id, template_type):
def abort_403_if_not_admin_user():
- if not current_user.has_permissions([], admin_override=True):
+ if not current_user.has_permissions(admin_override=True):
abort(403)
diff --git a/app/notify_client/models.py b/app/notify_client/models.py
index 1be63849b..3e6a7ae60 100644
--- a/app/notify_client/models.py
+++ b/app/notify_client/models.py
@@ -1,7 +1,18 @@
+from itertools import chain
from flask_login import UserMixin, AnonymousUserMixin
from flask import session
+roles = {
+ 'send_messages': ['send_texts', 'send_emails', 'send_letters'],
+ 'manage_templates': ['manage_templates'],
+ 'manage_service': ['manage_users', 'manage_settings'],
+ 'manage_api_keys': ['manage_api_keys']
+}
+
+all_permissions = set(chain.from_iterable(roles.values())) | {'view_activity'}
+
+
class User(UserMixin):
def __init__(self, fields, max_failed_login_count=3):
self._id = fields.get('id')
@@ -91,7 +102,13 @@ class User(UserMixin):
def permissions(self, permissions):
raise AttributeError("Read only property")
- def has_permissions(self, permissions=[], any_=False, admin_override=False):
+ def has_permissions(self, *permissions, any_=False, admin_override=False):
+
+ unknown_permissions = set(permissions) - all_permissions
+
+ if unknown_permissions:
+ raise TypeError('{} are not valid permissions'.format(unknown_permissions))
+
# Only available to the platform admin user
if admin_override and self.platform_admin:
return True
@@ -166,7 +183,7 @@ class InvitedUser(object):
self.created_at = created_at
self.auth_type = auth_type
- def has_permissions(self, permissions):
+ def has_permissions(self, *permissions):
return set(self.permissions) > set(permissions)
def __eq__(self, other):
diff --git a/app/templates/main_nav.html b/app/templates/main_nav.html
index 2dc588d0e..c6165b2d7 100644
--- a/app/templates/main_nav.html
+++ b/app/templates/main_nav.html
@@ -44,17 +44,17 @@
+ {% if current_user.has_permissions('manage_api_keys', admin_override=True) %}
+
+ You can set up callbacks for received text messages on the
+ API integration page.
+
+ {% endif %}
{% else %}
Receiving text messages from your users is an
diff --git a/app/templates/views/signedout.html b/app/templates/views/signedout.html
index 5c5968a0d..f644893d3 100644
--- a/app/templates/views/signedout.html
+++ b/app/templates/views/signedout.html
@@ -120,12 +120,12 @@
diff --git a/app/templates/views/templates/template.html b/app/templates/views/templates/template.html
index 80623185f..e99f56609 100644
--- a/app/templates/views/templates/template.html
+++ b/app/templates/views/templates/template.html
@@ -63,7 +63,7 @@
{% endif %}
- {% if current_user.has_permissions(permissions=['manage_templates'], admin_override=True) %}
+ {% if current_user.has_permissions('manage_templates', admin_override=True) %}
{% if not template._template.archived %}
Delete this template
diff --git a/app/templates/views/terms-of-use.html b/app/templates/views/terms-of-use.html
index 8956b9cfb..9517e6796 100644
--- a/app/templates/views/terms-of-use.html
+++ b/app/templates/views/terms-of-use.html
@@ -39,7 +39,7 @@ Terms of use
get the right levels of consent (to send messages and to use data)
not send unsolicited messages, only ones related to a transaction or something the user has subscribed to be updated about (check the Service Manual if you’re not sure)