mirror of
https://github.com/GSA/notifications-admin.git
synced 2026-08-18 13:39:41 -04:00
Merge pull request #2638 from alphagov/copy-template-from-folders
Allow users to navigate folders when copying
This commit is contained in:
@@ -29,7 +29,7 @@ from app.main.forms import (
|
||||
)
|
||||
from app.main.views.send import get_example_csv_rows, get_sender_details
|
||||
from app.models.service import Service
|
||||
from app.models.template_list import TemplateList
|
||||
from app.models.template_list import TemplateList, TemplateLists
|
||||
from app.template_previews import TemplatePreview, get_page_count_for_letter
|
||||
from app.utils import (
|
||||
email_or_sms_not_enabled,
|
||||
@@ -322,16 +322,44 @@ def _add_template_by_type(template_type, template_folder_id):
|
||||
|
||||
|
||||
@main.route("/services/<service_id>/templates/copy")
|
||||
@main.route("/services/<service_id>/templates/copy/from-folder/<uuid:from_folder>")
|
||||
@main.route("/services/<service_id>/templates/copy/from-service/<uuid:from_service>")
|
||||
@main.route("/services/<service_id>/templates/copy/from-service/<uuid:from_service>/from-folder/<uuid:from_folder>")
|
||||
@login_required
|
||||
@user_has_permissions('manage_templates')
|
||||
def choose_template_to_copy(service_id):
|
||||
return render_template(
|
||||
'views/templates/copy.html',
|
||||
services=[
|
||||
Service(service)
|
||||
for service in user_api_client.get_services_for_user(current_user)
|
||||
],
|
||||
)
|
||||
def choose_template_to_copy(
|
||||
service_id,
|
||||
from_service=None,
|
||||
from_folder=None,
|
||||
):
|
||||
|
||||
if from_service:
|
||||
|
||||
current_user.belongs_to_service_or_403(from_service)
|
||||
service = Service(
|
||||
service_api_client.get_service(from_service)['data']
|
||||
)
|
||||
|
||||
return render_template(
|
||||
'views/templates/copy.html',
|
||||
services_templates_and_folders=TemplateList(
|
||||
service,
|
||||
template_folder_id=from_folder,
|
||||
),
|
||||
template_folder_path=service.get_template_folder_path(from_folder),
|
||||
from_service=service,
|
||||
search_form=SearchTemplatesForm(),
|
||||
)
|
||||
|
||||
else:
|
||||
return render_template(
|
||||
'views/templates/copy.html',
|
||||
services_templates_and_folders=TemplateLists([
|
||||
Service(service) for service in
|
||||
user_api_client.get_services_for_user(current_user)
|
||||
]),
|
||||
search_form=SearchTemplatesForm(),
|
||||
)
|
||||
|
||||
|
||||
@main.route("/services/<service_id>/templates/copy/<uuid:template_id>", methods=['GET', 'POST'])
|
||||
@@ -339,10 +367,7 @@ def choose_template_to_copy(service_id):
|
||||
@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)
|
||||
current_user.belongs_to_service_or_403(request.args.get('from_service'))
|
||||
|
||||
template = service_api_client.get_service_template(
|
||||
request.args.get('from_service'),
|
||||
|
||||
@@ -61,7 +61,7 @@ class Service():
|
||||
|
||||
def _get_by_id(self, things, id):
|
||||
try:
|
||||
return next(thing for thing in things if thing['id'] == id)
|
||||
return next(thing for thing in things if thing['id'] == str(id))
|
||||
except StopIteration:
|
||||
abort(404)
|
||||
|
||||
@@ -132,6 +132,8 @@ class Service():
|
||||
def get_templates(self, template_type='all', template_folder_id=None):
|
||||
if isinstance(template_type, str):
|
||||
template_type = [template_type]
|
||||
if template_folder_id:
|
||||
template_folder_id = str(template_folder_id)
|
||||
return [
|
||||
template for template in self.all_templates
|
||||
if (set(template_type) & {'all', template['template_type']})
|
||||
@@ -330,6 +332,10 @@ class Service():
|
||||
return {folder['id'] for folder in self.all_template_folders}
|
||||
|
||||
def get_template_folders(self, template_type='all', parent_folder_id=None):
|
||||
|
||||
if parent_folder_id:
|
||||
parent_folder_id = str(parent_folder_id)
|
||||
|
||||
return [
|
||||
folder for folder in self.all_template_folders
|
||||
if (
|
||||
|
||||
@@ -30,6 +30,7 @@ class TemplateList():
|
||||
self.template_type, item['id']
|
||||
),
|
||||
ancestors=ancestors,
|
||||
service_id=self.service.id,
|
||||
)
|
||||
for sub_item in self.get_templates_and_folders(
|
||||
template_type, item['id'], ancestors + [item]
|
||||
@@ -42,6 +43,7 @@ class TemplateList():
|
||||
yield TemplateListTemplate(
|
||||
item,
|
||||
ancestors=ancestors,
|
||||
service_id=self.service.id,
|
||||
)
|
||||
|
||||
@property
|
||||
@@ -59,8 +61,47 @@ class TemplateList():
|
||||
))
|
||||
|
||||
|
||||
class TemplateLists():
|
||||
|
||||
def __init__(self, services):
|
||||
self.services = sorted(
|
||||
services,
|
||||
key=lambda service: service.name.lower(),
|
||||
)
|
||||
|
||||
def __iter__(self):
|
||||
|
||||
if len(self.services) == 1:
|
||||
|
||||
for template_or_folder in TemplateList(self.services[0]):
|
||||
yield template_or_folder
|
||||
|
||||
return
|
||||
|
||||
for service in self.services:
|
||||
|
||||
template_list_service = TemplateListService(service)
|
||||
|
||||
yield template_list_service
|
||||
|
||||
for service_templates_and_folders in TemplateList(
|
||||
service
|
||||
).get_templates_and_folders(
|
||||
template_type='all',
|
||||
template_folder_id=None,
|
||||
ancestors=[template_list_service],
|
||||
):
|
||||
yield service_templates_and_folders
|
||||
|
||||
@property
|
||||
def templates_to_show(self):
|
||||
return bool(self.services)
|
||||
|
||||
|
||||
class TemplateListItem():
|
||||
|
||||
is_service = False
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
template_or_folder,
|
||||
@@ -79,8 +120,10 @@ class TemplateListTemplate(TemplateListItem):
|
||||
self,
|
||||
template,
|
||||
ancestors,
|
||||
service_id,
|
||||
):
|
||||
super().__init__(template, ancestors)
|
||||
self.service_id = service_id
|
||||
self.hint = {
|
||||
'email': 'Email template',
|
||||
'sms': 'Text message template',
|
||||
@@ -98,8 +141,10 @@ class TemplateListFolder(TemplateListItem):
|
||||
templates,
|
||||
folders,
|
||||
ancestors,
|
||||
service_id,
|
||||
):
|
||||
super().__init__(folder, ancestors)
|
||||
self.service_id = service_id
|
||||
self.number_of_templates = len(templates)
|
||||
self.number_of_folders = len(folders)
|
||||
|
||||
@@ -122,3 +167,24 @@ class TemplateListFolder(TemplateListItem):
|
||||
@property
|
||||
def hint(self):
|
||||
return ', '.join(self._hint_parts)
|
||||
|
||||
|
||||
class TemplateListService(TemplateListFolder):
|
||||
|
||||
is_service = True
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
service,
|
||||
):
|
||||
super().__init__(
|
||||
folder=service._dict,
|
||||
templates=service.get_templates(
|
||||
template_folder_id=None,
|
||||
),
|
||||
folders=service.get_template_folders(
|
||||
parent_folder_id=None,
|
||||
),
|
||||
ancestors=[],
|
||||
service_id=service.id,
|
||||
)
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
from itertools import chain
|
||||
|
||||
from flask import request, session
|
||||
from flask import abort, request, session
|
||||
from flask_login import AnonymousUserMixin, UserMixin
|
||||
|
||||
from app.utils import is_gov_user
|
||||
@@ -151,6 +151,10 @@ class User(UserMixin):
|
||||
def has_permission_for_service(self, service_id, permission):
|
||||
return permission in self._permissions.get(service_id, [])
|
||||
|
||||
def belongs_to_service_or_403(self, service_id):
|
||||
if str(service_id) not in self.services:
|
||||
abort(403)
|
||||
|
||||
def is_locked(self):
|
||||
return self.failed_login_count >= self.max_failed_login_count
|
||||
|
||||
|
||||
@@ -230,7 +230,7 @@ class UserApiClient(NotifyAdminAPIClient):
|
||||
}
|
||||
|
||||
def user_belongs_to_service(self, user, service_id):
|
||||
return service_id in self.get_service_ids_for_user(user)
|
||||
return str(service_id) in self.get_service_ids_for_user(user)
|
||||
|
||||
|
||||
user_api_client = UserApiClient()
|
||||
|
||||
@@ -28,6 +28,37 @@
|
||||
{% endmacro %}
|
||||
|
||||
|
||||
{% macro copy_folder_path(
|
||||
folder_path,
|
||||
current_service_id,
|
||||
from_service
|
||||
) %}
|
||||
{% if folder_path %}
|
||||
<h2 class="heading-medium folder-heading">
|
||||
{% if folder_path|length == 1 %}
|
||||
<a href="{{ url_for('.choose_template_to_copy', service_id=current_service_id) }}">Services</a>
|
||||
{{ folder_path_separator() }}
|
||||
{% endif %}
|
||||
{% for folder in folder_path %}
|
||||
{% if loop.last %}
|
||||
<span class="folder-heading-folder">
|
||||
{{ folder.name if folder.id else from_service.name }}
|
||||
</span>
|
||||
{% else %}
|
||||
{% if folder.id %}
|
||||
<a href="{{ url_for('.choose_template_to_copy', service_id=current_service_id, from_service=from_service.id, from_folder=folder.id) }}" class="folder-heading-folder">{{ folder.name }}</a> {% if not loop.last %}{{ folder_path_separator() }}{% endif %}
|
||||
{% elif folder.parent_id == None %}
|
||||
<a href="{{ url_for('.choose_template_to_copy', service_id=current_service_id, from_service=from_service.id, from_folder=folder.id) }}" class="folder-heading-folder">{{ from_service.name }}</a> {% if not loop.last %}{{ folder_path_separator() }}{% endif %}
|
||||
{% else %}
|
||||
<a href="{{ url_for('.choose_template_to_copy', service_id=current_service_id, from_service=from_service.id) }}">{{ from_service.name }}</a> {% if not loop.last %}{{ folder_path_separator() }}{% endif %}
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
</h2>
|
||||
{% endif %}
|
||||
{% endmacro %}
|
||||
|
||||
|
||||
{% macro page_title_folder_path(
|
||||
folders,
|
||||
fallback_page_title=None,
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
{% from "components/message-count-label.html" import message_count_label %}
|
||||
{% from "components/folder-path.html" import copy_folder_path, page_title_folder_path %}
|
||||
{% from "components/live-search.html" import live_search %}
|
||||
|
||||
{% extends "withnav_template.html" %}
|
||||
|
||||
@@ -8,30 +9,49 @@
|
||||
|
||||
{% block maincolumn_content %}
|
||||
|
||||
<div class="bottom-gutter-3-2">
|
||||
<div class="bottom-gutter-1-2">
|
||||
<h1 class="heading-large">Copy an existing template</h1>
|
||||
{{ copy_folder_path(template_folder_path, current_service.id, from_service) }}
|
||||
</div>
|
||||
<nav>
|
||||
{% for service in services %}
|
||||
{% set templates = service.get_templates(current_service.permissions) %}
|
||||
{% if templates and services|length > 1 %}
|
||||
<h2 class="">
|
||||
{{ service.name }}
|
||||
</h2>
|
||||
<div class="left-gutter-4-3 bottom-gutter-3-2">
|
||||
{% endif %}
|
||||
{% for template in templates %}
|
||||
<h2 class="message-name">
|
||||
<a href="{{ url_for('.copy_template', service_id=current_service.id, template_id=template.id, from_service=service.id) }}">{{ template.name }}</a>
|
||||
</h2>
|
||||
<p class="message-type">
|
||||
{{ message_count_label(1, template.template_type, suffix='')|capitalize }} template
|
||||
</p>
|
||||
{% endfor %}
|
||||
{% if templates %}
|
||||
{% if not services_templates_and_folders.templates_to_show %}
|
||||
<p class="template-list-empty">
|
||||
This folder is empty
|
||||
</p>
|
||||
{% else %}
|
||||
{{ live_search(target_selector='#template-list .template-list-item', show=True, form=search_form) }}
|
||||
<nav id="template-list">
|
||||
{% for item in services_templates_and_folders %}
|
||||
<div class="template-list-item {% if item.ancestors %}template-list-item-hidden-by-default{% endif %} {% if not item.ancestors %}template-list-item-without-ancestors{% endif %}">
|
||||
<h2 class="message-name {% if item.is_folder or (item.ancestors and not item.is_folder) %}template-list-folder{% endif %}">
|
||||
{% for ancestor in item.ancestors %}
|
||||
{% if ancestor.is_service %}
|
||||
<a href="{{ url_for('.choose_template_to_copy', service_id=current_service.id, from_service=ancestor.service_id) }}">
|
||||
{% else %}
|
||||
<a href="{{ url_for('.choose_template_to_copy', service_id=current_service.id, from_folder=ancestor.id) }}">
|
||||
{% endif %}
|
||||
{{ ancestor.name }}
|
||||
</a> <span class="message-name-separator">/</span>
|
||||
{% endfor %}
|
||||
{% if item.is_service %}
|
||||
<a href="{{ url_for('.choose_template_to_copy', service_id=current_service.id, from_service=item.service_id) }}">
|
||||
<span class="live-search-relevant">{{ item.name }}</span>
|
||||
</a>
|
||||
{% elif item.is_folder %}
|
||||
<a href="{{ url_for('.choose_template_to_copy', service_id=current_service.id, from_service=item.service_id, from_folder=item.id) }}">
|
||||
<span class="live-search-relevant">{{ item.name }}</span>
|
||||
</a>
|
||||
{% else %}
|
||||
<a href="{{ url_for('.copy_template', service_id=current_service.id, template_id=item.id, from_service=item.service_id) }}">
|
||||
<span class="live-search-relevant">{{ item.name }}</span>
|
||||
</a>
|
||||
{% endif %}
|
||||
</h2>
|
||||
<p class="message-type">
|
||||
{{ item.hint }}
|
||||
</p>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
</nav>
|
||||
{% endfor %}
|
||||
</nav>
|
||||
{% endif %}
|
||||
|
||||
{% endblock %}
|
||||
|
||||
Reference in New Issue
Block a user