mirror of
https://github.com/GSA/notifications-admin.git
synced 2026-04-26 20:21:04 -04:00
Not everyone knows how to use `ctrl` + `f`, and it’s not scoped to just the list of templates. The template you want to work with is often not the first one in the list, but ordering by created at is useful for other reasons (mainly around first time use). This commit adds a find as you type control which aims to give users a quick way of getting to the template they want to work with.
414 lines
14 KiB
Python
414 lines
14 KiB
Python
from datetime import datetime, timedelta
|
|
from string import ascii_uppercase
|
|
|
|
from flask import request, render_template, redirect, url_for, flash, abort, send_file
|
|
from flask_login import login_required, current_user
|
|
from flask_weasyprint import HTML, render_pdf
|
|
from dateutil.parser import parse
|
|
|
|
from notifications_utils.field import escape_html
|
|
from notifications_utils.template import LetterPreviewTemplate
|
|
from notifications_utils.recipients import first_column_headings
|
|
from notifications_python_client.errors import HTTPError
|
|
|
|
from app.main import main
|
|
from app.utils import user_has_permissions, get_template, png_from_pdf
|
|
from app.main.forms import (
|
|
ChooseTemplateType,
|
|
SMSTemplateForm,
|
|
EmailTemplateForm,
|
|
LetterTemplateForm,
|
|
SearchTemplatesForm,
|
|
)
|
|
from app.main.views.send import get_example_csv_rows
|
|
from app import service_api_client, current_service, template_statistics_client
|
|
|
|
|
|
form_objects = {
|
|
'email': EmailTemplateForm,
|
|
'sms': SMSTemplateForm,
|
|
'letter': LetterTemplateForm
|
|
}
|
|
|
|
page_headings = {
|
|
'email': 'email',
|
|
'sms': 'text message'
|
|
}
|
|
|
|
|
|
@main.route("/services/<service_id>/templates", methods=['GET'])
|
|
@login_required
|
|
@user_has_permissions(
|
|
'view_activity',
|
|
'send_texts',
|
|
'send_emails',
|
|
'manage_templates',
|
|
'manage_api_keys',
|
|
admin_override=True,
|
|
any_=True,
|
|
)
|
|
def choose_template(service_id):
|
|
return render_template(
|
|
'views/templates/choose.html',
|
|
templates=service_api_client.get_service_templates(service_id)['data'],
|
|
search_form=SearchTemplatesForm(),
|
|
)
|
|
|
|
|
|
@main.route("/services/<service_id>/templates/<template_id>")
|
|
@login_required
|
|
@user_has_permissions(
|
|
'view_activity',
|
|
'send_texts',
|
|
'send_emails',
|
|
'manage_templates',
|
|
'manage_api_keys',
|
|
admin_override=True, any_=True
|
|
)
|
|
def view_template(service_id, template_id):
|
|
return render_template(
|
|
'views/templates/template.html',
|
|
template=get_template(
|
|
service_api_client.get_service_template(service_id, template_id)['data'],
|
|
current_service,
|
|
expand_emails=True,
|
|
letter_preview_url=url_for('.view_template', service_id=service_id, template_id=template_id)
|
|
)
|
|
)
|
|
|
|
|
|
@main.route("/services/<service_id>/templates/<template_id>.pdf")
|
|
@login_required
|
|
@user_has_permissions('view_activity', admin_override=True)
|
|
def view_letter_template_as_pdf(service_id, template_id):
|
|
return render_pdf(HTML(string=str(
|
|
LetterPreviewTemplate(
|
|
service_api_client.get_service_template(service_id, template_id)['data'],
|
|
contact_block=current_service['letter_contact_block'],
|
|
)
|
|
)))
|
|
|
|
|
|
@main.route("/services/<service_id>/templates/<template_id>.png")
|
|
@login_required
|
|
@user_has_permissions('view_activity', admin_override=True)
|
|
def view_letter_template_as_png(service_id, template_id):
|
|
return send_file(**png_from_pdf(
|
|
view_letter_template_as_pdf(service_id, template_id)
|
|
))
|
|
|
|
|
|
def _view_template_version(service_id, template_id, version, letters_as_pdf=False):
|
|
return dict(template=get_template(
|
|
service_api_client.get_service_template(service_id, template_id, version=version)['data'],
|
|
current_service,
|
|
expand_emails=True,
|
|
letter_preview_url=url_for(
|
|
'.view_template_version',
|
|
service_id=service_id,
|
|
template_id=template_id,
|
|
version=version,
|
|
) if not letters_as_pdf else None
|
|
))
|
|
|
|
|
|
@main.route("/services/<service_id>/templates/<template_id>/version/<int:version>")
|
|
@login_required
|
|
@user_has_permissions(
|
|
'view_activity',
|
|
'send_texts',
|
|
'send_emails',
|
|
'manage_templates',
|
|
'manage_api_keys',
|
|
admin_override=True,
|
|
any_=True
|
|
)
|
|
def view_template_version(service_id, template_id, version):
|
|
return render_template(
|
|
'views/templates/template_history.html',
|
|
**_view_template_version(service_id=service_id, template_id=template_id, version=version)
|
|
)
|
|
|
|
|
|
@main.route("/services/<service_id>/templates/<template_id>/version/<int:version>.pdf")
|
|
@login_required
|
|
@user_has_permissions(
|
|
'view_activity',
|
|
'send_texts',
|
|
'send_emails',
|
|
'manage_templates',
|
|
'manage_api_keys',
|
|
admin_override=True,
|
|
any_=True
|
|
)
|
|
def view_template_version_as_pdf(service_id, template_id, version):
|
|
return render_pdf(HTML(string=str(
|
|
LetterPreviewTemplate(
|
|
service_api_client.get_service_template(service_id, template_id, version=version)['data'],
|
|
contact_block=current_service['letter_contact_block'],
|
|
)
|
|
)))
|
|
|
|
|
|
@main.route("/services/<service_id>/templates/<template_id>/version/<int:version>.png")
|
|
@login_required
|
|
@user_has_permissions(
|
|
'view_activity',
|
|
'send_texts',
|
|
'send_emails',
|
|
'manage_templates',
|
|
'manage_api_keys',
|
|
admin_override=True,
|
|
any_=True
|
|
)
|
|
def view_template_version_as_png(service_id, template_id, version):
|
|
return send_file(**png_from_pdf(
|
|
view_template_version_as_pdf(service_id, template_id, version)
|
|
))
|
|
|
|
|
|
@main.route("/services/<service_id>/templates/add", methods=['GET', 'POST'])
|
|
@login_required
|
|
@user_has_permissions('manage_templates', admin_override=True)
|
|
def add_template_by_type(service_id):
|
|
|
|
form = ChooseTemplateType(
|
|
include_letters=current_service['can_send_letters']
|
|
)
|
|
|
|
if form.validate_on_submit():
|
|
return redirect(url_for(
|
|
'.add_service_template',
|
|
service_id=service_id,
|
|
template_type=form.template_type.data,
|
|
))
|
|
|
|
return render_template('views/templates/add.html', form=form)
|
|
|
|
|
|
@main.route("/services/<service_id>/templates/add-<template_type>", methods=['GET', 'POST'])
|
|
@login_required
|
|
@user_has_permissions('manage_templates', admin_override=True)
|
|
def add_service_template(service_id, template_type):
|
|
|
|
if template_type not in ['sms', 'email', 'letter']:
|
|
abort(404)
|
|
if not current_service['can_send_letters'] and template_type == 'letter':
|
|
abort(403)
|
|
|
|
form = form_objects[template_type]()
|
|
if form.validate_on_submit():
|
|
if form.process_type.data == 'priority':
|
|
abort_403_if_not_admin_user()
|
|
try:
|
|
service_api_client.create_service_template(
|
|
form.name.data,
|
|
template_type,
|
|
form.template_content.data,
|
|
service_id,
|
|
form.subject.data if hasattr(form, 'subject') else None,
|
|
form.process_type.data
|
|
)
|
|
except HTTPError as e:
|
|
if (
|
|
e.status_code == 400 and
|
|
'content' in e.message and
|
|
any(['character count greater than' in x for x in e.message['content']])
|
|
):
|
|
form.template_content.errors.extend(e.message['content'])
|
|
else:
|
|
raise e
|
|
else:
|
|
return redirect(
|
|
url_for('.choose_template', service_id=service_id)
|
|
)
|
|
|
|
return render_template(
|
|
'views/edit-{}-template.html'.format(template_type),
|
|
form=form,
|
|
template_type=template_type,
|
|
heading_action='Add'
|
|
)
|
|
|
|
|
|
def abort_403_if_not_admin_user():
|
|
if not current_user.has_permissions([], admin_override=True):
|
|
abort(403)
|
|
|
|
|
|
@main.route("/services/<service_id>/templates/<template_id>/edit", methods=['GET', 'POST'])
|
|
@login_required
|
|
@user_has_permissions('manage_templates', admin_override=True)
|
|
def edit_service_template(service_id, template_id):
|
|
template = service_api_client.get_service_template(service_id, template_id)['data']
|
|
template['template_content'] = template['content']
|
|
form = form_objects[template['template_type']](**template)
|
|
|
|
if form.validate_on_submit():
|
|
if form.process_type.data != template['process_type']:
|
|
abort_403_if_not_admin_user()
|
|
|
|
subject = escape_html(form.subject.data) if hasattr(form, 'subject') else None
|
|
new_template = get_template({
|
|
'name': form.name.data,
|
|
'content': form.template_content.data,
|
|
'subject': subject,
|
|
'template_type': template['template_type'],
|
|
'id': template['id'],
|
|
'process_type': form.process_type.data
|
|
}, current_service)
|
|
template_change = get_template(template, current_service).compare_to(new_template)
|
|
if template_change.has_different_placeholders and not request.form.get('confirm'):
|
|
return render_template(
|
|
'views/templates/breaking-change.html',
|
|
template_change=template_change,
|
|
new_template=new_template,
|
|
column_headings=list(ascii_uppercase[:len(new_template.placeholders) + 1]),
|
|
example_rows=[
|
|
first_column_headings[new_template.template_type] + list(new_template.placeholders),
|
|
get_example_csv_rows(new_template),
|
|
get_example_csv_rows(new_template)
|
|
],
|
|
form=form
|
|
)
|
|
try:
|
|
service_api_client.update_service_template(
|
|
template_id,
|
|
form.name.data,
|
|
template['template_type'],
|
|
form.template_content.data,
|
|
service_id,
|
|
subject,
|
|
form.process_type.data
|
|
)
|
|
except HTTPError as e:
|
|
if e.status_code == 400:
|
|
if 'content' in e.message and any(['character count greater than' in x for x in e.message['content']]):
|
|
form.template_content.errors.extend(e.message['content'])
|
|
else:
|
|
raise e
|
|
else:
|
|
raise e
|
|
else:
|
|
return redirect(url_for(
|
|
'.view_template',
|
|
service_id=service_id,
|
|
template_id=template_id
|
|
))
|
|
return render_template(
|
|
'views/edit-{}-template.html'.format(template['template_type']),
|
|
form=form,
|
|
template_id=template_id,
|
|
template_type=template['template_type'],
|
|
heading_action='Edit'
|
|
)
|
|
|
|
|
|
@main.route("/services/<service_id>/templates/<template_id>/delete", methods=['GET', 'POST'])
|
|
@login_required
|
|
@user_has_permissions('manage_templates', admin_override=True)
|
|
def delete_service_template(service_id, template_id):
|
|
template = service_api_client.get_service_template(service_id, template_id)['data']
|
|
|
|
if request.method == 'POST':
|
|
service_api_client.delete_service_template(service_id, template_id)
|
|
return redirect(url_for(
|
|
'.choose_template',
|
|
service_id=service_id,
|
|
template_type=template['template_type']
|
|
))
|
|
|
|
template['template_content'] = template['content']
|
|
form = form_objects[template['template_type']](**template)
|
|
|
|
try:
|
|
last_used_notification = template_statistics_client.get_template_statistics_for_template(
|
|
service_id, template['id']
|
|
)
|
|
message = '{} was last used {} ago'.format(
|
|
last_used_notification['template']['name'],
|
|
get_human_readable_delta(
|
|
parse(last_used_notification['created_at']).replace(tzinfo=None),
|
|
datetime.utcnow())
|
|
)
|
|
except HTTPError as e:
|
|
if e.status_code == 404:
|
|
message = '{} has never been used'.format(template['name'])
|
|
else:
|
|
raise e
|
|
|
|
flash('{}. Are you sure you want to delete it?'.format(message), 'delete')
|
|
return render_template(
|
|
'views/edit-{}-template.html'.format(template['template_type']),
|
|
h1='Edit template',
|
|
form=form,
|
|
template_id=template_id)
|
|
|
|
|
|
@main.route('/services/<service_id>/templates/<template_id>/versions')
|
|
@login_required
|
|
@user_has_permissions(
|
|
'view_activity',
|
|
'send_texts',
|
|
'send_emails',
|
|
'manage_templates',
|
|
'manage_api_keys',
|
|
admin_override=True,
|
|
any_=True
|
|
)
|
|
def view_template_versions(service_id, template_id):
|
|
return render_template(
|
|
'views/templates/choose_history.html',
|
|
versions=[
|
|
get_template(
|
|
template,
|
|
current_service,
|
|
expand_emails=True,
|
|
letter_preview_url=url_for(
|
|
'.view_template_version',
|
|
service_id=service_id,
|
|
template_id=template_id,
|
|
version=template['version'],
|
|
)
|
|
)
|
|
for template in service_api_client.get_service_template_versions(service_id, template_id)['data']
|
|
]
|
|
)
|
|
|
|
|
|
def get_last_use_message(template_name, template_statistics):
|
|
try:
|
|
most_recent_use = max(
|
|
parse(template_stats['updated_at']).replace(tzinfo=None)
|
|
for template_stats in template_statistics
|
|
)
|
|
except ValueError:
|
|
return '{} has never been used'.format(template_name)
|
|
|
|
return '{} was last used {} ago'.format(
|
|
template_name,
|
|
get_human_readable_delta(most_recent_use, datetime.utcnow())
|
|
)
|
|
|
|
|
|
def get_human_readable_delta(from_time, until_time):
|
|
delta = until_time - from_time
|
|
if delta < timedelta(seconds=60):
|
|
return 'under a minute'
|
|
elif delta < timedelta(hours=1):
|
|
minutes = int(delta.seconds / 60)
|
|
return '{} minute{}'.format(minutes, '' if minutes == 1 else 's')
|
|
elif delta < timedelta(days=1):
|
|
hours = int(delta.seconds / 3600)
|
|
return '{} hour{}'.format(hours, '' if hours == 1 else 's')
|
|
else:
|
|
days = delta.days
|
|
return '{} day{}'.format(days, '' if days == 1 else 's')
|
|
|
|
|
|
def should_show_template(template_type):
|
|
return (
|
|
template_type != 'letter' or
|
|
current_service['can_send_letters']
|
|
)
|