Add a ‘See dashboard’ permission

Our research and prototyping around ‘basic view’ found that:
- a lot of users who send messages rarely or never look at the dashboard
  (yet it’s the first page they see when they sign in)
- team managers like the idea of taking away things that users don’t
  need in order to make the interface simpler

We’ve disentangled the simpler way of sending messages from being part
of ‘basic view’. This means we can give managers the option of taking
away the dashboard as an independent choice, not something that’s
wrapped up in a separate ‘view’.

I think that this checkbox is a more straightforward proposition than
‘basic view’ ever was (despite all the work we did to explain it and
develop the nested checkbox pattern). In research users would often
explain the feature back to us as being about hiding the dashboard – we
should try to make Notify operate in terms of concepts that come
naturally to people wherever possible.
This commit is contained in:
Chris Hill-Scott
2018-08-06 11:10:37 +01:00
parent d2fc613270
commit 646ba6e8c3
10 changed files with 220 additions and 387 deletions

View File

@@ -256,13 +256,13 @@ class RegisterUserFromOrgInviteForm(StripWhitespaceForm):
auth_type = HiddenField('auth_type', validators=[DataRequired()])
class AbstractPermissionsForm(StripWhitespaceForm):
class PermissionsForm(StripWhitespaceForm):
view_activity = HiddenField("View activity")
send_messages = BooleanField("Send messages from existing templates")
view_activity = BooleanField("See dashboard and reports")
send_messages = BooleanField("Send messages")
manage_templates = BooleanField("Add and edit templates")
manage_service = BooleanField("Manage this service and its team")
manage_api_keys = BooleanField("Create and revoke API keys")
manage_api_keys = BooleanField("Manage API keys")
login_authentication = RadioField(
'Sign in using',
@@ -277,38 +277,18 @@ class AbstractPermissionsForm(StripWhitespaceForm):
def permissions(self):
return {role for role in roles.keys() if self[role].data is True}
class AdminPermissionsForm(AbstractPermissionsForm):
def process(self, *args, **kwargs):
super().process(*args, **kwargs)
# view_activity is a default role to be added to all users.
self.view_activity.data = True
@classmethod
def from_user(cls, user, service_id):
return cls(
**{
role: user.has_permission_for_service(service_id, role)
for role in roles.keys()
},
login_authentication=user.auth_type
)
class CaseworkingPermissionsForm(AbstractPermissionsForm):
def process(self, *args, **kwargs):
super().process(*args, **kwargs)
if self.user_type.data == 'admin':
self.view_activity.data = True
elif self.user_type.data == 'caseworker':
self.view_activity.data = False
self.manage_templates.data = False
self.manage_service.data = False
self.manage_api_keys.data = False
self.send_messages.data = True
user_type = RadioField(
'User type',
choices=[
('caseworker', 'Basic view'),
('admin', 'Admin view'),
],
)
class AbstractInviteUserForm(StripWhitespaceForm):
class InviteUserForm(PermissionsForm):
email_address = email_address(gov_user=False)
def __init__(self, invalid_email_address, *args, **kwargs):
@@ -320,14 +300,6 @@ class AbstractInviteUserForm(StripWhitespaceForm):
raise ValidationError("You cant send an invitation to yourself")
class AdminInviteUserForm(AbstractInviteUserForm, AdminPermissionsForm):
pass
class CaseworkingInviteUserForm(AbstractInviteUserForm, CaseworkingPermissionsForm):
pass
class InviteOrgUserForm(StripWhitespaceForm):
email_address = email_address(gov_user=False)

View File

@@ -1,5 +1,3 @@
from functools import partial
from flask import abort, flash, redirect, render_template, request, url_for
from flask_login import current_user, login_required
from notifications_python_client.errors import HTTPError
@@ -11,14 +9,7 @@ from app import (
user_api_client,
)
from app.main import main
from app.main.forms import (
AdminInviteUserForm,
AdminPermissionsForm,
CaseworkingInviteUserForm,
CaseworkingPermissionsForm,
SearchUsersForm,
)
from app.notify_client.models import roles
from app.main.forms import InviteUserForm, PermissionsForm, SearchUsersForm
from app.utils import user_has_permissions
@@ -48,12 +39,7 @@ def manage_users(service_id):
@user_has_permissions('manage_service')
def invite_user(service_id):
if current_service.has_permission('caseworking'):
form = CaseworkingInviteUserForm
else:
form = AdminInviteUserForm
form = form(invalid_email_address=current_user.email_address)
form = InviteUserForm(invalid_email_address=current_user.email_address)
service_has_email_auth = current_service.has_permission('email_auth')
if not service_has_email_auth:
@@ -89,18 +75,7 @@ def edit_user_permissions(service_id, user_id):
user = user_api_client.get_user(user_id)
user_has_no_mobile_number = user.mobile_number is None
if current_service.has_permission('caseworking'):
form = partial(
CaseworkingPermissionsForm,
user_type='admin' if user.has_permission_for_service(service_id, 'view_activity') else 'caseworker',
)
else:
form = AdminPermissionsForm
form = form(
**{role: user.has_permission_for_service(service_id, role) for role in roles.keys()},
login_authentication=user.auth_type
)
form = PermissionsForm.from_user(user, service_id)
if form.validate_on_submit():
user_api_client.set_user_permissions(
@@ -125,11 +100,7 @@ def edit_user_permissions(service_id, user_id):
@user_has_permissions('manage_service')
def remove_user_from_service(service_id, user_id):
user = user_api_client.get_user(user_id)
# Need to make the email address read only, or a disabled field?
# Do it through the template or the form class?
form = AdminPermissionsForm(**{
role: user.has_permission_for_service(service_id, role) for role in roles.keys()
})
form = PermissionsForm.from_user(user, service_id)
if request.method == 'POST':
try:

View File

@@ -197,7 +197,7 @@ class InvitedUser(object):
return set(self.permissions) > set(permissions)
def has_permission_for_service(self, service_id, permission):
if self.status == 'cancelled' and permission != 'view_activity':
if self.status == 'cancelled':
return False
return self.service == service_id and permission in self.permissions

View File

@@ -1,10 +1,18 @@
{% if help %}
{% include 'partials/tour.html' %}
{% elif current_user.has_permissions('view_activity') %}
{% else %}
<nav class="navigation">
<ul>
{% if current_user.has_permissions('view_activity') %}
<li><a href="{{ url_for('.service_dashboard', service_id=current_service.id) }}" {{ main_navigation.is_selected('dashboard') }}>Dashboard</a></li>
{% endif %}
<li><a href="{{ url_for('.choose_template', service_id=current_service.id) }}" {{ main_navigation.is_selected('templates') }}>Templates</a></li>
{% if not current_user.has_permissions('view_activity') %}
<li><a href="{{ url_for('.view_notifications', service_id=current_service.id, status='sending,delivered,failed') }}" {{ casework_navigation.is_selected('sent-messages') }}>Sent messages</a></li>
{% if current_service.has_jobs() %}
<li><a href="{{ url_for('.view_jobs', service_id=current_service.id) }}" {{ casework_navigation.is_selected('uploaded-files') }}>Uploaded files</a></li>
{% endif %}
{% endif %}
<li><a href="{{ url_for('.manage_users', service_id=current_service.id) }}" {{ main_navigation.is_selected('team-members') }}>Team members</a></li>
{% if current_user.has_permissions('manage_service') %}
<li><a href="{{ url_for('.usage', service_id=current_service.id) }}" {{ main_navigation.is_selected('usage') }}>Usage</a></li>
@@ -17,15 +25,4 @@
{% endif %}
</ul>
</nav>
{% else %}
<nav class="navigation">
<ul>
<li><a href="{{ url_for('.choose_template', service_id=current_service.id) }}" {{ casework_navigation.is_selected('send-one-off') }}>Templates</a></li>
<li><a href="{{ url_for('.view_notifications', service_id=current_service.id, status='sending,delivered,failed') }}" {{ casework_navigation.is_selected('sent-messages') }}>Sent messages</a></li>
{% if current_service.has_jobs() %}
<li><a href="{{ url_for('.view_jobs', service_id=current_service.id) }}" {{ casework_navigation.is_selected('uploaded-files') }}>Uploaded files</a></li>
{% endif %}
</ul>
</nav>
{% endif %}

View File

@@ -63,35 +63,26 @@
</h3>
<ul class="tick-cross-list">
<div class="tick-cross-list-permissions">
{% if user.has_permission_for_service(current_service.id, 'view_activity') %}
{% if 'caseworking' in current_service.permissions %}
{{ tick_cross(
user.status != 'cancelled',
'Admin view'
) }}
{% endif %}
{{ tick_cross(
user.has_permission_for_service(current_service.id, 'send_messages'),
'Send messages'
) }}
{{ tick_cross(
user.has_permission_for_service(current_service.id, 'manage_templates'),
'Add and edit templates'
) }}
{{ tick_cross(
user.has_permission_for_service(current_service.id, 'manage_service'),
'Manage service'
) }}
{{ tick_cross(
user.has_permission_for_service(current_service.id, 'manage_api_keys'),
'Access API keys'
) }}
{% else %}
{{ tick_cross(
user.status != 'cancelled',
'Basic view'
) }}
{% endif %}
{{ tick_cross(
user.has_permission_for_service(current_service.id, 'view_activity'),
'See dashboard and reports'
) }}
{{ tick_cross(
user.has_permission_for_service(current_service.id, 'send_messages'),
'Send messages'
) }}
{{ tick_cross(
user.has_permission_for_service(current_service.id, 'manage_templates'),
'Add and edit templates'
) }}
{{ tick_cross(
user.has_permission_for_service(current_service.id, 'manage_service'),
'Manage service'
) }}
{{ tick_cross(
user.has_permission_for_service(current_service.id, 'manage_api_keys'),
'Access API keys'
) }}
{% if current_service.has_permission('email_auth') %}
<div class="tick-cross-list-hint">
{% if user.auth_type == 'sms_auth' %}

View File

@@ -1,56 +1,16 @@
{% from "components/checkbox.html" import checkbox %}
{% from "components/radios.html" import radio, radios, radios_wrapper, conditional_radio_panel %}
{% if 'caseworking' in current_service.permissions %}
<div class="conditional-radios" data-module='conditional-radios'>
{% call radios_wrapper(form.user_type, hide_legend=True) %}
{% for option in form.user_type %}
<div class="bottom-gutter-1-3">
{{ radio(option, option_hints={
'admin': 'Show dashboard and other options',
'caseworker': 'Send messages, hide dashboard and other options'
}) }}
</div>
{% if option.data == 'admin' %}
{% call conditional_radio_panel('admin') %}
<fieldset class="form-group">
<legend class="form-label">
Extra permissions
</legend>
{{ checkbox(form.send_messages) }}
{{ checkbox(form.manage_templates) }}
{{ checkbox(form.manage_service) }}
{{ checkbox(form.manage_api_keys) }}
</fieldset>
{% endcall %}
{% endif %}
{% endfor %}
{% endcall %}
</div>
{% else %}
<fieldset class="form-group">
<legend class="form-label">
Permissions
</legend>
{{ checkbox(form.send_messages) }}
{{ checkbox(form.manage_templates) }}
{{ checkbox(form.manage_service) }}
{{ checkbox(form.manage_api_keys) }}
</fieldset>
<div class="bottom-gutter">
<p class="form-label">
All team members can see
</p>
<ul class="list list-bullet">
<li>templates</li>
<li>history of sent messages</li>
<li>who the other team members are</li>
</ul>
</div>
{% endif %}
<fieldset class="form-group">
<legend class="form-label visually-hidden">
Permissions
</legend>
{{ checkbox(form.view_activity) }}
{{ checkbox(form.send_messages) }}
{{ checkbox(form.manage_templates) }}
{{ checkbox(form.manage_service) }}
{{ checkbox(form.manage_api_keys) }}
</fieldset>
{% if service_has_email_auth %}
{% if user_has_no_mobile_number %}