diff --git a/app/assets/stylesheets/_grids.scss b/app/assets/stylesheets/_grids.scss index 84cbbe94c..a4b7fdbcb 100644 --- a/app/assets/stylesheets/_grids.scss +++ b/app/assets/stylesheets/_grids.scss @@ -81,6 +81,14 @@ margin-bottom: $gutter * 2; } +.left-gutter { + padding-left: $gutter; +} + +.left-gutter-4-3 { + padding-left: $gutter * 4 / 3; +} + .align-with-heading { display: block; text-align: center; diff --git a/app/main/forms.py b/app/main/forms.py index f0f7d790a..78c2d7dd2 100644 --- a/app/main/forms.py +++ b/app/main/forms.py @@ -837,14 +837,15 @@ class ChooseTemplateType(StripWhitespaceForm): ] ) - def __init__(self, include_letters=False, *args, **kwargs): + def __init__(self, include_letters=False, include_copy=False, *args, **kwargs): super().__init__(*args, **kwargs) self.template_type.choices = filter(None, [ ('email', 'Email'), ('sms', 'Text message'), - ('letter', 'Letter') if include_letters else None + ('letter', 'Letter') if include_letters else None, + ('copy-existing', 'Copy of an existing template') if include_copy else None, ]) diff --git a/app/main/views/find_users.py b/app/main/views/find_users.py index eff9830f5..71a25f66f 100644 --- a/app/main/views/find_users.py +++ b/app/main/views/find_users.py @@ -30,9 +30,9 @@ def find_users_by_email(): @user_is_platform_admin def user_information(user_id): user = user_api_client.get_user(user_id) - services = user_api_client.get_organisations_and_services_for_user(user) + services = user_api_client.get_services_for_user(user) return render_template( 'views/find-users/user-information.html', user=user, - services=services['services_without_organisations'], + services=services, ) diff --git a/app/main/views/templates.py b/app/main/views/templates.py index 1029b5fd8..5430b180a 100644 --- a/app/main/views/templates.py +++ b/app/main/views/templates.py @@ -9,7 +9,12 @@ from notifications_python_client.errors import HTTPError from notifications_utils.formatters import nl2br from notifications_utils.recipients import first_column_headings -from app import current_service, service_api_client, template_statistics_client +from app import ( + current_service, + service_api_client, + template_statistics_client, + user_api_client, +) from app.main import main from app.main.forms import ( ChooseTemplateType, @@ -202,11 +207,21 @@ def view_template_version_preview(service_id, template_id, version, filetype): def add_template_by_type(service_id): form = ChooseTemplateType( - include_letters='letter' in current_service['permissions'] + include_letters='letter' in current_service['permissions'], + include_copy=any(( + service_api_client.count_service_templates(service_id), + len(user_api_client.get_service_ids_for_user(current_user)) > 1, + )), ) if form.validate_on_submit(): + if form.template_type.data == 'copy-existing': + return redirect(url_for( + '.choose_template_to_copy', + service_id=service_id, + )) + if form.template_type.data == 'letter': blank_letter = service_api_client.create_service_template( 'Untitled', @@ -240,6 +255,51 @@ def add_template_by_type(service_id): return render_template('views/templates/add.html', form=form) +@main.route("/services//templates/copy") +@login_required +@user_has_permissions('manage_templates') +def choose_template_to_copy(service_id): + return render_template( + 'views/templates/copy.html', + services=[{ + 'name': service['name'], + 'id': service['id'], + 'templates': [ + template for template in + service_api_client.get_service_templates(service['id'])['data'] + if template['template_type'] in current_service['permissions'] + ], + } for service in user_api_client.get_services_for_user(current_user)], + ) + + +@main.route("/services//templates/copy/", methods=['GET', 'POST']) +@login_required +@user_has_permissions('manage_templates') +def copy_template(service_id, template_id): + + if not user_api_client.user_belongs_to_service( + current_user, request.args.get('from_service') + ): + abort(403) + + template = service_api_client.get_service_template( + request.args.get('from_service'), + str(template_id), + )['data'] + template['template_content'] = template['content'] + template['name'] = 'Copy of ‘{}’'.format(template['name']) + form = form_objects[template['template_type']](**template) + + return render_template( + 'views/edit-{}-template.html'.format(template['template_type']), + form=form, + template_type=template['template_type'], + heading_action='Add', + services=user_api_client.get_service_ids_for_user(current_user), + ) + + @main.route("/services//templates/action-blocked///") @login_required @user_has_permissions('manage_templates') diff --git a/app/navigation.py b/app/navigation.py index 076af3d12..54b13e6b7 100644 --- a/app/navigation.py +++ b/app/navigation.py @@ -125,6 +125,7 @@ class HeaderNavigation(Navigation): 'choose_account', 'choose_service', 'choose_template', + 'choose_template_to_copy', 'confirm_edit_organisation_name', 'confirm_redact_template', 'conversation', @@ -132,6 +133,7 @@ class HeaderNavigation(Navigation): 'conversation_reply_with_template', 'conversation_updates', 'cookies', + 'copy_template', 'create_api_key', 'delete_service_template', 'delivery_and_failure', @@ -291,8 +293,10 @@ class MainNavigation(Navigation): 'check_messages', 'check_notification', 'choose_template', + 'choose_template_to_copy', 'confirm_redact_template', 'conversation_reply', + 'copy_template', 'delete_service_template', 'edit_service_template', 'send_messages', @@ -544,6 +548,7 @@ class CaseworkNavigation(Navigation): 'check_notification', 'choose_account', 'choose_service', + 'choose_template_to_copy', 'confirm_edit_organisation_name', 'confirm_redact_template', 'conversation', @@ -551,6 +556,7 @@ class CaseworkNavigation(Navigation): 'conversation_reply_with_template', 'conversation_updates', 'cookies', + 'copy_template', 'create_api_key', 'create_email_branding', 'delete_service_template', @@ -771,12 +777,14 @@ class OrgNavigation(Navigation): 'choose_account', 'choose_service', 'choose_template', + 'choose_template_to_copy', 'confirm_redact_template', 'conversation', 'conversation_reply', 'conversation_reply_with_template', 'conversation_updates', 'cookies', + 'copy_template', 'create_api_key', 'create_email_branding', 'delete_service_template', diff --git a/app/notify_client/user_api_client.py b/app/notify_client/user_api_client.py index 02b9bfd5b..7a898ea21 100644 --- a/app/notify_client/user_api_client.py +++ b/app/notify_client/user_api_client.py @@ -1,3 +1,5 @@ +from itertools import chain + from notifications_python_client.errors import HTTPError from app.notify_client import NotifyAdminAPIClient, cache @@ -209,3 +211,17 @@ class UserApiClient(NotifyAdminAPIClient): def get_organisations_and_services_for_user(self, user): endpoint = '/user/{}/organisations-and-services'.format(user.id) return self.get(endpoint) + + def get_services_for_user(self, user): + orgs_and_services_for_user = self.get_organisations_and_services_for_user(user) + return orgs_and_services_for_user['services_without_organisations'] + next(chain( + org['services'] for org in orgs_and_services_for_user['organisations'] + ), []) + + def get_service_ids_for_user(self, user): + return { + service['id'] for service in self.get_services_for_user(user) + } + + def user_belongs_to_service(self, user, service_id): + return service_id in self.get_service_ids_for_user(user) diff --git a/app/templates/admin_template.html b/app/templates/admin_template.html index ae715f45f..bdd34ba41 100644 --- a/app/templates/admin_template.html +++ b/app/templates/admin_template.html @@ -109,7 +109,7 @@ {% block footer_support_links %}