mirror of
https://github.com/GSA/notifications-admin.git
synced 2026-09-11 10:28:41 -04:00
Merge branch 'master' into pyup-update-whitenoise-4.1.1-to-4.1.2
This commit is contained in:
+11
-10
@@ -35,6 +35,7 @@ from notifications_utils.recipients import (
|
|||||||
)
|
)
|
||||||
from notifications_utils.formatters import formatted_list
|
from notifications_utils.formatters import formatted_list
|
||||||
from notifications_utils.sanitise_text import SanitiseASCII
|
from notifications_utils.sanitise_text import SanitiseASCII
|
||||||
|
from notifications_utils.timezones import utc_string_to_aware_gmt_datetime
|
||||||
from werkzeug.exceptions import abort, HTTPException as WerkzeugHTTPException
|
from werkzeug.exceptions import abort, HTTPException as WerkzeugHTTPException
|
||||||
from werkzeug.local import LocalProxy
|
from werkzeug.local import LocalProxy
|
||||||
|
|
||||||
@@ -69,7 +70,7 @@ from app.notify_client.complaint_api_client import complaint_api_client
|
|||||||
from app.notify_client.platform_stats_api_client import platform_stats_api_client
|
from app.notify_client.platform_stats_api_client import platform_stats_api_client
|
||||||
from app.notify_client.template_folder_api_client import template_folder_api_client
|
from app.notify_client.template_folder_api_client import template_folder_api_client
|
||||||
from app.commands import setup_commands
|
from app.commands import setup_commands
|
||||||
from app.utils import get_cdn_domain, gmt_timezones, id_safe
|
from app.utils import get_cdn_domain, id_safe
|
||||||
|
|
||||||
login_manager = LoginManager()
|
login_manager = LoginManager()
|
||||||
csrf = CSRFProtect()
|
csrf = CSRFProtect()
|
||||||
@@ -256,17 +257,17 @@ def format_datetime_numeric(date):
|
|||||||
|
|
||||||
|
|
||||||
def format_date_numeric(date):
|
def format_date_numeric(date):
|
||||||
return gmt_timezones(date).strftime('%Y-%m-%d')
|
return utc_string_to_aware_gmt_datetime(date).strftime('%Y-%m-%d')
|
||||||
|
|
||||||
|
|
||||||
def format_time_24h(date):
|
def format_time_24h(date):
|
||||||
return gmt_timezones(date).strftime('%H:%M')
|
return utc_string_to_aware_gmt_datetime(date).strftime('%H:%M')
|
||||||
|
|
||||||
|
|
||||||
def get_human_day(time):
|
def get_human_day(time):
|
||||||
|
|
||||||
# Add 1 minute to transform 00:00 into ‘midnight today’ instead of ‘midnight tomorrow’
|
# Add 1 minute to transform 00:00 into ‘midnight today’ instead of ‘midnight tomorrow’
|
||||||
date = (gmt_timezones(time) - timedelta(minutes=1)).date()
|
date = (utc_string_to_aware_gmt_datetime(time) - timedelta(minutes=1)).date()
|
||||||
if date == (datetime.utcnow() + timedelta(days=1)).date():
|
if date == (datetime.utcnow() + timedelta(days=1)).date():
|
||||||
return 'tomorrow'
|
return 'tomorrow'
|
||||||
if date == datetime.utcnow().date():
|
if date == datetime.utcnow().date():
|
||||||
@@ -281,21 +282,21 @@ def format_time(date):
|
|||||||
'12:00AM': 'Midnight',
|
'12:00AM': 'Midnight',
|
||||||
'12:00PM': 'Midday'
|
'12:00PM': 'Midday'
|
||||||
}.get(
|
}.get(
|
||||||
gmt_timezones(date).strftime('%-I:%M%p'),
|
utc_string_to_aware_gmt_datetime(date).strftime('%-I:%M%p'),
|
||||||
gmt_timezones(date).strftime('%-I:%M%p')
|
utc_string_to_aware_gmt_datetime(date).strftime('%-I:%M%p')
|
||||||
).lower()
|
).lower()
|
||||||
|
|
||||||
|
|
||||||
def format_date(date):
|
def format_date(date):
|
||||||
return gmt_timezones(date).strftime('%A %d %B %Y')
|
return utc_string_to_aware_gmt_datetime(date).strftime('%A %d %B %Y')
|
||||||
|
|
||||||
|
|
||||||
def format_date_normal(date):
|
def format_date_normal(date):
|
||||||
return gmt_timezones(date).strftime('%d %B %Y').lstrip('0')
|
return utc_string_to_aware_gmt_datetime(date).strftime('%d %B %Y').lstrip('0')
|
||||||
|
|
||||||
|
|
||||||
def format_date_short(date):
|
def format_date_short(date):
|
||||||
return _format_datetime_short(gmt_timezones(date))
|
return _format_datetime_short(utc_string_to_aware_gmt_datetime(date))
|
||||||
|
|
||||||
|
|
||||||
def _format_datetime_short(datetime):
|
def _format_datetime_short(datetime):
|
||||||
@@ -306,7 +307,7 @@ def format_delta(date):
|
|||||||
delta = (
|
delta = (
|
||||||
datetime.now(timezone.utc)
|
datetime.now(timezone.utc)
|
||||||
) - (
|
) - (
|
||||||
gmt_timezones(date)
|
utc_string_to_aware_gmt_datetime(date)
|
||||||
)
|
)
|
||||||
if delta < timedelta(seconds=30):
|
if delta < timedelta(seconds=30):
|
||||||
return "just now"
|
return "just now"
|
||||||
|
|||||||
@@ -9,7 +9,12 @@
|
|||||||
|
|
||||||
$targets.each(function() {
|
$targets.each(function() {
|
||||||
|
|
||||||
let content = $(this).text();
|
let content = $('.live-search-relevant', this).text() || $(this).text();
|
||||||
|
|
||||||
|
if (query == '') {
|
||||||
|
$(this).css('display', '');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
$(this).toggle(
|
$(this).toggle(
|
||||||
normalize(content).indexOf(normalize(query)) > -1
|
normalize(content).indexOf(normalize(query)) > -1
|
||||||
|
|||||||
@@ -5,9 +5,31 @@
|
|||||||
margin: 0;
|
margin: 0;
|
||||||
|
|
||||||
a {
|
a {
|
||||||
display: block;
|
display: inline;
|
||||||
margin-bottom: -$gutter;
|
margin-bottom: -$gutter;
|
||||||
padding-bottom: $gutter;
|
padding-bottom: $gutter;
|
||||||
|
|
||||||
|
&:focus {
|
||||||
|
|
||||||
|
// Use box shadow instead of outline to avoid buggy outline
|
||||||
|
// rendering in Firefox
|
||||||
|
outline: none;
|
||||||
|
box-shadow: 0 0 0 3px $yellow;
|
||||||
|
|
||||||
|
@include ie-lte(8) {
|
||||||
|
// Box shadow is not supported in IE8
|
||||||
|
outline: 3px solid $yellow;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&-separator {
|
||||||
|
display: inline-block;
|
||||||
|
vertical-align: top;
|
||||||
|
color: $secondary-text-colour;
|
||||||
|
padding: 0 4px 0 5px;
|
||||||
|
font-weight: normal;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -40,11 +62,32 @@
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&-hidden-by-default {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-without-ancestors {
|
||||||
|
|
||||||
|
.message-name {
|
||||||
|
|
||||||
|
a {
|
||||||
|
display: block;
|
||||||
|
|
||||||
|
&:first-child {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
&-folder {
|
&-folder {
|
||||||
|
|
||||||
a {
|
a:first-child {
|
||||||
|
display: inline-block;
|
||||||
text-indent: 40px;
|
text-indent: 40px;
|
||||||
background-image: file-url('folder-blue-bold.svg');
|
background-image: file-url('folder-blue-bold.svg');
|
||||||
background-repeat: no-repeat;
|
background-repeat: no-repeat;
|
||||||
|
|||||||
@@ -48,6 +48,11 @@
|
|||||||
|
|
||||||
.page-footer {
|
.page-footer {
|
||||||
margin-bottom: 0;
|
margin-bottom: 0;
|
||||||
|
min-height: 50px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.notification-status {
|
||||||
|
margin: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,6 +11,19 @@ $item-top-padding: $gutter-half;
|
|||||||
border-top: 1px solid $border-colour;
|
border-top: 1px solid $border-colour;
|
||||||
position: relative;
|
position: relative;
|
||||||
|
|
||||||
|
h3 {
|
||||||
|
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
color: $secondary-text-colour; // So the ellipsis is grey
|
||||||
|
|
||||||
|
.heading-small {
|
||||||
|
color: $black;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
&:last-child {
|
&:last-child {
|
||||||
border-bottom: 1px solid $border-colour;
|
border-bottom: 1px solid $border-colour;
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -124,7 +124,7 @@ class Staging(Config):
|
|||||||
HTTP_PROTOCOL = 'https'
|
HTTP_PROTOCOL = 'https'
|
||||||
HEADER_COLOUR = '#6F72AF' # $mauve
|
HEADER_COLOUR = '#6F72AF' # $mauve
|
||||||
STATSD_ENABLED = True
|
STATSD_ENABLED = True
|
||||||
CSV_UPLOAD_BUCKET_NAME = 'staging-notify-csv-upload'
|
CSV_UPLOAD_BUCKET_NAME = 'staging-notifications-csv-upload'
|
||||||
LOGO_UPLOAD_BUCKET_NAME = 'public-logos-staging'
|
LOGO_UPLOAD_BUCKET_NAME = 'public-logos-staging'
|
||||||
MOU_BUCKET_NAME = 'staging-notify.works-mou'
|
MOU_BUCKET_NAME = 'staging-notify.works-mou'
|
||||||
NOTIFY_ENVIRONMENT = 'staging'
|
NOTIFY_ENVIRONMENT = 'staging'
|
||||||
|
|||||||
+10
-9
@@ -2374,7 +2374,7 @@ internalauditscotland.gov.uk:
|
|||||||
inverclyde.gov.uk:
|
inverclyde.gov.uk:
|
||||||
owner: Inverclyde Council
|
owner: Inverclyde Council
|
||||||
crown: false
|
crown: false
|
||||||
agreement_signed: false
|
agreement_signed: true
|
||||||
iow.gov.uk:
|
iow.gov.uk:
|
||||||
owner: Isle of Wight Council
|
owner: Isle of Wight Council
|
||||||
crown: false
|
crown: false
|
||||||
@@ -2629,7 +2629,7 @@ lincoln.gov.uk:
|
|||||||
lincolnshire.gov.uk:
|
lincolnshire.gov.uk:
|
||||||
owner: Lincolnshire County Council
|
owner: Lincolnshire County Council
|
||||||
crown: false
|
crown: false
|
||||||
agreement_signed: false
|
agreement_signed: true
|
||||||
lincsbc.gov.uk:
|
lincsbc.gov.uk:
|
||||||
owner: East Lindsey District Council
|
owner: East Lindsey District Council
|
||||||
crown: false
|
crown: false
|
||||||
@@ -3246,7 +3246,7 @@ northtyneside.gov.uk:
|
|||||||
northumberland.gov.uk:
|
northumberland.gov.uk:
|
||||||
owner: Northumberland County Council
|
owner: Northumberland County Council
|
||||||
crown: false
|
crown: false
|
||||||
agreement_signed: false
|
agreement_signed: true
|
||||||
northwalesadoption.gov.uk:
|
northwalesadoption.gov.uk:
|
||||||
owner: Wrexham County Borough Council
|
owner: Wrexham County Borough Council
|
||||||
crown: false
|
crown: false
|
||||||
@@ -3494,11 +3494,8 @@ pkc.gov.uk:
|
|||||||
plymouth.gov.uk:
|
plymouth.gov.uk:
|
||||||
owner: Plymouth City Council
|
owner: Plymouth City Council
|
||||||
crown: false
|
crown: false
|
||||||
agreement_signed: false
|
agreement_signed: true
|
||||||
plymouthmuseum.gov.uk:
|
plymouthmuseum.gov.uk: plymouth.gov.uk
|
||||||
owner: Plymouth City Council
|
|
||||||
crown: false
|
|
||||||
agreement_signed: false
|
|
||||||
pocklington.gov.uk:
|
pocklington.gov.uk:
|
||||||
owner: Pocklington Town Council
|
owner: Pocklington Town Council
|
||||||
crown: false
|
crown: false
|
||||||
@@ -4659,7 +4656,7 @@ towcester-tc.gov.uk:
|
|||||||
towerhamlets.gov.uk:
|
towerhamlets.gov.uk:
|
||||||
owner: Tower Hamlets London Borough Council
|
owner: Tower Hamlets London Borough Council
|
||||||
crown: false
|
crown: false
|
||||||
agreement_signed: false
|
agreement_signed: true
|
||||||
towynkinmelbay-tc.gov.uk:
|
towynkinmelbay-tc.gov.uk:
|
||||||
owner: Towyn and Kinmel Bay Town Council
|
owner: Towyn and Kinmel Bay Town Council
|
||||||
crown: false
|
crown: false
|
||||||
@@ -5070,6 +5067,10 @@ wixford-pc.gov.uk:
|
|||||||
owner: Stratford-on-Avon District Council
|
owner: Stratford-on-Avon District Council
|
||||||
crown: false
|
crown: false
|
||||||
agreement_signed: false
|
agreement_signed: false
|
||||||
|
wmfs.net:
|
||||||
|
owner: West Midlands Fire Service
|
||||||
|
crown: false
|
||||||
|
agreement_signed: true
|
||||||
woking.gov.uk:
|
woking.gov.uk:
|
||||||
owner: Woking Borough Council
|
owner: Woking Borough Council
|
||||||
crown: false
|
crown: false
|
||||||
|
|||||||
@@ -40,3 +40,4 @@
|
|||||||
- hscni.net
|
- hscni.net
|
||||||
- bi.team
|
- bi.team
|
||||||
- networkrail.co.uk
|
- networkrail.co.uk
|
||||||
|
- uksbs.co.uk
|
||||||
|
|||||||
+7
-15
@@ -1143,8 +1143,8 @@ class TemplateAndFoldersSelectionForm(Form):
|
|||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
service,
|
all_template_folders,
|
||||||
template_type,
|
template_list,
|
||||||
current_folder_id,
|
current_folder_id,
|
||||||
*args,
|
*args,
|
||||||
**kwargs
|
**kwargs
|
||||||
@@ -1152,20 +1152,12 @@ class TemplateAndFoldersSelectionForm(Form):
|
|||||||
|
|
||||||
super().__init__(*args, **kwargs)
|
super().__init__(*args, **kwargs)
|
||||||
|
|
||||||
self.templates_and_folders.choices = self.ids_and_names(
|
self.templates_and_folders.choices = template_list.as_id_and_name
|
||||||
service.get_template_folders_and_templates(template_type, current_folder_id)
|
|
||||||
)
|
|
||||||
|
|
||||||
self.move_to.choices = self.ids_and_names(
|
self.move_to.choices = [
|
||||||
[self.ALL_TEMPLATES_FOLDER] + service.all_template_folders,
|
(item['id'], item['name'])
|
||||||
exclude=current_folder_id,
|
for item in ([self.ALL_TEMPLATES_FOLDER] + all_template_folders)
|
||||||
)
|
if item['id'] != str(current_folder_id)
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def ids_and_names(items, exclude=None):
|
|
||||||
return [
|
|
||||||
(item['id'], item['name']) for item in items
|
|
||||||
if item['id'] != str(exclude)
|
|
||||||
]
|
]
|
||||||
|
|
||||||
templates_and_folders = MultiCheckboxField('Choose templates or folders')
|
templates_and_folders = MultiCheckboxField('Choose templates or folders')
|
||||||
|
|||||||
@@ -65,6 +65,7 @@ def view_notification(service_id, notification_id):
|
|||||||
notification_id=notification_id,
|
notification_id=notification_id,
|
||||||
filetype='png',
|
filetype='png',
|
||||||
),
|
),
|
||||||
|
expand_emails=True,
|
||||||
page_count=page_count,
|
page_count=page_count,
|
||||||
show_recipient=True,
|
show_recipient=True,
|
||||||
redact_missing_personalisation=True,
|
redact_missing_personalisation=True,
|
||||||
|
|||||||
@@ -552,8 +552,12 @@ def service_set_letters(service_id):
|
|||||||
|
|
||||||
@main.route("/services/<service_id>/service-settings/set-postage", methods=['GET', 'POST'])
|
@main.route("/services/<service_id>/service-settings/set-postage", methods=['GET', 'POST'])
|
||||||
@login_required
|
@login_required
|
||||||
@user_is_platform_admin
|
@user_has_permissions('manage_service')
|
||||||
def service_set_postage(service_id):
|
def service_set_postage(service_id):
|
||||||
|
|
||||||
|
if not current_service.has_permission('letter'):
|
||||||
|
abort(404)
|
||||||
|
|
||||||
form = ServicePostageForm(postage=current_service.postage)
|
form = ServicePostageForm(postage=current_service.postage)
|
||||||
|
|
||||||
if form.validate_on_submit():
|
if form.validate_on_submit():
|
||||||
|
|||||||
+31
-14
@@ -29,6 +29,7 @@ from app.main.forms import (
|
|||||||
)
|
)
|
||||||
from app.main.views.send import get_example_csv_rows, get_sender_details
|
from app.main.views.send import get_example_csv_rows, get_sender_details
|
||||||
from app.models.service import Service
|
from app.models.service import Service
|
||||||
|
from app.models.template_list import TemplateList
|
||||||
from app.template_previews import TemplatePreview, get_page_count_for_letter
|
from app.template_previews import TemplatePreview, get_page_count_for_letter
|
||||||
from app.utils import (
|
from app.utils import (
|
||||||
email_or_sms_not_enabled,
|
email_or_sms_not_enabled,
|
||||||
@@ -108,8 +109,11 @@ def start_tour(service_id, template_id):
|
|||||||
@user_has_permissions()
|
@user_has_permissions()
|
||||||
def choose_template(service_id, template_type='all', template_folder_id=None):
|
def choose_template(service_id, template_type='all', template_folder_id=None):
|
||||||
|
|
||||||
|
template_list = TemplateList(current_service, template_type, template_folder_id)
|
||||||
|
|
||||||
templates_and_folders_form = TemplateAndFoldersSelectionForm(
|
templates_and_folders_form = TemplateAndFoldersSelectionForm(
|
||||||
service=current_service,
|
all_template_folders=current_service.all_template_folders,
|
||||||
|
template_list=template_list,
|
||||||
template_type=template_type,
|
template_type=template_type,
|
||||||
current_folder_id=template_folder_id,
|
current_folder_id=template_folder_id,
|
||||||
)
|
)
|
||||||
@@ -126,9 +130,7 @@ def choose_template(service_id, template_type='all', template_folder_id=None):
|
|||||||
current_template_folder_id=template_folder_id,
|
current_template_folder_id=template_folder_id,
|
||||||
can_manage_folders=can_manage_folders(),
|
can_manage_folders=can_manage_folders(),
|
||||||
template_folder_path=current_service.get_template_folder_path(template_folder_id),
|
template_folder_path=current_service.get_template_folder_path(template_folder_id),
|
||||||
template_folder_has_contents=current_service.get_template_folders_and_templates('all', template_folder_id),
|
template_list=template_list,
|
||||||
template_folders=current_service.get_template_folders(template_type, template_folder_id),
|
|
||||||
templates=current_service.get_templates(template_type, template_folder_id),
|
|
||||||
show_search_box=current_service.count_of_templates_and_folders > 7,
|
show_search_box=current_service.count_of_templates_and_folders > 7,
|
||||||
show_template_nav=(
|
show_template_nav=(
|
||||||
current_service.has_multiple_template_types
|
current_service.has_multiple_template_types
|
||||||
@@ -318,7 +320,7 @@ def copy_template(service_id, template_id):
|
|||||||
return add_service_template(service_id, template['template_type'])
|
return add_service_template(service_id, template['template_type'])
|
||||||
|
|
||||||
template['template_content'] = template['content']
|
template['template_content'] = template['content']
|
||||||
template['name'] = 'Copy of ‘{}’'.format(template['name'])
|
template['name'] = _get_template_copy_name(template, current_service.all_templates)
|
||||||
form = form_objects[template['template_type']](**template)
|
form = form_objects[template['template_type']](**template)
|
||||||
|
|
||||||
return render_template(
|
return render_template(
|
||||||
@@ -330,6 +332,20 @@ def copy_template(service_id, template_id):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def _get_template_copy_name(template, existing_templates):
|
||||||
|
|
||||||
|
template_names = [existing['name'] for existing in existing_templates]
|
||||||
|
|
||||||
|
for index in reversed(range(1, 10)):
|
||||||
|
if '{} (copy {})'.format(template['name'], index) in template_names:
|
||||||
|
return '{} (copy {})'.format(template['name'], index + 1)
|
||||||
|
|
||||||
|
if '{} (copy)'.format(template['name']) in template_names:
|
||||||
|
return '{} (copy 2)'.format(template['name'])
|
||||||
|
|
||||||
|
return '{} (copy)'.format(template['name'])
|
||||||
|
|
||||||
|
|
||||||
@main.route("/services/<service_id>/templates/action-blocked/<notification_type>/<return_to>/<template_id>")
|
@main.route("/services/<service_id>/templates/action-blocked/<notification_type>/<return_to>/<template_id>")
|
||||||
@login_required
|
@login_required
|
||||||
@user_has_permissions('manage_templates')
|
@user_has_permissions('manage_templates')
|
||||||
@@ -405,16 +421,18 @@ def manage_template_folder(service_id, template_folder_id):
|
|||||||
@login_required
|
@login_required
|
||||||
@user_has_permissions('manage_templates')
|
@user_has_permissions('manage_templates')
|
||||||
def delete_template_folder(service_id, template_folder_id):
|
def delete_template_folder(service_id, template_folder_id):
|
||||||
|
|
||||||
if not current_service.has_permission('edit_folders'):
|
if not current_service.has_permission('edit_folders'):
|
||||||
abort(403)
|
abort(403)
|
||||||
form = TemplateFolderForm()
|
|
||||||
template_folder_path = current_service.get_template_folder_path(template_folder_id)
|
template_folder = current_service.get_template_folder(template_folder_id)
|
||||||
template_folder_name = template_folder_path[-1]["name"]
|
|
||||||
|
form = TemplateFolderForm(name=template_folder['name'])
|
||||||
|
|
||||||
if len(current_service.get_template_folders_and_templates(
|
if len(current_service.get_template_folders_and_templates(
|
||||||
template_type="all", template_folder_id=template_folder_id
|
template_type="all", template_folder_id=template_folder_id
|
||||||
)) > 0:
|
)) > 0:
|
||||||
flash("You must empty this folder before you can delete it".format(template_folder_name), 'info')
|
flash("You must empty this folder before you can delete it".format(template_folder['name']), 'info')
|
||||||
return redirect(
|
return redirect(
|
||||||
url_for(
|
url_for(
|
||||||
'.choose_template', service_id=service_id, template_type="all", template_folder_id=template_folder_id
|
'.choose_template', service_id=service_id, template_type="all", template_folder_id=template_folder_id
|
||||||
@@ -426,12 +444,12 @@ def delete_template_folder(service_id, template_folder_id):
|
|||||||
template_folder_api_client.delete_template_folder(current_service.id, template_folder_id)
|
template_folder_api_client.delete_template_folder(current_service.id, template_folder_id)
|
||||||
|
|
||||||
return redirect(
|
return redirect(
|
||||||
url_for('.choose_template', service_id=service_id)
|
url_for('.choose_template', service_id=service_id, template_folder_id=template_folder['parent_id'])
|
||||||
)
|
)
|
||||||
except HTTPError as e:
|
except HTTPError as e:
|
||||||
msg = "Folder is not empty"
|
msg = "Folder is not empty"
|
||||||
if e.status_code == 400 and msg in e.message:
|
if e.status_code == 400 and msg in e.message:
|
||||||
flash("You must empty this folder before you can delete it".format(template_folder_name), 'info')
|
flash("You must empty this folder before you can delete it", 'info')
|
||||||
return redirect(
|
return redirect(
|
||||||
url_for(
|
url_for(
|
||||||
'.choose_template',
|
'.choose_template',
|
||||||
@@ -443,15 +461,14 @@ def delete_template_folder(service_id, template_folder_id):
|
|||||||
else:
|
else:
|
||||||
abort(500, e)
|
abort(500, e)
|
||||||
|
|
||||||
flash("Are you sure you want to delete the ‘{}’ folder?".format(template_folder_name), 'delete')
|
flash("Are you sure you want to delete the ‘{}’ folder?".format(template_folder['name']), 'delete')
|
||||||
return render_template(
|
return render_template(
|
||||||
'views/templates/manage-template-folder.html',
|
'views/templates/manage-template-folder.html',
|
||||||
form=form,
|
form=form,
|
||||||
template_folder_path=template_folder_path,
|
template_folder_path=current_service.get_template_folder_path(template_folder_id),
|
||||||
current_service_id=current_service.id,
|
current_service_id=current_service.id,
|
||||||
template_folder_id=template_folder_id,
|
template_folder_id=template_folder_id,
|
||||||
template_type="all",
|
template_type="all",
|
||||||
delete_folder=True
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -369,7 +369,7 @@ class Service():
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def count_of_templates_and_folders(self):
|
def count_of_templates_and_folders(self):
|
||||||
return len(self.get_template_folders_and_templates('all', None))
|
return len(self.all_templates + self.all_template_folders)
|
||||||
|
|
||||||
def move_to_folder(self, ids_to_move, move_to):
|
def move_to_folder(self, ids_to_move, move_to):
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,124 @@
|
|||||||
|
class TemplateList():
|
||||||
|
|
||||||
|
def __init__(
|
||||||
|
self,
|
||||||
|
service,
|
||||||
|
template_type='all',
|
||||||
|
template_folder_id=None,
|
||||||
|
):
|
||||||
|
self.service = service
|
||||||
|
self.template_type = template_type
|
||||||
|
self.template_folder_id = template_folder_id
|
||||||
|
|
||||||
|
def __iter__(self):
|
||||||
|
for item in self.get_templates_and_folders(
|
||||||
|
self.template_type, self.template_folder_id, ancestors=[]
|
||||||
|
):
|
||||||
|
yield item
|
||||||
|
|
||||||
|
def get_templates_and_folders(self, template_type, template_folder_id, ancestors):
|
||||||
|
|
||||||
|
for item in self.service.get_template_folders(
|
||||||
|
template_type, template_folder_id
|
||||||
|
):
|
||||||
|
yield TemplateListFolder(
|
||||||
|
item,
|
||||||
|
folders=self.service.get_template_folders(
|
||||||
|
self.template_type, item['id']
|
||||||
|
),
|
||||||
|
templates=self.service.get_templates(
|
||||||
|
self.template_type, item['id']
|
||||||
|
),
|
||||||
|
ancestors=ancestors,
|
||||||
|
)
|
||||||
|
for sub_item in self.get_templates_and_folders(
|
||||||
|
template_type, item['id'], ancestors + [item]
|
||||||
|
):
|
||||||
|
yield sub_item
|
||||||
|
|
||||||
|
for item in self.service.get_templates(
|
||||||
|
self.template_type, template_folder_id
|
||||||
|
):
|
||||||
|
yield TemplateListTemplate(
|
||||||
|
item,
|
||||||
|
ancestors=ancestors,
|
||||||
|
)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def as_id_and_name(self):
|
||||||
|
return [(item.id, item.name) for item in self]
|
||||||
|
|
||||||
|
@property
|
||||||
|
def templates_to_show(self):
|
||||||
|
return any(self)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def folder_is_empty(self):
|
||||||
|
return not any(self.get_templates_and_folders(
|
||||||
|
'all', self.template_folder_id, []
|
||||||
|
))
|
||||||
|
|
||||||
|
|
||||||
|
class TemplateListItem():
|
||||||
|
|
||||||
|
def __init__(
|
||||||
|
self,
|
||||||
|
template_or_folder,
|
||||||
|
ancestors,
|
||||||
|
):
|
||||||
|
self.id = template_or_folder['id']
|
||||||
|
self.name = template_or_folder['name']
|
||||||
|
self.ancestors = ancestors
|
||||||
|
|
||||||
|
|
||||||
|
class TemplateListTemplate(TemplateListItem):
|
||||||
|
|
||||||
|
is_folder = False
|
||||||
|
|
||||||
|
def __init__(
|
||||||
|
self,
|
||||||
|
template,
|
||||||
|
ancestors,
|
||||||
|
):
|
||||||
|
super().__init__(template, ancestors)
|
||||||
|
self.hint = {
|
||||||
|
'email': 'Email template',
|
||||||
|
'sms': 'Text message template',
|
||||||
|
'letter': 'Letter template',
|
||||||
|
}.get(template['template_type'])
|
||||||
|
|
||||||
|
|
||||||
|
class TemplateListFolder(TemplateListItem):
|
||||||
|
|
||||||
|
is_folder = True
|
||||||
|
|
||||||
|
def __init__(
|
||||||
|
self,
|
||||||
|
folder,
|
||||||
|
templates,
|
||||||
|
folders,
|
||||||
|
ancestors,
|
||||||
|
):
|
||||||
|
super().__init__(folder, ancestors)
|
||||||
|
self.number_of_templates = len(templates)
|
||||||
|
self.number_of_folders = len(folders)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def _hint_parts(self):
|
||||||
|
|
||||||
|
if self.number_of_folders == self.number_of_templates == 0:
|
||||||
|
yield 'Empty'
|
||||||
|
|
||||||
|
if self.number_of_templates == 1:
|
||||||
|
yield '1 template'
|
||||||
|
elif self.number_of_templates > 1:
|
||||||
|
yield '{} templates'.format(self.number_of_templates)
|
||||||
|
|
||||||
|
if self.number_of_folders == 1:
|
||||||
|
yield '1 folder'
|
||||||
|
elif self.number_of_folders > 1:
|
||||||
|
yield '{} folders'.format(self.number_of_folders)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def hint(self):
|
||||||
|
return ', '.join(self._hint_parts)
|
||||||
@@ -55,26 +55,3 @@
|
|||||||
{%- endif -%}
|
{%- endif -%}
|
||||||
{%- endif %}
|
{%- endif %}
|
||||||
{%- endmacro %}
|
{%- endmacro %}
|
||||||
|
|
||||||
|
|
||||||
{% macro folder_contents_count(number_of_folders, number_of_templates) %}
|
|
||||||
|
|
||||||
{% if number_of_folders == number_of_templates == 0 %}
|
|
||||||
Empty
|
|
||||||
{% endif %}
|
|
||||||
|
|
||||||
{% if number_of_templates == 1 %}
|
|
||||||
{{ number_of_templates }} template
|
|
||||||
{%- elif number_of_templates > 1 -%}
|
|
||||||
{{ number_of_templates }} templates
|
|
||||||
{%- endif -%}
|
|
||||||
|
|
||||||
{%- if number_of_folders and number_of_templates %}, {% endif -%}
|
|
||||||
|
|
||||||
{%- if number_of_folders == 1 -%}
|
|
||||||
{{ number_of_folders }} folder
|
|
||||||
{%- elif number_of_folders > 1 -%}
|
|
||||||
{{ number_of_folders }} folders
|
|
||||||
{% endif %}
|
|
||||||
|
|
||||||
{%- endmacro %}
|
|
||||||
|
|||||||
@@ -39,7 +39,7 @@
|
|||||||
{% if template.template_type != 'letter' or not request.args.from_test %}
|
{% if template.template_type != 'letter' or not request.args.from_test %}
|
||||||
<button type="submit" class="button">Send {{ count_of_recipients }} {{ message_count_label(count_of_recipients, template.template_type, suffix='') }}</button>
|
<button type="submit" class="button">Send {{ count_of_recipients }} {{ message_count_label(count_of_recipients, template.template_type, suffix='') }}</button>
|
||||||
{% else %}
|
{% else %}
|
||||||
<a href="{{ url_for('main.check_messages_preview', service_id=current_service.id, template_id=template.id, upload_id=upload_id, filetype='pdf') }}" download class="button">Download as a printable PDF</a>
|
<a href="{{ url_for('main.check_messages_preview', service_id=current_service.id, template_id=template.id, upload_id=upload_id, filetype='pdf') }}" download class="button">Download as a PDF</a>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<a href="{{ back_link }}" class="page-footer-back-link">Back</a>
|
<a href="{{ back_link }}" class="page-footer-back-link">Back</a>
|
||||||
</form>
|
</form>
|
||||||
|
|||||||
@@ -33,7 +33,7 @@
|
|||||||
<div class="user-list">
|
<div class="user-list">
|
||||||
{% for user in users %}
|
{% for user in users %}
|
||||||
<div class="user-list-item">
|
<div class="user-list-item">
|
||||||
<h3>
|
<h3 title="{{ user.email_address }}">
|
||||||
{%- if user.name -%}
|
{%- if user.name -%}
|
||||||
<span class="heading-small">{{ user.name }}</span> 
|
<span class="heading-small">{{ user.name }}</span> 
|
||||||
{%- endif -%}
|
{%- endif -%}
|
||||||
|
|||||||
@@ -65,7 +65,7 @@
|
|||||||
{% endif %}
|
{% endif %}
|
||||||
<a href="{{ back_link }}" class="page-footer-back-link">Back</a>
|
<a href="{{ back_link }}" class="page-footer-back-link">Back</a>
|
||||||
{% if template.template_type == 'letter' %}
|
{% if template.template_type == 'letter' %}
|
||||||
<a href="{{ url_for('main.check_notification_preview', service_id=current_service.id, template_id=template.id, filetype='pdf') }}" download class="page-footer-right-aligned-link">Download as a printable PDF</a>
|
<a href="{{ url_for('main.check_notification_preview', service_id=current_service.id, template_id=template.id, filetype='pdf') }}" download class="page-footer-right-aligned-link">Download as a PDF</a>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -51,15 +51,23 @@
|
|||||||
<p>
|
<p>
|
||||||
Estimated delivery date: {{ estimated_letter_delivery_date|string|format_date_short }}
|
Estimated delivery date: {{ estimated_letter_delivery_date|string|format_date_short }}
|
||||||
</p>
|
</p>
|
||||||
<p class="bottom-gutter">
|
|
||||||
<a href="{{ url_for('main.view_letter_notification_as_preview', service_id=current_service.id, notification_id=notification_id, filetype='pdf') }}" download>Download as a PDF</a>
|
|
||||||
</p>
|
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
{{ template|string }}
|
{{ template|string }}
|
||||||
|
|
||||||
{% if template.template_type != 'letter' %}
|
{% if template.template_type == 'letter' %}
|
||||||
|
<div class="js-stick-at-bottom-when-scrolling">
|
||||||
|
<div class="page-footer">
|
||||||
|
<div> </div>
|
||||||
|
<a class="page-footer-right-aligned-link" href="{{ url_for('main.view_letter_notification_as_preview', service_id=current_service.id, notification_id=notification_id, filetype='pdf') }}" download>Download as a PDF</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% elif template.template_type == 'email' %}
|
||||||
|
<div class="js-stick-at-bottom-when-scrolling">
|
||||||
|
{{ ajax_block(partials, updates_url, 'status', finished=finished) }}
|
||||||
|
</div>
|
||||||
|
{% elif template.template_type == 'sms' %}
|
||||||
{{ ajax_block(partials, updates_url, 'status', finished=finished) }}
|
{{ ajax_block(partials, updates_url, 'status', finished=finished) }}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
|
|||||||
@@ -234,20 +234,18 @@
|
|||||||
)}}
|
)}}
|
||||||
{% endcall %}
|
{% endcall %}
|
||||||
|
|
||||||
{% if current_user.platform_admin %}
|
{% call settings_row(if_has_permission='letter') %}
|
||||||
{% call settings_row(if_has_permission='letter') %}
|
{{ text_field('Postage') }}
|
||||||
{{ text_field('Postage') }}
|
{% set postage = {'first': 'First class only', 'second': 'Second class only'} %}
|
||||||
{% set postage = {'first': 'First class only', 'second': 'Second class only'} %}
|
{{ text_field(postage[current_service.postage]) }}
|
||||||
{{ text_field(postage[current_service.postage]) }}
|
{{ edit_field(
|
||||||
{{ edit_field(
|
'Change',
|
||||||
'Change',
|
url_for('.service_set_postage',
|
||||||
url_for('.service_set_postage',
|
service_id=current_service.id),
|
||||||
service_id=current_service.id),
|
permissions=['manage_service']
|
||||||
permissions=['manage_service']
|
)
|
||||||
)
|
}}
|
||||||
}}
|
{% endcall %}
|
||||||
{% endcall %}
|
|
||||||
{% endif %}
|
|
||||||
|
|
||||||
{% endcall %}
|
{% endcall %}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{% from "components/radios.html" import radios %}
|
{% from "components/radios.html" import radios %}
|
||||||
{% from "components/page-footer.html" import page_footer %}
|
{% from "components/page-footer.html" import page_footer %}
|
||||||
|
|
||||||
{% if templates_and_folders_form.move_to.choices and (templates or template_folders) %}
|
{% if templates_and_folders_form.move_to.choices and template_list.templates_to_show %}
|
||||||
{{ radios(templates_and_folders_form.move_to) }}
|
{{ radios(templates_and_folders_form.move_to) }}
|
||||||
{{ page_footer('Move selected', button_name='operation', button_value='move') }}
|
{{ page_footer('Move selected', button_name='operation', button_value='move') }}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|||||||
@@ -1,52 +1,43 @@
|
|||||||
{% from "components/checkbox.html" import unlabelled_checkbox %}
|
{% from "components/checkbox.html" import unlabelled_checkbox %}
|
||||||
{% from "components/message-count-label.html" import folder_contents_count, message_count_label %}
|
{% from "components/message-count-label.html" import folder_contents_count, message_count_label %}
|
||||||
|
|
||||||
{% if service_has_templates_or_folders and not templates and not template_folders %}
|
{% if not template_list.templates_to_show %}
|
||||||
<p class="template-list-empty">
|
<p class="template-list-empty">
|
||||||
{% if template_folder_has_contents %}
|
{% if template_list.folder_is_empty %}
|
||||||
There are no {{ message_count_label(1, template_type, suffix='') }} templates in this folder
|
|
||||||
{% else %}
|
|
||||||
This folder is empty
|
This folder is empty
|
||||||
|
{% else %}
|
||||||
|
There are no {{ message_count_label(1, template_type, suffix='') }} templates in this folder
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</p>
|
</p>
|
||||||
{% else %}
|
{% else %}
|
||||||
<nav id=template-list>
|
<nav id=template-list>
|
||||||
{% for template_folder in template_folders %}
|
{% for item in template_list %}
|
||||||
<div class="template-list-item {% if can_manage_folders %}template-list-item-with-checkbox{% endif %}">
|
<div class="template-list-item {% if can_manage_folders %}template-list-item-with-checkbox{% endif %} {% if item.ancestors %}template-list-item-hidden-by-default{% endif %} {% if not item.ancestors %}template-list-item-without-ancestors{% endif %}">
|
||||||
{% if can_manage_folders %}
|
{% if can_manage_folders %}
|
||||||
{{ unlabelled_checkbox(
|
{{ unlabelled_checkbox(
|
||||||
id='templates-or-folder-{}'.format(template_folder.id),
|
id='templates-or-folder-{}'.format(item.id),
|
||||||
name='templates_and_folders',
|
name='templates_and_folders',
|
||||||
value=template_folder.id,
|
value=item.id,
|
||||||
) }}
|
) }}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<h2 class="message-name template-list-folder">
|
<h2 class="message-name {% if item.is_folder or (item.ancestors and not item.is_folder) %}template-list-folder{% endif %}">
|
||||||
<a href="{{ url_for('.choose_template', service_id=current_service.id, template_type=template_type, template_folder_id=template_folder.id) }}">
|
{% for ancestor in item.ancestors %}
|
||||||
{{ template_folder.name }}
|
<a href="{{ url_for('.choose_template', service_id=current_service.id, template_type=template_type, template_folder_id=ancestor.id) }}">
|
||||||
</a>
|
{{ ancestor.name }}
|
||||||
|
</a> <span class="message-name-separator">/</span>
|
||||||
|
{% endfor %}
|
||||||
|
{% if item.is_folder %}
|
||||||
|
<a href="{{ url_for('.choose_template', service_id=current_service.id, template_type=template_type, template_folder_id=item.id) }}">
|
||||||
|
<span class="live-search-relevant">{{ item.name }}</span>
|
||||||
|
</a>
|
||||||
|
{% else %}
|
||||||
|
<a href="{{ url_for('.view_template', service_id=current_service.id, template_id=item.id) }}">
|
||||||
|
<span class="live-search-relevant">{{ item.name }}</span>
|
||||||
|
</a>
|
||||||
|
{% endif %}
|
||||||
</h2>
|
</h2>
|
||||||
<p class="message-type">
|
<p class="message-type">
|
||||||
{{ folder_contents_count(
|
{{ item.hint }}
|
||||||
current_service.get_template_folders(template_type, template_folder.id)|length,
|
|
||||||
current_service.get_templates(template_type, template_folder.id)|length,
|
|
||||||
) }}
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
{% endfor %}
|
|
||||||
{% for template in templates %}
|
|
||||||
<div class="template-list-item {% if can_manage_folders %}template-list-item-with-checkbox{% endif %}">
|
|
||||||
{% if can_manage_folders %}
|
|
||||||
{{ unlabelled_checkbox(
|
|
||||||
id='templates-or-folder-{}'.format(template.id),
|
|
||||||
name='templates_and_folders',
|
|
||||||
value=template.id,
|
|
||||||
) }}
|
|
||||||
{% endif %}
|
|
||||||
<h2 class="message-name">
|
|
||||||
<a href="{{ url_for('.view_template', service_id=current_service.id, template_id=template.id) }}">{{ template.name }}</a>
|
|
||||||
</h2>
|
|
||||||
<p class="message-type">
|
|
||||||
{{ message_count_label(1, template.template_type, suffix='')|capitalize }} template
|
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
|||||||
@@ -77,29 +77,11 @@
|
|||||||
|
|
||||||
{% if can_manage_folders %}
|
{% if can_manage_folders %}
|
||||||
<form method="post">
|
<form method="post">
|
||||||
{% with
|
{% include 'views/templates/_template_list.html' %}
|
||||||
templates=templates,
|
{% include 'views/templates/_move_to.html' %}
|
||||||
template_folders=template_folders,
|
|
||||||
service_has_templates_or_folders=(current_service.has_templates or current_service.has_folders),
|
|
||||||
template_type=template_type,
|
|
||||||
template_folder_has_contents=template_folder_has_contents
|
|
||||||
%}
|
|
||||||
{% include 'views/templates/_template_list.html' %}
|
|
||||||
{% endwith %}
|
|
||||||
{% with templates=templates, template_folders=template_folders, templates_and_folders_form=templates_and_folders_form %}
|
|
||||||
{% include 'views/templates/_move_to.html' %}
|
|
||||||
{% endwith %}
|
|
||||||
</form>
|
</form>
|
||||||
{% else %}
|
{% else %}
|
||||||
{% with
|
{% include 'views/templates/_template_list.html' %}
|
||||||
templates=templates,
|
|
||||||
template_folders=template_folders,
|
|
||||||
service_has_templates_or_folders=(current_service.has_templates or current_service.has_folders),
|
|
||||||
template_type=template_type,
|
|
||||||
template_folder_has_contents=template_folder_has_contents
|
|
||||||
%}
|
|
||||||
{% include 'views/templates/_template_list.html' %}
|
|
||||||
{% endwith %}
|
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
|
|||||||
@@ -21,20 +21,16 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{% if not delete_folder %}
|
{% call form_wrapper(action=url_for('main.manage_template_folder', service_id=current_service.id, template_folder_id=template_folder_id)) %}
|
||||||
{% call form_wrapper() %}
|
{{ textbox(form.name) }}
|
||||||
{{ textbox(form.name) }}
|
{{ page_footer(
|
||||||
{{ page_footer(
|
'Save',
|
||||||
'Save',
|
delete_link=url_for(
|
||||||
delete_link=url_for(
|
'.delete_template_folder',
|
||||||
'.delete_template_folder',
|
service_id=current_service_id,
|
||||||
service_id=current_service_id,
|
template_folder_id=template_folder_id
|
||||||
template_folder_id=template_folder_id
|
),
|
||||||
),
|
delete_link_text="Delete this folder") }}
|
||||||
delete_link_text="Delete this folder") }}
|
{% endcall %}
|
||||||
{% endcall %}
|
|
||||||
{% else %}
|
|
||||||
<a href="{{url_for('.manage_template_folder', service_id=current_service.id, template_folder_id=template_folder_id)}}">Back to manage folder page</a>
|
|
||||||
{% endif %}
|
|
||||||
|
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|||||||
@@ -12,7 +12,6 @@ from urllib.parse import urlparse
|
|||||||
import ago
|
import ago
|
||||||
import dateutil
|
import dateutil
|
||||||
import pyexcel
|
import pyexcel
|
||||||
import pytz
|
|
||||||
import yaml
|
import yaml
|
||||||
from flask import (
|
from flask import (
|
||||||
Markup,
|
Markup,
|
||||||
@@ -355,12 +354,6 @@ def email_or_sms_not_enabled(template_type, permissions):
|
|||||||
return (template_type in ['email', 'sms']) and (template_type not in permissions)
|
return (template_type in ['email', 'sms']) and (template_type not in permissions)
|
||||||
|
|
||||||
|
|
||||||
def gmt_timezones(date):
|
|
||||||
date = dateutil.parser.parse(date)
|
|
||||||
forced_utc = date.replace(tzinfo=pytz.utc)
|
|
||||||
return forced_utc.astimezone(pytz.timezone('Europe/London'))
|
|
||||||
|
|
||||||
|
|
||||||
def get_cdn_domain():
|
def get_cdn_domain():
|
||||||
parsed_uri = urlparse(current_app.config['ADMIN_BASE_URL'])
|
parsed_uri = urlparse(current_app.config['ADMIN_BASE_URL'])
|
||||||
|
|
||||||
|
|||||||
@@ -24,4 +24,4 @@ awscli-cwlogs>=1.4,<1.5
|
|||||||
# Putting upgrade on hold due to v1.0.0 using sha512 instead of sha1 by default
|
# Putting upgrade on hold due to v1.0.0 using sha512 instead of sha1 by default
|
||||||
itsdangerous==0.24 # pyup: <1.0.0
|
itsdangerous==0.24 # pyup: <1.0.0
|
||||||
|
|
||||||
git+https://github.com/alphagov/notifications-utils.git@30.6.0#egg=notifications-utils==30.6.0
|
git+https://github.com/alphagov/notifications-utils.git@30.7.0#egg=notifications-utils==30.7.0
|
||||||
|
|||||||
+4
-4
@@ -26,13 +26,13 @@ awscli-cwlogs>=1.4,<1.5
|
|||||||
# Putting upgrade on hold due to v1.0.0 using sha512 instead of sha1 by default
|
# Putting upgrade on hold due to v1.0.0 using sha512 instead of sha1 by default
|
||||||
itsdangerous==0.24 # pyup: <1.0.0
|
itsdangerous==0.24 # pyup: <1.0.0
|
||||||
|
|
||||||
git+https://github.com/alphagov/notifications-utils.git@30.6.0#egg=notifications-utils==30.6.0
|
git+https://github.com/alphagov/notifications-utils.git@30.7.0#egg=notifications-utils==30.7.0
|
||||||
|
|
||||||
## The following requirements were added by pip freeze:
|
## The following requirements were added by pip freeze:
|
||||||
awscli==1.16.58
|
awscli==1.16.61
|
||||||
bleach==2.1.3
|
bleach==2.1.3
|
||||||
boto3==1.6.16
|
boto3==1.6.16
|
||||||
botocore==1.12.48
|
botocore==1.12.51
|
||||||
certifi==2018.10.15
|
certifi==2018.10.15
|
||||||
chardet==3.0.4
|
chardet==3.0.4
|
||||||
Click==7.0
|
Click==7.0
|
||||||
@@ -54,7 +54,7 @@ lxml==4.2.5
|
|||||||
MarkupSafe==1.1.0
|
MarkupSafe==1.1.0
|
||||||
mistune==0.8.3
|
mistune==0.8.3
|
||||||
monotonic==1.5
|
monotonic==1.5
|
||||||
openpyxl==2.5.10
|
openpyxl==2.5.11
|
||||||
orderedset==2.0.1
|
orderedset==2.0.1
|
||||||
phonenumbers==8.9.4
|
phonenumbers==8.9.4
|
||||||
pyasn1==0.4.4
|
pyasn1==0.4.4
|
||||||
|
|||||||
@@ -2803,7 +2803,7 @@ def test_one_off_letters_have_download_link(
|
|||||||
template_id=fake_uuid,
|
template_id=fake_uuid,
|
||||||
filetype='pdf',
|
filetype='pdf',
|
||||||
)
|
)
|
||||||
assert page.select_one('a[download]').text == 'Download as a printable PDF'
|
assert page.select_one('a[download]').text == 'Download as a PDF'
|
||||||
|
|
||||||
|
|
||||||
def test_send_one_off_letter_errors_in_trial_mode(
|
def test_send_one_off_letter_errors_in_trial_mode(
|
||||||
@@ -2848,7 +2848,7 @@ def test_send_one_off_letter_errors_in_trial_mode(
|
|||||||
|
|
||||||
assert not page.select('[type=submit]')
|
assert not page.select('[type=submit]')
|
||||||
assert page.select_one('.page-footer-back-link').text == 'Back'
|
assert page.select_one('.page-footer-back-link').text == 'Back'
|
||||||
assert page.select_one('a[download]').text == 'Download as a printable PDF'
|
assert page.select_one('a[download]').text == 'Download as a PDF'
|
||||||
|
|
||||||
|
|
||||||
def test_check_messages_shows_over_max_row_error(
|
def test_check_messages_shows_over_max_row_error(
|
||||||
|
|||||||
@@ -216,7 +216,7 @@ def test_should_show_overview_for_service_with_more_things_set(
|
|||||||
|
|
||||||
|
|
||||||
def test_if_cant_send_letters_then_cant_see_postage(
|
def test_if_cant_send_letters_then_cant_see_postage(
|
||||||
logged_in_platform_admin_client,
|
client_request,
|
||||||
service_one,
|
service_one,
|
||||||
single_reply_to_email_address,
|
single_reply_to_email_address,
|
||||||
single_letter_contact_block,
|
single_letter_contact_block,
|
||||||
@@ -224,8 +224,7 @@ def test_if_cant_send_letters_then_cant_see_postage(
|
|||||||
single_sms_sender,
|
single_sms_sender,
|
||||||
mock_get_service_settings_page_common,
|
mock_get_service_settings_page_common,
|
||||||
):
|
):
|
||||||
response = logged_in_platform_admin_client.get(url_for('main.service_settings', service_id=SERVICE_ONE_ID))
|
page = client_request.get('main.service_settings', service_id=SERVICE_ONE_ID)
|
||||||
page = BeautifulSoup(response.data.decode('utf-8'), 'html.parser')
|
|
||||||
|
|
||||||
letter_table = page.find_all('table')[3]
|
letter_table = page.find_all('table')[3]
|
||||||
rows = letter_table.find_all('tr')
|
rows = letter_table.find_all('tr')
|
||||||
@@ -2148,35 +2147,42 @@ def test_set_letter_branding_saves(
|
|||||||
mock_update_service.assert_called_once_with(service_one['id'], dvla_organisation='500')
|
mock_update_service.assert_called_once_with(service_one['id'], dvla_organisation='500')
|
||||||
|
|
||||||
|
|
||||||
def test_set_postage_platform_admin_only(
|
def test_set_postage_only_for_letter_services(
|
||||||
logged_in_client,
|
client_request,
|
||||||
service_one,
|
|
||||||
):
|
):
|
||||||
response = logged_in_client.get(url_for('main.service_set_postage', service_id=SERVICE_ONE_ID))
|
client_request.get(
|
||||||
assert response.status_code == 403
|
'main.service_set_postage',
|
||||||
|
service_id=SERVICE_ONE_ID,
|
||||||
|
_expected_status=404,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def test_set_postage_prepopulates(
|
def test_set_postage_prepopulates(
|
||||||
logged_in_platform_admin_client,
|
client_request,
|
||||||
service_one,
|
service_one,
|
||||||
):
|
):
|
||||||
response = logged_in_platform_admin_client.get(url_for('main.service_set_postage', service_id=SERVICE_ONE_ID))
|
service_one['permissions'] += ['letter']
|
||||||
assert response.status_code == 200
|
page = client_request.get('main.service_set_postage', service_id=SERVICE_ONE_ID)
|
||||||
page = BeautifulSoup(response.data.decode('utf-8'), 'html.parser')
|
|
||||||
assert page.select('input[checked]')[0]['value'] == 'second'
|
assert page.select('input[checked]')[0]['value'] == 'second'
|
||||||
|
|
||||||
|
|
||||||
def test_set_postage_saves(
|
def test_set_postage_saves(
|
||||||
logged_in_platform_admin_client,
|
client_request,
|
||||||
service_one,
|
service_one,
|
||||||
mock_update_service,
|
mock_update_service,
|
||||||
):
|
):
|
||||||
response = logged_in_platform_admin_client.post(
|
service_one['permissions'] += ['letter']
|
||||||
url_for('main.service_set_postage', service_id=SERVICE_ONE_ID),
|
client_request.post(
|
||||||
data={'postage': 'first'}
|
'main.service_set_postage',
|
||||||
|
service_id=SERVICE_ONE_ID,
|
||||||
|
_data={'postage': 'first'},
|
||||||
|
_expected_status=302,
|
||||||
|
expected_redirect=url_for(
|
||||||
|
'main.service_settings',
|
||||||
|
service_id=SERVICE_ONE_ID,
|
||||||
|
_external=True,
|
||||||
|
)
|
||||||
)
|
)
|
||||||
assert response.status_code == 302
|
|
||||||
assert response.location == url_for('main.service_settings', service_id=SERVICE_ONE_ID, _external=True)
|
|
||||||
mock_update_service.assert_called_once_with(SERVICE_ONE_ID, postage='first')
|
mock_update_service.assert_called_once_with(SERVICE_ONE_ID, postage='first')
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -107,6 +107,8 @@ def test_post_add_template_folder_page(client_request, service_one, mocker, pare
|
|||||||
'extra_args,'
|
'extra_args,'
|
||||||
'expected_nav_links,'
|
'expected_nav_links,'
|
||||||
'expected_items, '
|
'expected_items, '
|
||||||
|
'expected_displayed_items, '
|
||||||
|
'expected_searchable_text, '
|
||||||
'expected_empty_message '
|
'expected_empty_message '
|
||||||
),
|
),
|
||||||
[
|
[
|
||||||
@@ -118,6 +120,11 @@ def test_post_add_template_folder_page(client_request, service_one, mocker, pare
|
|||||||
['Text message', 'Email', 'Letter'],
|
['Text message', 'Email', 'Letter'],
|
||||||
[
|
[
|
||||||
'folder_one 2 folders',
|
'folder_one 2 folders',
|
||||||
|
'folder_one / folder_one_one 1 template, 1 folder',
|
||||||
|
'folder_one / folder_one_one / folder_one_one_one 1 template',
|
||||||
|
'folder_one / folder_one_one / folder_one_one_one / sms_template_nested Text message template',
|
||||||
|
'folder_one / folder_one_one / letter_template_nested Letter template',
|
||||||
|
'folder_one / folder_one_two Empty',
|
||||||
'folder_two Empty',
|
'folder_two Empty',
|
||||||
'sms_template_one Text message template',
|
'sms_template_one Text message template',
|
||||||
'sms_template_two Text message template',
|
'sms_template_two Text message template',
|
||||||
@@ -126,6 +133,31 @@ def test_post_add_template_folder_page(client_request, service_one, mocker, pare
|
|||||||
'letter_template_one Letter template',
|
'letter_template_one Letter template',
|
||||||
'letter_template_two Letter template',
|
'letter_template_two Letter template',
|
||||||
],
|
],
|
||||||
|
[
|
||||||
|
'folder_one 2 folders',
|
||||||
|
'folder_two Empty',
|
||||||
|
'sms_template_one Text message template',
|
||||||
|
'sms_template_two Text message template',
|
||||||
|
'email_template_one Email template',
|
||||||
|
'email_template_two Email template',
|
||||||
|
'letter_template_one Letter template',
|
||||||
|
'letter_template_two Letter template',
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'folder_one',
|
||||||
|
'folder_one_one',
|
||||||
|
'folder_one_one_one',
|
||||||
|
'sms_template_nested',
|
||||||
|
'letter_template_nested',
|
||||||
|
'folder_one_two',
|
||||||
|
'folder_two',
|
||||||
|
'sms_template_one',
|
||||||
|
'sms_template_two',
|
||||||
|
'email_template_one',
|
||||||
|
'email_template_two',
|
||||||
|
'letter_template_one',
|
||||||
|
'letter_template_two',
|
||||||
|
],
|
||||||
None,
|
None,
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
@@ -136,9 +168,25 @@ def test_post_add_template_folder_page(client_request, service_one, mocker, pare
|
|||||||
['All', 'Email', 'Letter'],
|
['All', 'Email', 'Letter'],
|
||||||
[
|
[
|
||||||
'folder_one 1 folder',
|
'folder_one 1 folder',
|
||||||
|
'folder_one / folder_one_one 1 folder',
|
||||||
|
'folder_one / folder_one_one / folder_one_one_one 1 template',
|
||||||
|
'folder_one / folder_one_one / folder_one_one_one / sms_template_nested Text message template',
|
||||||
'sms_template_one Text message template',
|
'sms_template_one Text message template',
|
||||||
'sms_template_two Text message template',
|
'sms_template_two Text message template',
|
||||||
],
|
],
|
||||||
|
[
|
||||||
|
'folder_one 1 folder',
|
||||||
|
'sms_template_one Text message template',
|
||||||
|
'sms_template_two Text message template',
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'folder_one',
|
||||||
|
'folder_one_one',
|
||||||
|
'folder_one_one_one',
|
||||||
|
'sms_template_nested',
|
||||||
|
'sms_template_one',
|
||||||
|
'sms_template_two',
|
||||||
|
],
|
||||||
None,
|
None,
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
@@ -149,8 +197,22 @@ def test_post_add_template_folder_page(client_request, service_one, mocker, pare
|
|||||||
['Text message', 'Email', 'Letter'],
|
['Text message', 'Email', 'Letter'],
|
||||||
[
|
[
|
||||||
'folder_one_one 1 template, 1 folder',
|
'folder_one_one 1 template, 1 folder',
|
||||||
|
'folder_one_one / folder_one_one_one 1 template',
|
||||||
|
'folder_one_one / folder_one_one_one / sms_template_nested Text message template',
|
||||||
|
'folder_one_one / letter_template_nested Letter template',
|
||||||
'folder_one_two Empty',
|
'folder_one_two Empty',
|
||||||
],
|
],
|
||||||
|
[
|
||||||
|
'folder_one_one 1 template, 1 folder',
|
||||||
|
'folder_one_two Empty',
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'folder_one_one',
|
||||||
|
'folder_one_one_one',
|
||||||
|
'sms_template_nested',
|
||||||
|
'letter_template_nested',
|
||||||
|
'folder_one_two',
|
||||||
|
],
|
||||||
None,
|
None,
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
@@ -161,6 +223,16 @@ def test_post_add_template_folder_page(client_request, service_one, mocker, pare
|
|||||||
['All', 'Email', 'Letter'],
|
['All', 'Email', 'Letter'],
|
||||||
[
|
[
|
||||||
'folder_one_one 1 folder',
|
'folder_one_one 1 folder',
|
||||||
|
'folder_one_one / folder_one_one_one 1 template',
|
||||||
|
'folder_one_one / folder_one_one_one / sms_template_nested Text message template',
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'folder_one_one 1 folder',
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'folder_one_one',
|
||||||
|
'folder_one_one_one',
|
||||||
|
'sms_template_nested',
|
||||||
],
|
],
|
||||||
None,
|
None,
|
||||||
),
|
),
|
||||||
@@ -171,6 +243,8 @@ def test_post_add_template_folder_page(client_request, service_one, mocker, pare
|
|||||||
{'template_type': 'email', 'template_folder_id': PARENT_FOLDER_ID},
|
{'template_type': 'email', 'template_folder_id': PARENT_FOLDER_ID},
|
||||||
['All', 'Text message', 'Letter'],
|
['All', 'Text message', 'Letter'],
|
||||||
[],
|
[],
|
||||||
|
[],
|
||||||
|
[],
|
||||||
'There are no email templates in this folder',
|
'There are no email templates in this folder',
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
@@ -181,8 +255,18 @@ def test_post_add_template_folder_page(client_request, service_one, mocker, pare
|
|||||||
['Text message', 'Email', 'Letter'],
|
['Text message', 'Email', 'Letter'],
|
||||||
[
|
[
|
||||||
'folder_one_one_one 1 template',
|
'folder_one_one_one 1 template',
|
||||||
|
'folder_one_one_one / sms_template_nested Text message template',
|
||||||
'letter_template_nested Letter template',
|
'letter_template_nested Letter template',
|
||||||
],
|
],
|
||||||
|
[
|
||||||
|
'folder_one_one_one 1 template',
|
||||||
|
'letter_template_nested Letter template',
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'folder_one_one_one',
|
||||||
|
'sms_template_nested',
|
||||||
|
'letter_template_nested',
|
||||||
|
],
|
||||||
None,
|
None,
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
@@ -194,6 +278,12 @@ def test_post_add_template_folder_page(client_request, service_one, mocker, pare
|
|||||||
[
|
[
|
||||||
'sms_template_nested Text message template',
|
'sms_template_nested Text message template',
|
||||||
],
|
],
|
||||||
|
[
|
||||||
|
'sms_template_nested Text message template',
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'sms_template_nested',
|
||||||
|
],
|
||||||
None,
|
None,
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
@@ -203,6 +293,8 @@ def test_post_add_template_folder_page(client_request, service_one, mocker, pare
|
|||||||
{'template_folder_id': FOLDER_TWO_ID},
|
{'template_folder_id': FOLDER_TWO_ID},
|
||||||
['Text message', 'Email', 'Letter'],
|
['Text message', 'Email', 'Letter'],
|
||||||
[],
|
[],
|
||||||
|
[],
|
||||||
|
[],
|
||||||
'This folder is empty',
|
'This folder is empty',
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
@@ -212,6 +304,8 @@ def test_post_add_template_folder_page(client_request, service_one, mocker, pare
|
|||||||
{'template_folder_id': FOLDER_TWO_ID, 'template_type': 'sms'},
|
{'template_folder_id': FOLDER_TWO_ID, 'template_type': 'sms'},
|
||||||
['All', 'Email', 'Letter'],
|
['All', 'Email', 'Letter'],
|
||||||
[],
|
[],
|
||||||
|
[],
|
||||||
|
[],
|
||||||
'This folder is empty',
|
'This folder is empty',
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
@@ -229,6 +323,8 @@ def test_should_show_templates_folder_page(
|
|||||||
extra_args,
|
extra_args,
|
||||||
expected_nav_links,
|
expected_nav_links,
|
||||||
expected_items,
|
expected_items,
|
||||||
|
expected_displayed_items,
|
||||||
|
expected_searchable_text,
|
||||||
expected_empty_message,
|
expected_empty_message,
|
||||||
):
|
):
|
||||||
mock_get_template_folders.return_value = [
|
mock_get_template_folders.return_value = [
|
||||||
@@ -281,12 +377,32 @@ def test_should_show_templates_folder_page(
|
|||||||
for index, expected_link in enumerate(expected_nav_links):
|
for index, expected_link in enumerate(expected_nav_links):
|
||||||
assert links_in_page[index].text.strip() == expected_link
|
assert links_in_page[index].text.strip() == expected_link
|
||||||
|
|
||||||
page_items = page.select('.template-list-item')
|
all_page_items = page.select('.template-list-item')
|
||||||
|
checkboxes = page.select('input[name=templates_and_folders]')
|
||||||
assert len(page_items) == len(expected_items)
|
unique_checkbox_values = set(item['value'] for item in checkboxes)
|
||||||
|
assert len(all_page_items) == len(expected_items)
|
||||||
|
assert len(checkboxes) == len(expected_items)
|
||||||
|
assert len(unique_checkbox_values) == len(expected_items)
|
||||||
|
|
||||||
for index, expected_item in enumerate(expected_items):
|
for index, expected_item in enumerate(expected_items):
|
||||||
assert normalize_spaces(page_items[index].text) == expected_item
|
assert normalize_spaces(all_page_items[index].text) == expected_item
|
||||||
|
|
||||||
|
displayed_page_items = page.find_all(lambda tag: (
|
||||||
|
tag.has_attr('class')
|
||||||
|
and 'template-list-item' in tag['class']
|
||||||
|
and 'template-list-item-hidden-by-default' not in tag['class']
|
||||||
|
))
|
||||||
|
assert len(displayed_page_items) == len(expected_displayed_items)
|
||||||
|
|
||||||
|
for index, expected_item in enumerate(expected_displayed_items):
|
||||||
|
assert '/' not in expected_item # Yo dawg I heard you like tests…
|
||||||
|
assert normalize_spaces(displayed_page_items[index].text) == expected_item
|
||||||
|
|
||||||
|
all_searchable_text = page.select('#template-list .template-list-item .live-search-relevant')
|
||||||
|
assert len(all_searchable_text) == len(expected_searchable_text)
|
||||||
|
|
||||||
|
for index, expected_item in enumerate(expected_searchable_text):
|
||||||
|
assert normalize_spaces(all_searchable_text[index].text) == expected_item
|
||||||
|
|
||||||
if expected_empty_message:
|
if expected_empty_message:
|
||||||
assert normalize_spaces(page.select_one('.template-list-empty').text) == (
|
assert normalize_spaces(page.select_one('.template-list-empty').text) == (
|
||||||
@@ -402,7 +518,7 @@ def test_get_manage_folder_page(
|
|||||||
assert normalize_spaces(page.select_one('title').text) == (
|
assert normalize_spaces(page.select_one('title').text) == (
|
||||||
'folder_two – Templates – service one – GOV.UK Notify'
|
'folder_two – Templates – service one – GOV.UK Notify'
|
||||||
)
|
)
|
||||||
assert page.select_one('input[name=name]') is not None
|
assert page.select_one('input[name=name]')['value'] == 'folder_two'
|
||||||
delete_link = page.find('a', string="Delete this folder")
|
delete_link = page.find('a', string="Delete this folder")
|
||||||
expected_delete_url = "/services/{}/templates/folders/{}/delete".format(service_one['id'], folder_id)
|
expected_delete_url = "/services/{}/templates/folders/{}/delete".format(service_one['id'], folder_id)
|
||||||
|
|
||||||
@@ -478,9 +594,20 @@ def test_delete_template_folder_should_request_confirmation(
|
|||||||
'Yes, delete'
|
'Yes, delete'
|
||||||
)
|
)
|
||||||
|
|
||||||
assert len(page.select('label')) == 0
|
assert page.select_one('input[name=name]')['value'] == 'sacrifice'
|
||||||
assert len(page.select('button')) == 1
|
|
||||||
assert "Back to manage folder page" in page.text
|
assert len(page.select('form')) == 2
|
||||||
|
assert len(page.select('button')) == 2
|
||||||
|
|
||||||
|
assert 'action' not in page.select('form')[0]
|
||||||
|
assert page.select('form button')[0].text == 'Yes, delete'
|
||||||
|
|
||||||
|
assert page.select('form')[1]['action'] == url_for(
|
||||||
|
'main.manage_template_folder',
|
||||||
|
service_id=service_one['id'],
|
||||||
|
template_folder_id=folder_id,
|
||||||
|
)
|
||||||
|
assert page.select('form button')[1].text == 'Save'
|
||||||
|
|
||||||
|
|
||||||
def test_delete_template_folder_should_detect_non_empty_folder_on_get(
|
def test_delete_template_folder_should_detect_non_empty_folder_on_get(
|
||||||
@@ -511,11 +638,15 @@ def test_delete_template_folder_should_detect_non_empty_folder_on_get(
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def test_delete_folder(client_request, service_one, mock_get_template_folders, mocker):
|
@pytest.mark.parametrize('parent_folder_id', (
|
||||||
|
None,
|
||||||
|
PARENT_FOLDER_ID,
|
||||||
|
))
|
||||||
|
def test_delete_folder(client_request, service_one, mock_get_template_folders, mocker, parent_folder_id):
|
||||||
mock_delete = mocker.patch('app.template_folder_api_client.delete_template_folder')
|
mock_delete = mocker.patch('app.template_folder_api_client.delete_template_folder')
|
||||||
folder_id = str(uuid.uuid4())
|
folder_id = str(uuid.uuid4())
|
||||||
mock_get_template_folders.side_effect = [[
|
mock_get_template_folders.side_effect = [[
|
||||||
{'id': folder_id, 'name': 'sacrifice', 'parent_id': None},
|
{'id': folder_id, 'name': 'sacrifice', 'parent_id': parent_folder_id},
|
||||||
], []]
|
], []]
|
||||||
mocker.patch(
|
mocker.patch(
|
||||||
'app.models.service.Service.get_templates',
|
'app.models.service.Service.get_templates',
|
||||||
@@ -527,9 +658,12 @@ def test_delete_folder(client_request, service_one, mock_get_template_folders, m
|
|||||||
'main.delete_template_folder',
|
'main.delete_template_folder',
|
||||||
service_id=service_one['id'],
|
service_id=service_one['id'],
|
||||||
template_folder_id=folder_id,
|
template_folder_id=folder_id,
|
||||||
_expected_redirect=url_for("main.choose_template",
|
_expected_redirect=url_for(
|
||||||
service_id=service_one['id'],
|
"main.choose_template",
|
||||||
_external=True)
|
service_id=service_one['id'],
|
||||||
|
template_folder_id=parent_folder_id,
|
||||||
|
_external=True,
|
||||||
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
mock_delete.assert_called_once_with(service_one['id'], folder_id)
|
mock_delete.assert_called_once_with(service_one['id'], folder_id)
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ from tests import (
|
|||||||
from tests.app.main.views.test_template_folders import (
|
from tests.app.main.views.test_template_folders import (
|
||||||
CHILD_FOLDER_ID,
|
CHILD_FOLDER_ID,
|
||||||
PARENT_FOLDER_ID,
|
PARENT_FOLDER_ID,
|
||||||
|
_folder,
|
||||||
)
|
)
|
||||||
from tests.conftest import (
|
from tests.conftest import (
|
||||||
SERVICE_ONE_ID,
|
SERVICE_ONE_ID,
|
||||||
@@ -364,6 +365,40 @@ def test_should_post_move_to_api(
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def test_should_be_able_to_move_a_sub_item(
|
||||||
|
client_request,
|
||||||
|
service_one,
|
||||||
|
fake_uuid,
|
||||||
|
mock_get_service_templates,
|
||||||
|
mock_get_template_folders,
|
||||||
|
mock_move_to_template_folder,
|
||||||
|
):
|
||||||
|
service_one['permissions'] += ['edit_folders']
|
||||||
|
GRANDCHILD_FOLDER_ID = str(uuid.uuid4())
|
||||||
|
mock_get_template_folders.return_value = [
|
||||||
|
{'id': PARENT_FOLDER_ID, 'name': 'folder_one', 'parent_id': None},
|
||||||
|
{'id': CHILD_FOLDER_ID, 'name': 'folder_one_one', 'parent_id': PARENT_FOLDER_ID},
|
||||||
|
{'id': GRANDCHILD_FOLDER_ID, 'name': 'folder_one_one_one', 'parent_id': CHILD_FOLDER_ID},
|
||||||
|
]
|
||||||
|
client_request.post(
|
||||||
|
'main.choose_template',
|
||||||
|
service_id=SERVICE_ONE_ID,
|
||||||
|
template_folder_id=PARENT_FOLDER_ID,
|
||||||
|
_data={
|
||||||
|
'operation': 'move',
|
||||||
|
'move_to': 'None',
|
||||||
|
'templates_and_folders': [GRANDCHILD_FOLDER_ID],
|
||||||
|
},
|
||||||
|
_expected_status=302,
|
||||||
|
)
|
||||||
|
mock_move_to_template_folder.assert_called_once_with(
|
||||||
|
service_id=SERVICE_ONE_ID,
|
||||||
|
folder_id=None,
|
||||||
|
folder_ids={GRANDCHILD_FOLDER_ID},
|
||||||
|
template_ids=set(),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize('thing_to_move', [
|
@pytest.mark.parametrize('thing_to_move', [
|
||||||
PARENT_FOLDER_ID, # Can’t move a folder inside itself
|
PARENT_FOLDER_ID, # Can’t move a folder inside itself
|
||||||
CHILD_FOLDER_ID, # Can’t move a folder which doesn’t belong to the service
|
CHILD_FOLDER_ID, # Can’t move a folder which doesn’t belong to the service
|
||||||
@@ -446,6 +481,33 @@ def test_should_show_live_search_if_list_of_templates_taller_than_screen(
|
|||||||
assert len(page.select(search['data-targets'])) == len(page.select('.message-name')) == 14
|
assert len(page.select(search['data-targets'])) == len(page.select('.message-name')) == 14
|
||||||
|
|
||||||
|
|
||||||
|
def test_should_show_live_search_if_service_has_lots_of_folders(
|
||||||
|
client_request,
|
||||||
|
mock_get_template_folders,
|
||||||
|
mock_get_service_templates, # returns 4 templates
|
||||||
|
):
|
||||||
|
|
||||||
|
mock_get_template_folders.return_value = [
|
||||||
|
_folder('one', PARENT_FOLDER_ID),
|
||||||
|
_folder('two', None, parent=PARENT_FOLDER_ID),
|
||||||
|
_folder('three', None, parent=PARENT_FOLDER_ID),
|
||||||
|
_folder('four', None, parent=PARENT_FOLDER_ID),
|
||||||
|
]
|
||||||
|
|
||||||
|
page = client_request.get(
|
||||||
|
'main.choose_template',
|
||||||
|
service_id=SERVICE_ONE_ID,
|
||||||
|
)
|
||||||
|
|
||||||
|
count_of_templates_and_folders = len(page.select('.message-name'))
|
||||||
|
count_of_folders = len(page.select('.template-list-folder'))
|
||||||
|
count_of_templates = count_of_templates_and_folders - count_of_folders
|
||||||
|
|
||||||
|
assert len(page.select('.live-search')) == 1
|
||||||
|
assert count_of_folders == 4
|
||||||
|
assert count_of_templates == 4
|
||||||
|
|
||||||
|
|
||||||
def test_should_show_page_for_one_template(
|
def test_should_show_page_for_one_template(
|
||||||
logged_in_client,
|
logged_in_client,
|
||||||
mock_get_service_template,
|
mock_get_service_template,
|
||||||
@@ -823,11 +885,44 @@ def test_choose_a_template_to_copy(
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize('existing_template_names, expected_name', (
|
||||||
|
(
|
||||||
|
['Two week reminder'],
|
||||||
|
'Two week reminder (copy)'
|
||||||
|
),
|
||||||
|
(
|
||||||
|
['Two week reminder (copy)'],
|
||||||
|
'Two week reminder (copy 2)'
|
||||||
|
),
|
||||||
|
(
|
||||||
|
['Two week reminder', 'Two week reminder (copy)'],
|
||||||
|
'Two week reminder (copy 2)'
|
||||||
|
),
|
||||||
|
(
|
||||||
|
['Two week reminder (copy 8)', 'Two week reminder (copy 9)'],
|
||||||
|
'Two week reminder (copy 10)'
|
||||||
|
),
|
||||||
|
(
|
||||||
|
['Two week reminder (copy)', 'Two week reminder (copy 9)'],
|
||||||
|
'Two week reminder (copy 10)'
|
||||||
|
),
|
||||||
|
(
|
||||||
|
['Two week reminder (copy)', 'Two week reminder (copy 10)'],
|
||||||
|
'Two week reminder (copy 2)'
|
||||||
|
),
|
||||||
|
))
|
||||||
def test_load_edit_template_with_copy_of_template(
|
def test_load_edit_template_with_copy_of_template(
|
||||||
client_request,
|
client_request,
|
||||||
|
mock_get_service_templates,
|
||||||
mock_get_service_email_template,
|
mock_get_service_email_template,
|
||||||
mock_get_non_empty_organisations_and_services_for_user,
|
mock_get_non_empty_organisations_and_services_for_user,
|
||||||
|
existing_template_names,
|
||||||
|
expected_name,
|
||||||
):
|
):
|
||||||
|
mock_get_service_templates.side_effect = lambda service_id: {'data': [
|
||||||
|
{'name': existing_template_name, 'template_type': 'sms'}
|
||||||
|
for existing_template_name in existing_template_names
|
||||||
|
]}
|
||||||
page = client_request.get(
|
page = client_request.get(
|
||||||
'main.copy_template',
|
'main.copy_template',
|
||||||
service_id=SERVICE_ONE_ID,
|
service_id=SERVICE_ONE_ID,
|
||||||
@@ -838,7 +933,7 @@ def test_load_edit_template_with_copy_of_template(
|
|||||||
assert page.select_one('form')['method'] == 'post'
|
assert page.select_one('form')['method'] == 'post'
|
||||||
|
|
||||||
assert page.select_one('input')['value'] == (
|
assert page.select_one('input')['value'] == (
|
||||||
'Copy of ‘Two week reminder’'
|
expected_name
|
||||||
)
|
)
|
||||||
assert page.select_one('textarea').text == (
|
assert page.select_one('textarea').text == (
|
||||||
'Your ((thing)) is due soon'
|
'Your ((thing)) is due soon'
|
||||||
|
|||||||
Reference in New Issue
Block a user